fix: restore platform backup data

This commit is contained in:
2026-04-29 14:11:00 +00:00
parent 69b528a499
commit 90ca53356d
+75 -33
View File
@@ -206,14 +206,12 @@ export class BackupManager {
for (const resourceType of resourceTypes) {
const dataDir = `${tempDir}/data/${resourceType}`;
try {
for await (const entry of Deno.readDir(dataDir)) {
if (entry.isFile) {
items.push({
stream: plugins.nodeFs.createReadStream(`${dataDir}/${entry.name}`),
name: `data/${resourceType}/${entry.name}`,
type: 'data',
});
}
for await (const filePath of this.walkFiles(dataDir)) {
items.push({
stream: plugins.nodeFs.createReadStream(filePath),
name: plugins.path.relative(tempDir, filePath).replaceAll('\\', '/'),
type: 'data',
});
}
} catch {
// Directory may not exist if export produced no files
@@ -819,7 +817,7 @@ export class BackupManager {
throw new Error('MongoDB service not running');
}
const connectionUri = credentials.connectionUri || credentials.MONGODB_URI;
const connectionUri = credentials.connectionUri || credentials.connectionString || credentials.MONGODB_URI;
if (!connectionUri) {
throw new Error('MongoDB connection URI not found in credentials');
}
@@ -836,19 +834,8 @@ export class BackupManager {
throw new Error(`mongodump failed: ${result.stderr}`);
}
const container = await this.oneboxRef.docker.getContainerById(mongoService.containerId);
if (!container) {
throw new Error('MongoDB container not found');
}
const copyResult = await this.oneboxRef.docker.execInContainer(mongoService.containerId, [
'cat',
archivePath,
]);
const localPath = `${dataDir}/${resource.resourceName}.archive`;
const encoder = new TextEncoder();
await Deno.writeFile(localPath, encoder.encode(copyResult.stdout));
await this.copyFromContainer(mongoService.containerId, archivePath, localPath);
await this.oneboxRef.docker.execInContainer(mongoService.containerId, ['rm', archivePath]);
@@ -868,7 +855,7 @@ export class BackupManager {
const bucketDir = `${dataDir}/${resource.resourceName}`;
await Deno.mkdir(bucketDir, { recursive: true });
const s3Info = this.getS3ConnectionInfo(credentials);
const s3Info = await this.getReachableS3ConnectionInfo(credentials, resource.platformServiceId);
const s3Client = this.createS3Client(s3Info);
let objectCount = 0;
let continuationToken: string | undefined;
@@ -1137,6 +1124,36 @@ export class BackupManager {
return imageName;
}
private async copyFromContainer(
containerId: string,
containerPath: string,
outputPath: string,
): Promise<void> {
await this.runDockerCp([`${containerId}:${containerPath}`, outputPath], 'docker cp from container failed');
}
private async copyToContainer(
inputPath: string,
containerId: string,
containerPath: string,
): Promise<void> {
await this.runDockerCp([inputPath, `${containerId}:${containerPath}`], 'docker cp to container failed');
}
private async runDockerCp(args: string[], errorMessage: string): Promise<void> {
const command = new Deno.Command('docker', {
args: ['cp', ...args],
stdout: 'piped',
stderr: 'piped',
});
const result = await command.output();
if (!result.success) {
const stderr = new TextDecoder().decode(result.stderr).trim();
throw new Error(`${errorMessage}: ${stderr}`);
}
}
/**
* Restore platform resources for a service
*/
@@ -1241,22 +1258,14 @@ export class BackupManager {
}
const archivePath = `${dataDir}/${backupResourceName}.archive`;
const connectionUri = credentials.connectionUri || credentials.MONGODB_URI;
const connectionUri = credentials.connectionUri || credentials.connectionString || credentials.MONGODB_URI;
if (!connectionUri) {
throw new Error('MongoDB connection URI not found');
}
const archiveData = await Deno.readFile(archivePath);
const containerArchivePath = `/tmp/${resource.resourceName}.archive`;
const base64Data = btoa(String.fromCharCode(...archiveData));
await this.oneboxRef.docker.execInContainer(mongoService.containerId, [
'bash',
'-c',
`echo '${base64Data}' | base64 -d > ${containerArchivePath}`,
]);
await this.copyToContainer(archivePath, mongoService.containerId, containerArchivePath);
const result = await this.oneboxRef.docker.execInContainer(mongoService.containerId, [
'mongorestore',
@@ -1288,7 +1297,7 @@ export class BackupManager {
const bucketDir = `${dataDir}/${backupResourceName}`;
const s3Info = this.getS3ConnectionInfo(credentials);
const s3Info = await this.getReachableS3ConnectionInfo(credentials, resource.platformServiceId);
const s3Client = this.createS3Client(s3Info);
let uploadedCount = 0;
@@ -1619,6 +1628,39 @@ export class BackupManager {
};
}
private async getReachableS3ConnectionInfo(
credentials: Record<string, string>,
platformServiceId: number,
): Promise<IS3ConnectionInfo> {
const s3Info = this.getS3ConnectionInfo(credentials);
let endpointUrl: URL;
try {
endpointUrl = new URL(s3Info.endpoint);
} catch {
return s3Info;
}
if (endpointUrl.hostname !== 'onebox-minio') {
return s3Info;
}
const platformService = this.oneboxRef.database.getPlatformServiceById(platformServiceId);
const hostPort = platformService?.containerId
? await this.oneboxRef.docker.getContainerHostPort(platformService.containerId, 9000)
: null;
if (!hostPort) {
return s3Info;
}
endpointUrl.hostname = '127.0.0.1';
endpointUrl.port = String(hostPort);
return {
...s3Info,
endpoint: endpointUrl.toString().replace(/\/$/, ''),
};
}
private createS3Client(s3Info: IS3ConnectionInfo) {
return new plugins.awsS3.S3Client({
endpoint: s3Info.endpoint,