feat: add Docker and Gitea services, monitoring, queue, and Telegram notification functionalities
- Implemented Docker operations including image building, container management, and resource stats. - Added Gitea API client for repository management and webhook handling. - Introduced monitoring service to collect and store container metrics in InfluxDB. - Created a queue system using BullMQ for managing deployment jobs with real-time log streaming. - Developed Telegram notification service for deployment status updates. - Added Traefik label generation for dynamic reverse proxy configuration. - Implemented WebSocket endpoints for log streaming and terminal access to containers. - Created an updater sidecar for self-updating the AutoDeployer container.
This commit is contained in:
74
dashboard/js/router.js
Normal file
74
dashboard/js/router.js
Normal file
@@ -0,0 +1,74 @@
|
||||
// Simple hash-based SPA router
|
||||
const routes = {};
|
||||
let currentCleanup = null;
|
||||
|
||||
export function route(path, handler) {
|
||||
routes[path] = handler;
|
||||
}
|
||||
|
||||
export function navigate(path) {
|
||||
window.location.hash = path;
|
||||
}
|
||||
|
||||
export function currentPath() {
|
||||
return window.location.hash.slice(1) || '/';
|
||||
}
|
||||
|
||||
export function getParam(name) {
|
||||
const path = currentPath();
|
||||
// Match patterns like /services/:id
|
||||
for (const pattern of Object.keys(routes)) {
|
||||
const paramNames = [];
|
||||
const regex = pattern.replace(/:(\w+)/g, (_, name) => {
|
||||
paramNames.push(name);
|
||||
return '([^/]+)';
|
||||
});
|
||||
const match = path.match(new RegExp(`^${regex}$`));
|
||||
if (match) {
|
||||
const idx = paramNames.indexOf(name);
|
||||
if (idx >= 0) return match[idx + 1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function startRouter(container) {
|
||||
function handleRoute() {
|
||||
const path = currentPath();
|
||||
|
||||
// Cleanup previous page
|
||||
if (currentCleanup) { currentCleanup(); currentCleanup = null; }
|
||||
|
||||
// Find matching route
|
||||
for (const [pattern, handler] of Object.entries(routes)) {
|
||||
const paramNames = [];
|
||||
const regex = pattern.replace(/:(\w+)/g, (_, n) => { paramNames.push(n); return '([^/]+)'; });
|
||||
const match = path.match(new RegExp(`^${regex}$`));
|
||||
if (match) {
|
||||
const params = {};
|
||||
paramNames.forEach((n, i) => params[n] = match[i + 1]);
|
||||
const cleanup = handler(container, params);
|
||||
if (typeof cleanup === 'function') currentCleanup = cleanup;
|
||||
updateActiveLink(path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Default: redirect to /
|
||||
navigate('/');
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', handleRoute);
|
||||
handleRoute();
|
||||
}
|
||||
|
||||
function updateActiveLink(path) {
|
||||
document.querySelectorAll('.sidebar-link[data-path]').forEach(el => {
|
||||
const linkPath = el.dataset.path;
|
||||
if (linkPath === '/') {
|
||||
el.classList.toggle('active', path === '/');
|
||||
} else {
|
||||
el.classList.toggle('active', path.startsWith(linkPath));
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user