1
13import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Query, Req } from "@nestjs/common";
14
15import type { Request } from "express";
16import { z } from "zod";
17import { SubmissionsService } from "./submissions.service.js";
18import { PublicFormsService } from "./public-forms.service.js";
19import { ZodValidate } from "../../common/pipes/zod-validation.pipe.js";
20import { requireUser } from "../../common/types.js";
21import { ApiError } from "../../common/errors.js";
22
23
24const SubmissionsQueryDto = z
25 .object({
26 cursor: z.string().min(1).max(120).optional(),
27 limit: z.coerce.number().int().min(1).max(100).optional(),
28 q: z.string().min(1).max(200).optional(),
29 from: z.string().datetime().optional(),
30 to: z.string().datetime().optional(),
31 anonymousOnly: z
32 .enum(["true", "false"])
33 .optional()
34 .transform((v) => v === "true")
35 })
36 .strict();
37
38
39const PublicUploadDto = z
40 .object({
41 name: z.string().min(1).max(255),
42 size: z.number().int().min(1).max(1024 ** 3 * 25),
43 mimeType: z.string().min(1).max(150)
44 })
45 .strict();
46type PublicUploadInput = z.infer<typeof PublicUploadDto>;
47
48@Controller()
49export class SubmissionsController {
50 constructor(
51 @Inject(SubmissionsService) private readonly submissions: SubmissionsService,
52 @Inject(PublicFormsService) private readonly publicForms: PublicFormsService
53 ) {}
54
55
56
57 @Get("forms/:formId/submissions")
58 list(
59 @Param("formId") formId: string,
60 @Query() query: unknown,
61 @Req() req: Request
62 ) {
63 const parsed = SubmissionsQueryDto.safeParse(query ?? {});
64 if (!parsed.success) {
65 throw ApiError.validation("Invalid list query.", [
66 { field: "query", issue: parsed.error.issues[0]?.message ?? "invalid" }
67 ]);
68 }
69 return this.submissions.list(formId, requireUser(req), parsed.data);
70 }
71
72 @Delete("forms/:formId/submissions/:submissionId")
73 delete(
74 @Param("formId") formId: string,
75 @Param("submissionId") submissionId: string,
76 @Query("confirm") confirm: string | undefined,
77 @Req() req: Request
78 ) {
79 return this.submissions.delete(formId, submissionId, requireUser(req), confirm === "true", req);
80 }
81
82 @Get("forms/:formId/export")
83 export(
84 @Param("formId") formId: string,
85 @Query("format") format: string | undefined,
86 @Req() req: Request
87 ) {
88 return this.submissions.export(formId, requireUser(req), format ?? "csv");
89 }
90
91
92
93 @Post("public/forms/:slug/files")
94 openPublicUpload(
95 @Param("slug") slug: string,
96 @Body(ZodValidate(PublicUploadDto)) body: PublicUploadInput,
97 @Req() req: Request
98 ) {
99 return this.publicForms.openPublicUpload(slug, body, req);
100 }
101
102 @Put("public/forms/:slug/files/:fileId/complete")
103 completePublicUpload(
104 @Param("slug") slug: string,
105 @Param("fileId") fileId: string,
106 @Req() req: Request
107 ) {
108 return this.publicForms.completePublicUpload(slug, fileId, req);
109 }
110}
111