Skip to content

Commit 0b3ebe7

Browse files
committed
feat: implement pg dump export
1 parent 48531a2 commit 0b3ebe7

File tree

4 files changed

+42
-12
lines changed

4 files changed

+42
-12
lines changed

apps/backend/src/system/export/export.controller.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ export class ExportController {
2424
},
2525
},
2626
})
27-
get(@Response() res: FastifyReply) {
28-
const data = this.exportService.fullDatabaseDump();
29-
const buffer = Buffer.from(data, "utf-8");
30-
res.header("Content-Disposition", "attachment; filename=dump.sql");
31-
res.header("Content-Type", "text/sql");
32-
res.header("Content-Length", buffer.byteLength);
33-
res.send(data);
27+
async get(@Response() res: FastifyReply) {
28+
try {
29+
const data = await this.exportService.fullDatabaseDump();
30+
const buffer = Buffer.from(data, "utf-8");
31+
res.header("Content-Disposition", "attachment; filename=dump.sql");
32+
res.header("Content-Type", "text/sql");
33+
res.header("Content-Length", buffer.byteLength.toString());
34+
res.send(buffer);
35+
} catch (error) {
36+
res.status(500).send(error.message);
37+
}
3438
}
3539
}
Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,31 @@
11
import { Injectable } from "@nestjs/common";
2+
import { exec } from "node:child_process";
3+
import { promisify } from "node:util";
4+
import { ConfigService } from "../../config/config.service";
5+
6+
const execPromise = promisify(exec);
27

38
@Injectable()
49
export class ExportService {
5-
fullDatabaseDump() {
6-
return "Hoi";
10+
constructor(private readonly configService: ConfigService) {}
11+
12+
async fullDatabaseDump() {
13+
try {
14+
const user = this.configService.get("database.user");
15+
const database = this.configService.get("database.name");
16+
const password = this.configService.get("database.password");
17+
const host = this.configService.get("database.host");
18+
const port = this.configService.get("database.port");
19+
20+
const command = `PGPASSWORD=${password} pg_dump -U ${user} -h ${host} -p ${port} ${database}`;
21+
22+
const { stdout, stderr } = await execPromise(command);
23+
if (stderr) {
24+
throw new Error(stderr);
25+
}
26+
return stdout;
27+
} catch (error) {
28+
throw new Error(`Database dump failed: ${error.message}`);
29+
}
730
}
831
}

apps/backend/src/system/system.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import { TerminusModule } from "@nestjs/terminus";
1313
import { StatusController } from "./status/status.controller";
1414
import { ExportController } from "./export/export.controller";
1515
import { ExportService } from "./export/export.service";
16+
import { ConfigModule } from "../config/config.module";
1617

1718
@Module({
18-
imports: [MikroOrmModule.forFeature([User]), TerminusModule],
19+
imports: [MikroOrmModule.forFeature([User]), TerminusModule, ConfigModule],
1920
controllers: [UsersController, SessionController, HealthController, StatusController, ExportController],
2021
providers: [
2122
UsersService,

apps/frontend/src/routes/_auth/administration/export/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { createFileRoute } from "@tanstack/react-router";
33
import { $api } from "../../../../stores/api";
44

55
function AdministrationExportIndex() {
6-
const { downloadFile } = $api.useDownload("/export", "dump.sql");
6+
const { isDownloading, downloadFile } = $api.useDownload("/export", "dump.sql");
77
return (
88
<div>
9-
<Button onClick={() => downloadFile()}>Download</Button>
9+
<Button loading={isDownloading} onClick={() => downloadFile()}>
10+
Download
11+
</Button>
1012
</div>
1113
);
1214
}

0 commit comments

Comments
 (0)