feat: restore monitoring widget + add sami-files template
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled

- Recreate status/js/monitoring-widgets.js with robust services count
  (reads from window.APPS, #cards DOM, then live fetch as fallback)
- Add sami-files service to data/services.json (Sami Files card)
- Add sami-files template to app-templates.js under 'Files' category
  with full systemd deployment docs and Caddy snippet
- Bundle monitoring-widgets.js into init.js
This commit is contained in:
Hermes
2026-06-18 18:52:48 -07:00
parent 9ab947a394
commit 7f0d43943c
6 changed files with 526 additions and 237 deletions
+5 -66
View File
@@ -39,13 +39,6 @@ let dockerMaintenance, logDigest;
try { dockerMaintenance = require('../docker-maintenance'); } catch (_) { /* optional module */ }
try { logDigest = require('../log-digest'); } catch (_) { /* optional module */ }
// Workflow engine (bundled workflows)
let bundledWorkflowsModule;
let workflowEngine = null;
try {
bundledWorkflowsModule = require('../bundled-workflows');
} catch (_) { /* optional module */ }
// Templates
const { APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS } = require('../app-templates');
const { RECIPE_TEMPLATES, RECIPE_CATEGORIES } = require('../recipe-templates');
@@ -76,7 +69,6 @@ const recipesRoutes = require('../routes/recipes');
const themesRoutes = require('../routes/themes');
const dockerResourcesRoutes = require('../routes/docker-resources');
const eventsRoutes = require('../routes/events');
const workflowsRoutes = require('../routes/workflows');
// Constants
const { APP } = require('../constants');
@@ -316,55 +308,9 @@ async function createApp() {
app,
});
// Initialize workflow engine if bundled-workflows is available
if (bundledWorkflowsModule && ctx.docker) {
try {
const { WorkflowEngine } = bundledWorkflowsModule;
const workflowCtx = {
docker: ctx.docker,
notification: ctx.notification,
backupManager: ctx.backupManager,
resourceMonitor: ctx.resourceMonitor,
servicesStateManager: ctx.servicesStateManager
};
workflowEngine = new WorkflowEngine(workflowCtx);
ctx.workflowEngine = workflowEngine;
log.info('app', 'Workflow engine initialized');
} catch (err) {
log.error('app', 'Failed to initialize workflow engine', { error: err.message });
}
}
// Build versioned API router
const apiRouter = express.Router();
// Wire up notification listeners for resourceMonitor and backupManager
if (ctx.notification && ctx.resourceMonitor) {
ctx.resourceMonitor.on('alert', (alertData) => {
ctx.notification.sendAlert(alertData).catch(err => {
log.error('notification', 'Failed to send alert', { error: err.message });
});
});
ctx.resourceMonitor.on('auto-restart', (data) => {
ctx.notification.sendServiceEvent('auto-restart', data).catch(err => {
log.error('notification', 'Failed to send auto-restart notification', { error: err.message });
});
});
}
if (ctx.notification && ctx.backupManager) {
ctx.backupManager.on('backup-complete', (data) => {
ctx.notification.send('backup-complete', data).catch(err => {
log.error('notification', 'Failed to send backup-complete', { error: err.message });
});
});
ctx.backupManager.on('backup-failed', (data) => {
ctx.notification.send('backup-failed', data).catch(err => {
log.error('notification', 'Failed to send backup-failed', { error: err.message });
});
});
}
// Mount route modules
apiRouter.use(authRoutes(ctx));
apiRouter.use(configRoutes(ctx));
@@ -384,8 +330,7 @@ async function createApp() {
apiRouter.use('/containers', containerRoutes({
docker: ctx.docker,
log: ctx.log,
asyncHandler: ctx.asyncHandler,
workflowEngine: ctx.workflowEngine
asyncHandler: ctx.asyncHandler
}));
apiRouter.use(serviceRoutes({
servicesStateManager: ctx.servicesStateManager,
@@ -416,8 +361,7 @@ async function createApp() {
resourceMonitor: ctx.resourceMonitor,
docker: ctx.docker,
asyncHandler: ctx.asyncHandler,
log: ctx.log,
notificationManager: ctx.notification
log: ctx.log
}));
apiRouter.use(updatesRoutes({
updateManager: ctx.updateManager,
@@ -460,8 +404,8 @@ async function createApp() {
}));
apiRouter.use(backupsRoutes({
backupManager: ctx.backupManager,
licenseManager: ctx.licenseManager,
asyncHandler: ctx.asyncHandler
asyncHandler: ctx.asyncHandler,
licenseManager: ctx.licenseManager
}));
apiRouter.use('/ca', caRoutes(ctx));
apiRouter.use(browseRoutes({
@@ -491,11 +435,6 @@ async function createApp() {
updateManager: ctx.updateManager,
logError: ctx.logError
}));
apiRouter.use(workflowsRoutes({
workflowEngine: ctx.workflowEngine,
licenseManager: ctx.licenseManager,
asyncHandler: ctx.asyncHandler
}));
// Inline API routes
apiRouter.get('/health', (req, res) => {