Skip to content

Commit 8f9349e

Browse files
committed
feat: implement use download
1 parent 6b6fef2 commit 8f9349e

File tree

13 files changed

+282
-15
lines changed

13 files changed

+282
-15
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Controller, Get, Response } from "@nestjs/common";
2+
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
3+
import { FastifyReply } from "fastify";
4+
import { ExportService } from "./export.service";
5+
6+
@Controller("export")
7+
export class ExportController {
8+
constructor(private readonly exportService: ExportService) {}
9+
10+
@Get()
11+
@ApiOperation({ summary: "Offers the backends data for download" })
12+
@ApiResponse({
13+
status: 200,
14+
description: "Database dump file",
15+
content: {
16+
"text/sql": {
17+
schema: { type: "string", format: "binary" },
18+
},
19+
},
20+
headers: {
21+
"Content-Disposition": {
22+
description: "Attachment dump.sql",
23+
schema: { type: "string", example: "attachment; filename=dump.sql" },
24+
},
25+
},
26+
})
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);
34+
}
35+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Injectable } from "@nestjs/common";
2+
3+
@Injectable()
4+
export class ExportService {
5+
fullDatabaseDump() {
6+
return "Hoi";
7+
}
8+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Controller, Get } from "@nestjs/common";
22
import { HealthCheck, HealthCheckService, MikroOrmHealthIndicator } from "@nestjs/terminus";
33
import { Public } from "../session/public.decorator";
4+
import { ApiOperation } from "@nestjs/swagger";
45

