gladiator30

custom.js

Jan 21st, 2026
1,530
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.76 KB | None | 0 0
  1. // Remove refresh button
  2. document.getElementById("revalidate")?.remove();
  3.  
  4. // Rename widget labels dynamically
  5. function renameLabels() {
  6. const mappings = {
  7. "Missing Episodes": "Episodes", // bazarr
  8. "Missing Movies": "Movies", // bazarr
  9. "Logins (24h)": "Logins", // authentik
  10. "Failed Logins (24h)": "Failed", // authentik
  11. "Sites Down": "Down", // uptime-kuma
  12. "Sites Up": "Up" // uptime-kuma
  13. };
  14.  
  15. document.querySelectorAll("div.font-bold.text-xs.uppercase").forEach((el) => {
  16. const text = el.textContent.trim();
  17. if (mappings[text]) el.textContent = mappings[text];
  18. });
  19. }
  20.  
  21. // Add logout button
  22. function addLogoutButton() {
  23. const logoutUrl = "REDACTED"; // Replace with your actual logout URL
  24. const targetElement = document.querySelector(".flex.flex-row.w-full.flex-wrap.justify-between.gap-x-2");
  25.  
  26. if (targetElement && !document.getElementById("logout-icon")) {
  27. const logoutButton = document.createElement("a");
  28. logoutButton.innerHTML = `
  29. <svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
  30. <path d="M10 9V5a1 1 0 011-1h8a1 1 0 011 1v14a1 1 0 01-1 1h-8a1 1 0 01-1-1v-4H8v4a3 3 0 003 3h8a3 3 0 003-3V5a3 3 0 00-3-3h-8a3 3 0 00-3 3v4h2zM15 12l-4-4v3H3v2h8v3l4-4z"/>
  31. </svg>
  32. `;
  33. logoutButton.href = logoutUrl;
  34. logoutButton.id = "logout-icon";
  35. logoutButton.classList.add("logout-icon");
  36.  
  37. targetElement.appendChild(logoutButton);
  38. }
  39. }
  40.  
  41. // Add Crictimes floating widget
  42. // ref: https://dwidget.crictimes.org/script.js?v=1.0.0
  43. function addCrictimesWidget() {
  44. if (document.getElementById("floatCrictimes")) return;
  45.  
  46. const floatDiv = document.createElement('div');
  47. floatDiv.id = 'floatCrictimes';
  48. document.body.appendChild(floatDiv);
  49.  
  50. const styles = `
  51. #floatCrictimes {
  52. position: fixed;
  53. bottom: 10px;
  54. right: 10px;
  55. // right: unset;
  56. z-index: 9999;
  57. border-radius: 10px;
  58. // background: rgba(51,102,255,0.9);
  59. box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
  60. backdrop-filter: blur(var(--blur-md));
  61. border-radius: 8px;
  62. }
  63. #floatCrictimes iframe {
  64. mix-blend-mode: multiply;
  65. }
  66. `;
  67. const styleSheet = document.createElement("style");
  68. styleSheet.innerHTML = styles;
  69. document.head.appendChild(styleSheet);
  70.  
  71. floatDiv.innerHTML = `
  72. <iframe id="iframe"
  73. src="https://dwidget.crictimes.org/index.html"
  74. style="width: 200px;height: 66px;"
  75. frameborder="0"
  76. scrolling="no">
  77. </iframe>
  78. `;
  79.  
  80. window.addEventListener('message', function (event) {
  81. const iframe = document.getElementById("iframe");
  82. if (!iframe) return;
  83.  
  84. if (event.data === 'open') {
  85. iframe.style.height = "400px";
  86. iframe.style.width = "320px";
  87. iframe.style.filter = "invert(1) hue-rotate(180deg) brightness(0.9)";
  88. document.getElementById("floatCrictimes").style.background = "rgba(51,102,255,0.9)";
  89. } else if (event.data === 'close') {
  90. iframe.style.height = "66px";
  91. iframe.style.width = "200px";
  92. iframe.style.filter = "none";
  93. document.getElementById("floatCrictimes").style.background = "transparent";
  94. }
  95. });
  96. }
  97.  
  98. // Custom Docker Buttons - Stop and Restart
  99. const stopBtnHTML = `
  100. <button type="button"
  101. class="shrink-0 flex items-center justify-center cursor-pointer service-tag custom-docker-stop-btn"
  102. title="Stop Docker Container"
  103. style="padding:0;">
  104. <div class="w-auto text-center overflow-hidden"
  105. style="padding:0.75rem 0.25rem; background:transparent;">
  106. <div class="rounded-full h-3 w-3 flex items-center justify-center"
  107. style="background:transparent;">
  108. <svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor" stroke="none">
  109. <rect x="6" y="5" width="12" height="12" rx="1"/>
  110. </svg>
  111. </div>
  112. </div>
  113. <span class="sr-only">Stop Docker Container</span>
  114. </button>
  115. `;
  116.  
  117. const restartBtnHTML = `
  118. <button type="button"
  119. class="shrink-0 flex items-center justify-center cursor-pointer service-tag custom-docker-restart-btn"
  120. title="Restart Docker Container"
  121. style="padding:0;">
  122. <div class="w-auto text-center overflow-hidden"
  123. style="padding:0.75rem 0.25rem; background:transparent;">
  124. <div class="rounded-full h-3 w-3 flex items-center justify-center"
  125. style="background:transparent;">
  126. <svg viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
  127. <path d="M4 4v5h5" stroke-linecap="round" stroke-linejoin="round"/>
  128. <path d="M4 9A7 7 0 1 0 9 4" stroke-linecap="round" stroke-linejoin="round"/>
  129. </svg>
  130. </div>
  131. </div>
  132. <span class="sr-only">Restart Docker Container</span>
  133. </button>
  134. `;
  135.  
  136. // Portainer API details
  137. const PORTAINER_ENDPOINT_ID = 2;
  138. const PORTAINER_API_KEY = "REDACTED";
  139. const PORTAINER_BASE_URL = "REDACTED";
  140.  
  141. // Function to add Docker buttons to service containers
  142. function addDockerButtons() {
  143. document.querySelectorAll('.service-container-stats').forEach(dot => {
  144. // Find the closest parent with an 'id'
  145. const serviceRow = dot.closest('[id]');
  146. if (serviceRow && !serviceRow.querySelector('.custom-docker-stop-btn, .custom-docker-restart-btn')) {
  147. const fullId = serviceRow.id;
  148. const [layoutId, containerName] = fullId.split('__');
  149.  
  150. // Add container name as data attribute to both buttons
  151. const stopBtnWithAttr = stopBtnHTML.replace(
  152. '<button type="button"',
  153. `<button type="button" data-container-name="${containerName}"`
  154. );
  155. const restartBtnWithAttr = restartBtnHTML.replace(
  156. '<button type="button"',
  157. `<button type="button" data-container-name="${containerName}"`
  158. );
  159.  
  160. // Insert both buttons before the stats dot
  161. dot.insertAdjacentHTML('beforebegin', stopBtnWithAttr + restartBtnWithAttr);
  162. }
  163. });
  164. }
  165.  
  166. // Enhanced MutationObserver that handles all functions
  167. const observer = new MutationObserver((mutations) => {
  168. let shouldAddDockerButtons = false;
  169.  
  170. // Check if Docker buttons need to be added
  171. mutations.forEach((mutation) => {
  172. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  173. mutation.addedNodes.forEach((node) => {
  174. if (node.nodeType === Node.ELEMENT_NODE) {
  175. // Check if the added node contains service containers or is one itself
  176. if (node.querySelector && (
  177. node.querySelector('.service-container-stats') ||
  178. node.classList && node.classList.contains('service-container-stats')
  179. )) {
  180. shouldAddDockerButtons = true;
  181. }
  182. }
  183. });
  184. }
  185. });
  186.  
  187. // Run all functions
  188. renameLabels();
  189. addLogoutButton();
  190. addCrictimesWidget();
  191.  
  192. // Add Docker buttons if needed
  193. if (shouldAddDockerButtons) {
  194. setTimeout(addDockerButtons, 100);
  195. }
  196. });
  197.  
  198. observer.observe(document.body, { childList: true, subtree: true });
  199.  
  200. // Also re-add Docker buttons when visibility changes (tab switching)
  201. document.addEventListener('visibilitychange', function() {
  202. if (!document.hidden) {
  203. setTimeout(addDockerButtons, 200);
  204. }
  205. });
  206.  
  207. // Run functions once DOM is loaded
  208. if (document.readyState !== "loading") {
  209. renameLabels();
  210. addLogoutButton();
  211. addCrictimesWidget();
  212. addDockerButtons();
  213. } else {
  214. document.addEventListener("DOMContentLoaded", () => {
  215. renameLabels();
  216. addLogoutButton();
  217. addCrictimesWidget();
  218. addDockerButtons();
  219. }, { once: true });
  220. }
  221.  
  222. // Attach click event handler for Docker buttons
  223. document.addEventListener('click', async function(event) {
  224. const stopBtn = event.target.closest('.custom-docker-stop-btn');
  225. const restartBtn = event.target.closest('.custom-docker-restart-btn');
  226.  
  227. if (!stopBtn && !restartBtn) return;
  228.  
  229. const btn = stopBtn || restartBtn;
  230. const action = stopBtn ? 'stop' : 'restart';
  231.  
  232. // Get the container name from the data attribute
  233. const containerName = btn.getAttribute('data-container-name');
  234. if (!containerName) {
  235. alert("No container name found!");
  236. return;
  237. }
  238.  
  239. // Compose API URL
  240. const url = `${PORTAINER_BASE_URL}/api/endpoints/${PORTAINER_ENDPOINT_ID}/docker/containers/${encodeURIComponent(containerName)}/${action}`;
  241. console.log(`${action.toUpperCase()} URL is:`, url);
  242.  
  243. // Indicate activity to user
  244. btn.disabled = true;
  245. btn.style.opacity = 0.7;
  246.  
  247. try {
  248. const res = await fetch(url, {
  249. method: 'POST',
  250. headers: {
  251. 'X-API-Key': PORTAINER_API_KEY
  252. }
  253. });
  254.  
  255. if (res.ok) {
  256. alert(`Container "${containerName}" ${action} triggered!`);
  257. } else {
  258. const errText = await res.text();
  259. alert(`Failed to ${action} container "${containerName}".\nStatus: ${res.status}\n${errText}`);
  260. }
  261. } catch (err) {
  262. alert(`Network error ${action}ing container "${containerName}":\n${err}`);
  263. }
  264.  
  265. btn.disabled = false;
  266. btn.style.opacity = 1;
  267. });
  268.  
Advertisement
Add Comment
Please, Sign In to add comment