feat(backup): Add backup system: BackupManager, DB schema, API endpoints and UI support
Introduce a complete service backup/restore subsystem with encrypted archives, database records and REST endpoints. Implements BackupManager with export/import for service config, platform resources (MongoDB, MinIO, ClickHouse), and Docker images; adds BackupRepository and migrations for backups table and include_image_in_backup; integrates backup flows into the HTTP API and the UI client; exposes backup password management and restore modes (restore/import/clone). Wire BackupManager into Onebox initialization.
This commit is contained in:
86
ts/database/repositories/backup.repository.ts
Normal file
86
ts/database/repositories/backup.repository.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Backup Repository
|
||||
* Handles CRUD operations for backups table
|
||||
*/
|
||||
|
||||
import { BaseRepository } from '../base.repository.ts';
|
||||
import type { IBackup, TPlatformServiceType } from '../../types.ts';
|
||||
|
||||
export class BackupRepository extends BaseRepository {
|
||||
create(backup: Omit<IBackup, 'id'>): IBackup {
|
||||
this.query(
|
||||
`INSERT INTO backups (
|
||||
service_id, service_name, filename, size_bytes, created_at,
|
||||
includes_image, platform_resources, checksum
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
backup.serviceId,
|
||||
backup.serviceName,
|
||||
backup.filename,
|
||||
backup.sizeBytes,
|
||||
backup.createdAt,
|
||||
backup.includesImage ? 1 : 0,
|
||||
JSON.stringify(backup.platformResources),
|
||||
backup.checksum,
|
||||
]
|
||||
);
|
||||
|
||||
// Get the created backup by looking for the most recent one with matching filename
|
||||
const rows = this.query(
|
||||
'SELECT * FROM backups WHERE filename = ? ORDER BY id DESC LIMIT 1',
|
||||
[backup.filename]
|
||||
);
|
||||
|
||||
return this.rowToBackup(rows[0]);
|
||||
}
|
||||
|
||||
getById(id: number): IBackup | null {
|
||||
const rows = this.query('SELECT * FROM backups WHERE id = ?', [id]);
|
||||
return rows.length > 0 ? this.rowToBackup(rows[0]) : null;
|
||||
}
|
||||
|
||||
getByService(serviceId: number): IBackup[] {
|
||||
const rows = this.query(
|
||||
'SELECT * FROM backups WHERE service_id = ? ORDER BY created_at DESC',
|
||||
[serviceId]
|
||||
);
|
||||
return rows.map((row) => this.rowToBackup(row));
|
||||
}
|
||||
|
||||
getAll(): IBackup[] {
|
||||
const rows = this.query('SELECT * FROM backups ORDER BY created_at DESC');
|
||||
return rows.map((row) => this.rowToBackup(row));
|
||||
}
|
||||
|
||||
delete(id: number): void {
|
||||
this.query('DELETE FROM backups WHERE id = ?', [id]);
|
||||
}
|
||||
|
||||
deleteByService(serviceId: number): void {
|
||||
this.query('DELETE FROM backups WHERE service_id = ?', [serviceId]);
|
||||
}
|
||||
|
||||
private rowToBackup(row: any): IBackup {
|
||||
let platformResources: TPlatformServiceType[] = [];
|
||||
const platformResourcesRaw = row.platform_resources;
|
||||
if (platformResourcesRaw) {
|
||||
try {
|
||||
platformResources = JSON.parse(String(platformResourcesRaw));
|
||||
} catch {
|
||||
platformResources = [];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: Number(row.id),
|
||||
serviceId: Number(row.service_id),
|
||||
serviceName: String(row.service_name),
|
||||
filename: String(row.filename),
|
||||
sizeBytes: Number(row.size_bytes),
|
||||
createdAt: Number(row.created_at),
|
||||
includesImage: Boolean(row.includes_image),
|
||||
platformResources,
|
||||
checksum: String(row.checksum),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user