summaryrefslogtreecommitdiff
path: root/apps/web/src/app/api
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-14 21:45:11 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-14 21:45:11 +0200
commit78a0a27a72e5e6c3d349abe2cb28ce6cd1e64d83 (patch)
tree1b21aa4493f79df1560e7d89aaad2d55ee6c33a9 /apps/web/src/app/api
parentb68bf004549e08ae81f99c9ece15fd7e7969c385 (diff)
downloadopencut-78a0a27a72e5e6c3d349abe2cb28ce6cd1e64d83.tar.gz
feat: editor feedback
Diffstat (limited to 'apps/web/src/app/api')
-rw-r--r--apps/web/src/app/api/feedback/route.ts31
1 files changed, 31 insertions, 0 deletions
diff --git a/apps/web/src/app/api/feedback/route.ts b/apps/web/src/app/api/feedback/route.ts
new file mode 100644
index 00000000..b13ec2bf
--- /dev/null
+++ b/apps/web/src/app/api/feedback/route.ts
@@ -0,0 +1,31 @@
+import { type NextRequest, NextResponse } from "next/server";
+import { z } from "zod";
+import { checkRateLimit } from "@/lib/rate-limit";
+import { submitFeedback, MAX_MESSAGE_LENGTH } from "@/lib/feedback";
+
+const submitSchema = z.object({
+ message: z
+ .string()
+ .min(1, "Message is required")
+ .max(MAX_MESSAGE_LENGTH, "Message too long"),
+});
+
+export async function POST(request: NextRequest) {
+ const { limited } = await checkRateLimit({ request });
+ if (limited) {
+ return NextResponse.json({ error: "Too many requests" }, { status: 429 });
+ }
+
+ const body = await request.json();
+ const result = submitSchema.safeParse(body);
+
+ if (!result.success) {
+ return NextResponse.json(
+ { error: "Invalid input", details: result.error.flatten().fieldErrors },
+ { status: 400 },
+ );
+ }
+
+ const entry = await submitFeedback(result.data);
+ return NextResponse.json({ entry }, { status: 201 });
+}