index.ts134 lines · main
1import { paths } from 'api-types'
2import { compact } from 'lodash'
3import { NextApiRequest, NextApiResponse } from 'next'
4import { z } from 'zod'
5
6import apiWrapper from '@/lib/api/apiWrapper'
7import {
8 deleteSnippet,
9 getSnippets,
10 saveSnippet,
11 SnippetSchema,
12 updateSnippet,
13} from '@/lib/api/snippets.utils'
14
15const wrappedHandler = (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
16
17async function handler(req: NextApiRequest, res: NextApiResponse) {
18 const { method } = req
19
20 switch (method) {
21 case 'GET':
22 return handleGetAll(req, res)
23 case 'PUT':
24 return handlePut(req, res)
25
26 case 'DELETE':
27 return handleDelete(req, res)
28 default:
29 res.setHeader('Allow', ['GET', 'PUT', 'DELETE'])
30 res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
31 }
32}
33
34type GetResponseData =
35 paths['/platform/projects/{ref}/content']['get']['responses']['200']['content']['application/json']
36
37const getRequestParamsSchema = z.object({
38 visibility: z.enum(['project', 'user']).optional(),
39 name: z.string().optional(),
40 limit: z
41 .string()
42 .transform((val) => Number(val))
43 .pipe(z.number().int().positive())
44 .optional(),
45 cursor: z.string().optional(),
46 sort_by: z.enum(['name', 'inserted_at']).optional(),
47 sort_order: z.enum(['asc', 'desc']).optional(),
48})
49
50const handleGetAll = async (req: NextApiRequest, res: NextApiResponse<GetResponseData>) => {
51 const result = getRequestParamsSchema.safeParse(req.query)
52
53 if (!result.success) {
54 return res.status(400).json({ data: [] })
55 }
56
57 const params = result.data
58
59 // Platform specific endpoint
60 if (params.visibility === 'project') {
61 return res.status(200).json({ data: [] })
62 }
63
64 try {
65 const { cursor, snippets } = await getSnippets({
66 searchTerm: params.name,
67 limit: params.limit,
68 cursor: params.cursor,
69 sort: params.sort_by,
70 sortOrder: params.sort_order,
71 })
72
73 return res.status(200).json({ data: snippets, cursor })
74 } catch (error) {
75 console.error('Error fetching snippets:', error)
76 return res.status(500).json({ data: [] })
77 }
78}
79
80const handlePut = async (req: NextApiRequest, res: NextApiResponse) => {
81 try {
82 const updates = req.body
83 const updatedSnippet = await updateSnippet(updates.id, updates)
84 return res.status(200).json(updatedSnippet)
85 } catch (error: any) {
86 if (error instanceof Error && error.message.includes('not found')) {
87 const body = req.body
88 const { data, error: parseError } = SnippetSchema.safeParse(body)
89
90 if (parseError) {
91 console.error('Validation error:', parseError)
92 return res.status(400).json({ error: parseError.message })
93 }
94
95 try {
96 const savedSnippet = await saveSnippet(data)
97 return res.status(200).json(savedSnippet)
98 } catch (error) {
99 console.error('Error creating snippet:', error)
100 return res.status(500).json({ error: 'Failed to create snippet' })
101 }
102 }
103 return res.status(500).json({ error: error?.message ?? 'Failed to update snippet' })
104 }
105}
106
107const snippetIdsSchema = z
108 .string()
109 .transform((val) => compact(val.split(',').map((id) => id.trim())))
110 .pipe(z.array(z.string().uuid()).nonempty())
111
112const handleDelete = async (req: NextApiRequest, res: NextApiResponse) => {
113 const { ids } = req.query
114
115 const result = snippetIdsSchema.safeParse(ids)
116 if (!result.success) {
117 return res.status(400).json({ error: 'Snippet IDs are required' })
118 }
119
120 const snippetIds = result.data
121
122 try {
123 for (const id of snippetIds) {
124 await deleteSnippet(id)
125 }
126 res.setHeader('Content-Type', 'application/json')
127 return res.status(200).send(snippetIds.map((id) => ({ id })))
128 } catch (error) {
129 console.error('Error deleting snippets:', error)
130 return res.status(500).json({ error: 'Failed to delete snippets' })
131 }
132}
133
134export default wrappedHandler