refactor: complete opsserver migration
This commit is contained in:
@@ -43,6 +43,7 @@ export class OpsServer {
|
||||
|
||||
// Set up all handlers
|
||||
await this.setupHandlers();
|
||||
this.registerCustomRoutes();
|
||||
|
||||
await this.server.start(port);
|
||||
logger.success(`OpsServer started on http://localhost:${port}`);
|
||||
@@ -72,6 +73,78 @@ export class OpsServer {
|
||||
logger.success('OpsServer TypedRequest handlers initialized');
|
||||
}
|
||||
|
||||
private registerCustomRoutes(): void {
|
||||
this.server.typedserver.addRoute(
|
||||
'/backups/:backupId/download',
|
||||
'GET',
|
||||
async (ctx) => {
|
||||
const jwt = ctx.query.jwt;
|
||||
if (!jwt) {
|
||||
return new Response('Missing JWT', { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
await this.adminHandler.getVerifiedAdminIdentity({
|
||||
jwt,
|
||||
userId: '',
|
||||
username: '',
|
||||
expiresAt: 0,
|
||||
role: 'user',
|
||||
});
|
||||
} catch {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
|
||||
const backupId = Number(ctx.params.backupId);
|
||||
if (!Number.isInteger(backupId) || backupId < 1) {
|
||||
return new Response('Invalid backup id', { status: 400 });
|
||||
}
|
||||
|
||||
const backup = this.oneboxRef.database.getBackupById(backupId);
|
||||
if (!backup) {
|
||||
return new Response('Backup not found', { status: 404 });
|
||||
}
|
||||
|
||||
const filename = this.sanitizeDownloadFilename(
|
||||
backup.filename || `${backup.serviceName}-${backup.createdAt}.tar.enc`,
|
||||
);
|
||||
|
||||
let filePath = this.oneboxRef.backupManager.getBackupFilePath(backupId);
|
||||
let shouldCleanup = false;
|
||||
|
||||
if (!filePath) {
|
||||
filePath = await this.oneboxRef.backupManager.getBackupExportPath(backupId);
|
||||
shouldCleanup = !!filePath;
|
||||
}
|
||||
|
||||
if (!filePath) {
|
||||
return new Response('Backup export unavailable', { status: 404 });
|
||||
}
|
||||
|
||||
try {
|
||||
const fileData = await Deno.readFile(filePath);
|
||||
return new Response(fileData, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'content-type': 'application/octet-stream',
|
||||
'content-disposition': `attachment; filename="${filename}"`,
|
||||
'content-length': String(fileData.byteLength),
|
||||
'cache-control': 'no-store',
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
if (shouldCleanup) {
|
||||
await Deno.remove(filePath).catch(() => {});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private sanitizeDownloadFilename(filename: string): string {
|
||||
return filename.replace(/["\\\r\n]/g, '_');
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
if (this.server) {
|
||||
await this.server.stop();
|
||||
|
||||
Reference in New Issue
Block a user