Openstatus
www.openstatus.dev
1import { TRPCError } from "@trpc/server";
2import { z } from "zod";
3import { env } from "../env";
4import { createTRPCRouter, protectedProcedure } from "../trpc";
5
6export const feedbackRouter = createTRPCRouter({
7 submit: protectedProcedure
8 .input(
9 z.object({
10 // NOTE: coming from NavFeedback
11 message: z.string().min(1, "Message required"),
12 path: z.string().optional(),
13 isMobile: z.boolean().optional(),
14 // NOTE: coming from ContactForm
15 name: z.string().optional(),
16 email: z.email().optional(),
17 blocker: z.boolean().optional(),
18 type: z.string().optional(),
19 }),
20 )
21 .mutation(async (opts) => {
22 if (!env.SLACK_FEEDBACK_WEBHOOK_URL) {
23 throw new TRPCError({
24 code: "INTERNAL_SERVER_ERROR",
25 message: "Slack feedback webhook not configured.",
26 });
27 }
28
29 const textLines: string[] = [];
30 if (opts.input.name) textLines.push(`*Name:* ${opts.input.name}`);
31 if (opts.input.email) textLines.push(`*Email:* ${opts.input.email}`);
32 if (opts.input.blocker)
33 textLines.push(`*Blocker:* ${opts.input.blocker}`);
34 if (opts.input.type) textLines.push(`*Type:* ${opts.input.type}`);
35 if (opts.ctx.user) textLines.push(`*User:* ${opts.ctx.user.email}`);
36 if (opts.input.path) textLines.push(`*Path:* ${opts.input.path}`);
37 if (opts.input.isMobile)
38 textLines.push(`*Mobile:* ${opts.input.isMobile}`);
39 if (opts.ctx.metadata?.userAgent)
40 textLines.push(`*User Agent:* ${opts.ctx.metadata.userAgent}`);
41 if (opts.ctx.metadata?.location)
42 textLines.push(`*Location:* ${opts.ctx.metadata.location}`);
43
44 textLines.push("--------------------------------");
45
46 textLines.push(`*Message:* ${opts.input.message}`);
47
48 await fetch(env.SLACK_FEEDBACK_WEBHOOK_URL, {
49 method: "POST",
50 headers: {
51 "Content-Type": "application/json",
52 },
53 body: JSON.stringify({
54 text: textLines.join("\n"),
55 }),
56 });
57
58 return { success: true } as const;
59 }),
60});