feat: add encrypted online settings backup keys

Add immutable client-encrypted settings snapshots with independent MDD2 capability keys so fresh installs can restore configuration without transferring backup files. Keep credentials encrypted end to end, preserve queues and history during import, and avoid exposing identifiers in request URLs or errors. Include the persistent API with quota, rate limits, crash-safe storage locking, durability checks, and end-to-end race and recovery coverage.
This commit is contained in:
Sucukdeluxe
2026-08-07 18:29:06 +02:00
parent 5f19293ed2
commit 6e44e63167
22 changed files with 1832 additions and 44 deletions
+40
View File
@@ -0,0 +1,40 @@
import { resolve } from 'node:path'
import { createBackupServer } from './server.mjs'
const port = Number.parseInt(process.env.PORT ?? '8787', 10)
const host = process.env.HOST ?? '127.0.0.1'
const rootDir = resolve(process.env.BACKUP_DATA_DIR ?? './data')
const allowedOrigins = (process.env.ALLOWED_ORIGINS ?? '')
.split(',')
.map(origin => origin.trim())
.filter(Boolean)
const rateLimit = {
max: Number.parseInt(process.env.RATE_LIMIT_MAX ?? '60', 10),
windowMs: Number.parseInt(process.env.RATE_LIMIT_WINDOW_MS ?? '60000', 10)
}
const uploadRateLimit = {
max: Number.parseInt(process.env.UPLOAD_RATE_LIMIT_MAX ?? '10', 10),
windowMs: Number.parseInt(process.env.UPLOAD_RATE_LIMIT_WINDOW_MS ?? '3600000', 10)
}
const maxStorageBytes = Number.parseInt(process.env.MAX_STORAGE_BYTES ?? String(10 * 1024 * 1024 * 1024), 10)
const trustedProxy = process.env.TRUST_PROXY === 'true'
if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) throw new Error('Invalid PORT')
const server = createBackupServer({ rootDir, allowedOrigins, rateLimit, uploadRateLimit, maxStorageBytes, trustedProxy })
server.listen(port, host, () => {
process.stdout.write(`Backup API listening on ${host}:${port}\n`)
})
function shutdown() {
server.close(error => {
if (error) {
process.stderr.write('Backup API shutdown failed\n')
process.exitCode = 1
}
})
}
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)