feat: cloud backup destinations + long-term resource history
Cloud backups (Dropbox / WebDAV / SFTP):
- backup-manager.js: save + load handlers per provider, credential
resolution via credentialManager, destination probe.
- routes/backups.js: /credentials/{provider} (masked GET, POST, DELETE),
/test-destination, scheduling endpoints.
- status/js/backup-restore.js: destination picker, provider-specific
credential forms, test button wired to backend probe.
- npm deps already present (dropbox 10.34.0, webdav 5.7.1,
ssh2-sftp-client 11.0.0).
Resource history:
- resource-monitor.js: three-tier rollup storage — raw 10s samples
(7-day retention), hourly rollups (30-day), daily rollups
(365-day). getHistoryByRange() auto-selects the appropriate tier.
- routes/monitoring.js: /monitoring/history/:containerId now supports
startTime/endTime range mode (legacy ?hours=N still works).
- status/js/resource-monitor.js + dashboard.css: "History" tab with
range buttons (1h/24h/7d/30d/1y), SVG sparklines for
CPU / memory / network. Renderer handles raw and rolled-up shapes.
status/dist/features.js rebuilt from source via build.js.
Lifted out of wip/cloud-backups-and-history; the half-finished
app-deps feature from that branch (frontend calls /api/v1/apps/
check-dependencies but the endpoint doesn't exist) is preserved
separately on wip/app-deps for later.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,11 +31,28 @@ module.exports = function({ resourceMonitor, docker, asyncHandler, log }) {
|
||||
success(res, { stats });
|
||||
}, 'monitoring-stats-container'));
|
||||
|
||||
// Get historical stats
|
||||
// Get historical stats — supports either ?hours=24 (legacy raw) OR ?startTime=...&endTime=...
|
||||
// (range mode auto-selects raw / hourly / daily tier)
|
||||
router.get('/monitoring/history/:containerId', asyncHandler(async (req, res) => {
|
||||
const containerId = req.params.containerId;
|
||||
|
||||
// Range mode (preferred)
|
||||
if (req.query.startTime && req.query.endTime) {
|
||||
const startTime = parseInt(req.query.startTime, 10);
|
||||
const endTime = parseInt(req.query.endTime, 10);
|
||||
if (Number.isNaN(startTime) || Number.isNaN(endTime) || startTime >= endTime) {
|
||||
const { ValidationError } = require('../errors');
|
||||
throw new ValidationError('Invalid startTime/endTime');
|
||||
}
|
||||
const result = resourceMonitor.getHistoryByRange(containerId, startTime, endTime);
|
||||
success(res, { ...result, startTime, endTime });
|
||||
return;
|
||||
}
|
||||
|
||||
// Legacy hours-based mode (raw samples only)
|
||||
const hours = parseInt(req.query.hours) || 24;
|
||||
const history = resourceMonitor.getHistoricalStats(req.params.containerId, hours);
|
||||
success(res, { history, hours });
|
||||
const history = resourceMonitor.getHistoricalStats(containerId, hours);
|
||||
success(res, { history, hours, tier: 'raw', samples: history, unit: '10s' });
|
||||
}, 'monitoring-history'));
|
||||
|
||||
// Get aggregated stats
|
||||
|
||||
Reference in New Issue
Block a user