56
@Controller("health")
67
export class HealthController {
@@ -10,6 +11,7 @@ export class HealthController {
1011
) {}
1112

1213
@Get()
14+
@ApiOperation({ summary: "Returns the backends health information" })
1315
@HealthCheck()
1416
@Public()
1517
get() {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { Controller, Get } from "@nestjs/common";
22
import { Public } from "../session/public.decorator";
33
import { version } from "../../../package.json";
4+
import { ApiOperation } from "@nestjs/swagger";
45

56
@Controller("status")
67
export class StatusController {
78
@Get()
9+
@ApiOperation({ summary: "Returns the backends status information" })
810
@Public()
911
get() {
1012
return { version };

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ import { RolesGuard } from "./users/roles.guard";
1111
import { HealthController } from "./health/health.controller";
1212
import { TerminusModule } from "@nestjs/terminus";
1313
import { StatusController } from "./status/status.controller";
14+
import { ExportController } from "./export/export.controller";
15+
import { ExportService } from "./export/export.service";
1416

1517
@Module({
1618
imports: [MikroOrmModule.forFeature([User]), TerminusModule],
17-
controllers: [UsersController, SessionController, HealthController, StatusController],
18-
providers: [UsersService, SessionService, { provide: APP_GUARD, useClass: SessionGuard }, { provide: APP_GUARD, useClass: RolesGuard }],
19+
controllers: [UsersController, SessionController, HealthController, StatusController, ExportController],
20+
providers: [
21+
UsersService,
22+
SessionService,
23+
ExportService,
24+
{ provide: APP_GUARD, useClass: SessionGuard },
25+
{ provide: APP_GUARD, useClass: RolesGuard },
26+
],
1927
})
2028
export class SystemModule {}

apps/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@vitest/coverage-v8": "2.1.8",
4141
"jsdom": "^25.0.1",
4242
"openapi-typescript": "^7.4.4",
43+
"openapi-typescript-helpers": "^0.0.15",
4344
"typescript": "^5.7.2",
4445
"vite": "^6.0.2",
4546
"vitest": "^2.1.8"

apps/frontend/src/api.gen.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export interface paths {
6767
path?: never;
6868
cookie?: never;
6969
};
70+
/** Returns the backends health information */
7071
get: operations["HealthController_get"];
7172
put?: never;
7273
post?: never;
@@ -83,6 +84,7 @@ export interface paths {
8384
path?: never;
8485
cookie?: never;
8586
};
87+
/** Returns the backends status information */
8688
get: operations["StatusController_get"];
8789
put?: never;
8890
post?: never;
@@ -92,6 +94,23 @@ export interface paths {
9294
patch?: never;
9395
trace?: never;
9496
};
97+
"/export": {
98+
parameters: {
99+
query?: never;
100+
header?: never;
101+
path?: never;
102+
cookie?: never;
103+
};
104+
/** Offers the backends data for download */
105+
get: operations["ExportController_get"];
106+
put?: never;
107+
post?: never;
108+
delete?: never;
109+
options?: never;
110+
head?: never;
111+
patch?: never;
112+
trace?: never;
113+
};
95114
"/carers": {
96115
parameters: {
97116
query?: never;
@@ -1272,6 +1291,28 @@ export interface operations {
12721291
};
12731292
};
12741293
};
1294+
ExportController_get: {
1295+
parameters: {
1296+
query?: never;
1297+
header?: never;
1298+
path?: never;
1299+
cookie?: never;
1300+
};
1301+
requestBody?: never;
1302+
responses: {
1303+
/** @description Database dump file */
1304+
200: {
1305+
headers: {
1306+
/** @description Attachment dump.sql */
1307+
"Content-Disposition"?: string;
1308+
[name: string]: unknown;
1309+
};
1310+
content: {
1311+
"text/sql": string;
1312+
};
1313+
};
1314+
};
1315+
};
12751316
CarersController_index: {
12761317
parameters: {
12771318
query?: never;

apps/frontend/src/routeTree.gen.ts

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { Route as AuthAdministrationStudiesIndexImport } from "./routes/_auth/ad
3131
import { Route as AuthAdministrationQuestionnairesIndexImport } from "./routes/_auth/administration/questionnaires/index";
3232
import { Route as AuthAdministrationParticipantsIndexImport } from "./routes/_auth/administration/participants/index";
3333
import { Route as AuthAdministrationLanguagesIndexImport } from "./routes/_auth/administration/languages/index";
34+
import { Route as AuthAdministrationExportIndexImport } from "./routes/_auth/administration/export/index";
3435
import { Route as AuthAdministrationCarersIndexImport } from "./routes/_auth/administration/carers/index";
3536
import { Route as AuthQuestionnaireQuestionnaireParticipantImport } from "./routes/_auth/questionnaire/_questionnaire/participant";
3637
import { Route as AuthQuestionnaireQuestionnaireNewImport } from "./routes/_auth/questionnaire/_questionnaire/new";
@@ -179,6 +180,13 @@ const AuthAdministrationLanguagesIndexRoute =
179180
getParentRoute: () => AuthAdministrationLanguagesRoute,
180181
} as any);
181182

183+
const AuthAdministrationExportIndexRoute =
184+
AuthAdministrationExportIndexImport.update({
185+
id: "/",
186+
path: "/",
187+
getParentRoute: () => AuthAdministrationExportRoute,
188+
} as any);
189+
182190
const AuthAdministrationCarersIndexRoute =
183191
AuthAdministrationCarersIndexImport.update({
184192
id: "/",
@@ -471,6 +479,13 @@ declare module "@tanstack/react-router" {
471479
preLoaderRoute: typeof AuthAdministrationCarersIndexImport;
472480
parentRoute: typeof AuthAdministrationCarersImport;
473481
};
482+
"/_auth/administration/export/": {
483+
id: "/_auth/administration/export/";
484+
path: "/";
485+
fullPath: "/administration/export/";
486+
preLoaderRoute: typeof AuthAdministrationExportIndexImport;
487+
parentRoute: typeof AuthAdministrationExportImport;
488+
};
474489
"/_auth/administration/languages/": {
475490
id: "/_auth/administration/languages/";
476491
path: "/";
@@ -599,6 +614,20 @@ const AuthAdministrationCarersRouteWithChildren =
599614
AuthAdministrationCarersRouteChildren,
600615
);
601616

617+
interface AuthAdministrationExportRouteChildren {
618+
AuthAdministrationExportIndexRoute: typeof AuthAdministrationExportIndexRoute;
619+
}
620+
621+
const AuthAdministrationExportRouteChildren: AuthAdministrationExportRouteChildren =
622+
{
623+
AuthAdministrationExportIndexRoute: AuthAdministrationExportIndexRoute,
624+
};
625+
626+
const AuthAdministrationExportRouteWithChildren =
627+
AuthAdministrationExportRoute._addFileChildren(
628+
AuthAdministrationExportRouteChildren,
629+
);
630+
602631
interface AuthAdministrationLanguagesRouteChildren {
603632
AuthAdministrationLanguagesNewRoute: typeof AuthAdministrationLanguagesNewRoute;
604633
AuthAdministrationLanguagesIndexRoute: typeof AuthAdministrationLanguagesIndexRoute;
@@ -696,7 +725,7 @@ const AuthAdministrationUsersRouteWithChildren =
696725

697726
interface AuthAdministrationRouteChildren {
698727
AuthAdministrationCarersRoute: typeof AuthAdministrationCarersRouteWithChildren;
699-
AuthAdministrationExportRoute: typeof AuthAdministrationExportRoute;
728+
AuthAdministrationExportRoute: typeof AuthAdministrationExportRouteWithChildren;
700729
AuthAdministrationLanguagesRoute: typeof AuthAdministrationLanguagesRouteWithChildren;
701730
AuthAdministrationParticipantsRoute: typeof AuthAdministrationParticipantsRouteWithChildren;
702731
AuthAdministrationQuestionnairesRoute: typeof AuthAdministrationQuestionnairesRouteWithChildren;
@@ -707,7 +736,7 @@ interface AuthAdministrationRouteChildren {
707736

708737
const AuthAdministrationRouteChildren: AuthAdministrationRouteChildren = {
709738
AuthAdministrationCarersRoute: AuthAdministrationCarersRouteWithChildren,
710-
AuthAdministrationExportRoute: AuthAdministrationExportRoute,
739+
AuthAdministrationExportRoute: AuthAdministrationExportRouteWithChildren,
711740
AuthAdministrationLanguagesRoute:
712741
AuthAdministrationLanguagesRouteWithChildren,
713742
AuthAdministrationParticipantsRoute:
@@ -787,7 +816,7 @@ export interface FileRoutesByFullPath {
787816
"/questionnaire": typeof AuthQuestionnaireQuestionnaireRouteWithChildren;
788817
"/": typeof AuthIndexRoute;
789818
"/administration/carers": typeof AuthAdministrationCarersRouteWithChildren;
790-
"/administration/export": typeof AuthAdministrationExportRoute;
819+
"/administration/export": typeof AuthAdministrationExportRouteWithChildren;
791820
"/administration/languages": typeof AuthAdministrationLanguagesRouteWithChildren;
792821
"/administration/participants": typeof AuthAdministrationParticipantsRouteWithChildren;
793822
"/administration/questionnaires": typeof AuthAdministrationQuestionnairesRouteWithChildren;
@@ -803,6 +832,7 @@ export interface FileRoutesByFullPath {
803832
"/questionnaire/new": typeof AuthQuestionnaireQuestionnaireNewRoute;
804833
"/questionnaire/participant": typeof AuthQuestionnaireQuestionnaireParticipantRoute;
805834
"/administration/carers/": typeof AuthAdministrationCarersIndexRoute;
835+
"/administration/export/": typeof AuthAdministrationExportIndexRoute;
806836
"/administration/languages/": typeof AuthAdministrationLanguagesIndexRoute;
807837
"/administration/participants/": typeof AuthAdministrationParticipantsIndexRoute;
808838
"/administration/questionnaires/": typeof AuthAdministrationQuestionnairesIndexRoute;
@@ -823,7 +853,6 @@ export interface FileRoutesByFullPath {
823853
export interface FileRoutesByTo {
824854
"/session": typeof SessionRoute;
825855
"/": typeof AuthIndexRoute;
826-
"/administration/export": typeof AuthAdministrationExportRoute;
827856
"/questionnaire": typeof AuthQuestionnaireIndexRoute;
828857
"/administration": typeof AuthAdministrationIndexRoute;
829858
"/administration/carers/new": typeof AuthAdministrationCarersNewRoute;
@@ -834,6 +863,7 @@ export interface FileRoutesByTo {
834863
"/questionnaire/new": typeof AuthQuestionnaireQuestionnaireNewRoute;
835864
"/questionnaire/participant": typeof AuthQuestionnaireQuestionnaireParticipantRoute;
836865
"/administration/carers": typeof AuthAdministrationCarersIndexRoute;
866+
"/administration/export": typeof AuthAdministrationExportIndexRoute;
837867
"/administration/languages": typeof AuthAdministrationLanguagesIndexRoute;
838868
"/administration/participants": typeof AuthAdministrationParticipantsIndexRoute;
839869
"/administration/questionnaires": typeof AuthAdministrationQuestionnairesIndexRoute;
@@ -859,7 +889,7 @@ export interface FileRoutesById {
859889
"/_auth/questionnaire": typeof AuthQuestionnaireRouteWithChildren;
860890
"/_auth/": typeof AuthIndexRoute;
861891
"/_auth/administration/carers": typeof AuthAdministrationCarersRouteWithChildren;
862-
"/_auth/administration/export": typeof AuthAdministrationExportRoute;
892+
"/_auth/administration/export": typeof AuthAdministrationExportRouteWithChildren;
863893
"/_auth/administration/languages": typeof AuthAdministrationLanguagesRouteWithChildren;
864894
"/_auth/administration/participants": typeof AuthAdministrationParticipantsRouteWithChildren;
865895
"/_auth/administration/questionnaires": typeof AuthAdministrationQuestionnairesRouteWithChildren;
@@ -876,6 +906,7 @@ export interface FileRoutesById {
876906
"/_auth/questionnaire/_questionnaire/new": typeof AuthQuestionnaireQuestionnaireNewRoute;
877907
"/_auth/questionnaire/_questionnaire/participant": typeof AuthQuestionnaireQuestionnaireParticipantRoute;
878908
"/_auth/administration/carers/": typeof AuthAdministrationCarersIndexRoute;
909+
"/_auth/administration/export/": typeof AuthAdministrationExportIndexRoute;
879910
"/_auth/administration/languages/": typeof AuthAdministrationLanguagesIndexRoute;
880911
"/_auth/administration/participants/": typeof AuthAdministrationParticipantsIndexRoute;
881912
"/_auth/administration/questionnaires/": typeof AuthAdministrationQuestionnairesIndexRoute;
@@ -918,6 +949,7 @@ export interface FileRouteTypes {
918949
| "/questionnaire/new"
919950
| "/questionnaire/participant"
920951
| "/administration/carers/"
952+
| "/administration/export/"
921953
| "/administration/languages/"
922954
| "/administration/participants/"
923955
| "/administration/questionnaires/"
@@ -937,7 +969,6 @@ export interface FileRouteTypes {
937969
to:
938970
| "/session"
939971
| "/"
940-
| "/administration/export"
941972
| "/questionnaire"
942973
| "/administration"
943974
| "/administration/carers/new"
@@ -948,6 +979,7 @@ export interface FileRouteTypes {
948979
| "/questionnaire/new"
949980
| "/questionnaire/participant"
950981
| "/administration/carers"
982+
| "/administration/export"
951983
| "/administration/languages"
952984
| "/administration/participants"
953985
| "/administration/questionnaires"
@@ -988,6 +1020,7 @@ export interface FileRouteTypes {
9881020
| "/_auth/questionnaire/_questionnaire/new"
9891021
| "/_auth/questionnaire/_questionnaire/participant"
9901022
| "/_auth/administration/carers/"
1023+
| "/_auth/administration/export/"
9911024
| "/_auth/administration/languages/"
9921025
| "/_auth/administration/participants/"
9931026
| "/_auth/administration/questionnaires/"
@@ -1078,7 +1111,10 @@ export const routeTree = rootRoute
10781111
},
10791112
"/_auth/administration/export": {
10801113
"filePath": "_auth/administration/export.tsx",
1081-
"parent": "/_auth/administration"
1114+
"parent": "/_auth/administration",
1115+
"children": [
1116+
"/_auth/administration/export/"
1117+
]
10821118
},
10831119
"/_auth/administration/languages": {
10841120
"filePath": "_auth/administration/languages.tsx",
@@ -1176,6 +1212,10 @@ export const routeTree = rootRoute
11761212
"filePath": "_auth/administration/carers/index.tsx",
11771213
"parent": "/_auth/administration/carers"
11781214
},
1215+
"/_auth/administration/export/": {
1216+
"filePath": "_auth/administration/export/index.tsx",
1217+
"parent": "/_auth/administration/export"
1218+
},
11791219
"/_auth/administration/languages/": {
11801220
"filePath": "_auth/administration/languages/index.tsx",
11811221
"parent": "/_auth/administration/languages"

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { createFileRoute, Link } from "@tanstack/react-router";
22
import { $api } from "../../../../stores/api";
33
import { Button, Table } from "@quassel/ui";
4-
import { useSuspenseQuery } from "@tanstack/react-query";
54

65
function AdministrationCarersIndex() {
7-
const carers = useSuspenseQuery($api.queryOptions("get", "/carers"));
6+
const carers = $api.useSuspenseQuery("get", "/carers");
87
const deleteCarerMutation = $api.useMutation("delete", "/carers/{id}", {
98
onSuccess: () => carers.refetch(),
109
});
Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1-
import { createFileRoute } from "@tanstack/react-router";
1+
import { Paper, Title } from "@quassel/ui";
2+
import { createFileRoute, Outlet } from "@tanstack/react-router";
3+
4+
function AdministrationExport() {
5+
return (
6+
<>
7+
<Title>Export</Title>
8+
<Paper my="lg">
9+
<Outlet />
10+
</Paper>
11+
</>
12+
);
13+
}
214

315
export const Route = createFileRoute("/_auth/administration/export")({
4-
component: () => <div>Hello /_auth/administration/export!</div>,
16+
component: AdministrationExport,
517
});

0 commit comments

Comments
 (0)