forked from jordanlambrecht/tracker-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroute.ts
More file actions
82 lines (69 loc) · 2.83 KB
/
Copy pathroute.ts
File metadata and controls
82 lines (69 loc) · 2.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// src/app/api/notifications/route.ts
//
// Functions: GET, POST
import { NextResponse } from "next/server"
import { authenticate, decodeKey, parseJsonBody, validateMaxLength } from "@/lib/api-helpers"
import { encrypt } from "@/lib/crypto"
import { db } from "@/lib/db"
import { notificationTargets } from "@/lib/db/schema"
import { errMsg } from "@/lib/error-utils"
import { SHORT_NAME_MAX } from "@/lib/limits"
import { log } from "@/lib/logger"
import { SUPPORTED_NOTIFICATION_TYPES } from "@/lib/notifications/types"
import { validateNotificationConfig } from "@/lib/notifications/validate"
import { fetchNotificationTargets, serializeNotificationTarget } from "@/lib/server-data"
export async function GET() {
const auth = await authenticate()
if (auth instanceof NextResponse) return auth
const targets = await fetchNotificationTargets()
return NextResponse.json(targets.map(serializeNotificationTarget))
}
export async function POST(req: Request) {
const auth = await authenticate()
if (auth instanceof NextResponse) return auth
const body = await parseJsonBody(req)
if (body instanceof NextResponse) return body
const { name, type, config } = body as { name?: unknown; type?: unknown; config?: unknown }
if (typeof name !== "string" || !name.trim())
return NextResponse.json({ error: "name is required" }, { status: 400 })
const nameErr = validateMaxLength(name, SHORT_NAME_MAX, "name")
if (nameErr) return nameErr
if (
typeof type !== "string" ||
!SUPPORTED_NOTIFICATION_TYPES.includes(type as (typeof SUPPORTED_NOTIFICATION_TYPES)[number])
)
return NextResponse.json(
{ error: `type must be one of: ${SUPPORTED_NOTIFICATION_TYPES.join(", ")}` },
{ status: 400 }
)
if (!config || typeof config !== "object")
return NextResponse.json({ error: "config object is required" }, { status: 400 })
const validationError = validateNotificationConfig(
type as (typeof SUPPORTED_NOTIFICATION_TYPES)[number],
config as Record<string, unknown>
)
if (validationError) return NextResponse.json({ error: validationError }, { status: 400 })
try {
const key = decodeKey(auth)
const encryptedConfig = encrypt(JSON.stringify(config), key)
const [inserted] = await db
.insert(notificationTargets)
.values({
name: name.trim(),
type,
encryptedConfig,
})
.returning({ id: notificationTargets.id, name: notificationTargets.name })
log.info(
{ route: "POST /api/notifications", targetId: inserted.id },
"notification target created"
)
return NextResponse.json(inserted, { status: 201 })
} catch (err) {
log.error(
{ route: "POST /api/notifications", error: errMsg(err) },
"Failed to create notification target"
)
return NextResponse.json({ error: "Failed to create notification target" }, { status: 500 })
}
}