147 lines
5.3 KiB
TypeScript
147 lines
5.3 KiB
TypeScript
import type { Request } from '@sveltejs/kit';
|
|
import { saveServerLog } from '$lib/api/applications/logging';
|
|
import { execShellAsync } from '$lib/api/common';
|
|
import { docker } from '$lib/api/docker';
|
|
import fs from 'fs';
|
|
|
|
export async function post(request: Request) {
|
|
const tmpdir = '/tmp/backups';
|
|
const { deployId } = request.params;
|
|
try {
|
|
const now = new Date();
|
|
const configuration = JSON.parse(
|
|
JSON.parse(await execShellAsync(`docker inspect ${deployId}_${deployId}`))[0].Spec.Labels
|
|
.configuration
|
|
);
|
|
const type = configuration.general.type;
|
|
const serviceId = configuration.general.deployId;
|
|
const databaseService = (await docker.engine.listContainers()).find(
|
|
(r) => r.Labels['com.docker.stack.namespace'] === serviceId && r.State === 'running'
|
|
);
|
|
const containerID = databaseService.Labels['com.docker.swarm.task.name'];
|
|
await execShellAsync(`mkdir -p ${tmpdir}`);
|
|
if (type === 'mongodb') {
|
|
if (databaseService) {
|
|
const username = configuration.database.usernames[0];
|
|
const password = configuration.database.passwords[1];
|
|
const databaseName = configuration.database.defaultDatabaseName;
|
|
const filename = `${databaseName}_${now.getTime()}.gz`;
|
|
const fullfilename = `${tmpdir}/${filename}`;
|
|
await execShellAsync(
|
|
`docker exec -i ${containerID} /bin/bash -c "mkdir -p ${tmpdir};mongodump --uri='mongodb://${username}:${password}@${deployId}:27017' -d ${databaseName} --gzip --archive=${fullfilename}"`
|
|
);
|
|
await execShellAsync(`docker cp ${containerID}:${fullfilename} ${fullfilename}`);
|
|
await execShellAsync(`docker exec -i ${containerID} /bin/bash -c "rm -f ${fullfilename}"`);
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Transfer-Encoding': 'binary',
|
|
'Content-Disposition': `attachment; filename=${filename}`
|
|
},
|
|
body: fs.readFileSync(`${fullfilename}`)
|
|
};
|
|
}
|
|
} else if (type === 'postgresql') {
|
|
if (databaseService) {
|
|
const username = configuration.database.usernames[0];
|
|
const password = configuration.database.passwords[0];
|
|
const databaseName = configuration.database.defaultDatabaseName;
|
|
const filename = `${databaseName}_${now.getTime()}.sql.gz`;
|
|
const fullfilename = `${tmpdir}/${filename}`;
|
|
await execShellAsync(
|
|
`docker exec -i ${containerID} /bin/bash -c "PGPASSWORD=${password} pg_dump --username ${username} -Z 9 ${databaseName}" > ${fullfilename}`
|
|
);
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Transfer-Encoding': 'binary',
|
|
'Content-Disposition': `attachment; filename=${filename}`
|
|
},
|
|
body: fs.readFileSync(`${fullfilename}`)
|
|
};
|
|
}
|
|
} else if (type === 'couchdb') {
|
|
if (databaseService) {
|
|
const databaseName = configuration.database.defaultDatabaseName;
|
|
const filename = `${databaseName}_${now.getTime()}.tar.gz`;
|
|
const fullfilename = `${tmpdir}/${filename}`;
|
|
await execShellAsync(
|
|
`docker exec -i ${containerID} /bin/bash -c "cd /bitnami/couchdb/data/ && tar -czvf - ." > ${fullfilename}`
|
|
);
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Transfer-Encoding': 'binary',
|
|
'Content-Disposition': `attachment; filename=${filename}`
|
|
},
|
|
body: fs.readFileSync(`${fullfilename}`)
|
|
};
|
|
}
|
|
} else if (type === 'mysql') {
|
|
if (databaseService) {
|
|
const username = configuration.database.usernames[0];
|
|
const password = configuration.database.passwords[0];
|
|
const databaseName = configuration.database.defaultDatabaseName;
|
|
const filename = `${databaseName}_${now.getTime()}.sql.gz`;
|
|
const fullfilename = `${tmpdir}/${filename}`;
|
|
await execShellAsync(
|
|
`docker exec -i ${containerID} /bin/bash -c "mysqldump -u ${username} -p${password} ${databaseName} | gzip -9 -" > ${fullfilename}`
|
|
);
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Transfer-Encoding': 'binary',
|
|
'Content-Disposition': `attachment; filename=${filename}`
|
|
},
|
|
body: fs.readFileSync(`${fullfilename}`)
|
|
};
|
|
}
|
|
} else if (type === 'redis') {
|
|
if (databaseService) {
|
|
const password = configuration.database.passwords[0];
|
|
const databaseName = configuration.database.defaultDatabaseName;
|
|
const filename = `${databaseName}_${now.getTime()}.rdb`;
|
|
const fullfilename = `${tmpdir}/${filename}`;
|
|
await execShellAsync(
|
|
`docker exec -i ${containerID} /bin/bash -c "redis-cli --pass ${password} save"`
|
|
);
|
|
await execShellAsync(
|
|
`docker cp ${containerID}:/bitnami/redis/data/dump.rdb ${fullfilename}`
|
|
);
|
|
await execShellAsync(
|
|
`docker exec -i ${containerID} /bin/bash -c "rm -f /bitnami/redis/data/dump.rdb"`
|
|
);
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Transfer-Encoding': 'binary',
|
|
'Content-Disposition': `attachment; filename=${filename}`
|
|
},
|
|
body: fs.readFileSync(`${fullfilename}`)
|
|
};
|
|
}
|
|
}
|
|
return {
|
|
status: 501,
|
|
body: {
|
|
error: `Backup method not implemented yet for ${type}.`
|
|
}
|
|
};
|
|
} catch (error) {
|
|
await saveServerLog(error);
|
|
return {
|
|
status: 500,
|
|
body: {
|
|
error: error.message || error
|
|
}
|
|
};
|
|
} finally {
|
|
await execShellAsync(`rm -fr ${tmpdir}`);
|
|
}
|
|
}
|