304 lines
8.0 KiB
TypeScript
Raw Normal View History

2024-05-25 01:16:25 -07:00
// import type { DrizzleD1Database } from "drizzle-orm/d1";
2024-05-26 18:37:36 -07:00
import { drizzle } from "drizzle-orm/d1"
import { json } from "itty-router-extras"
import { ZodError, z } from "zod"
2024-04-16 19:06:23 -04:00
2024-05-26 18:37:36 -07:00
import { user, sandbox, usersToSandboxes } from "./schema"
import * as schema from "./schema"
import { and, eq, sql } from "drizzle-orm"
2024-04-22 00:30:50 -04:00
2024-04-16 18:19:34 -04:00
export interface Env {
2024-05-26 18:37:36 -07:00
DB: D1Database
STORAGE: any
KEY: string
STORAGE_WORKER_URL: string
2024-04-16 18:19:34 -04:00
}
2024-04-21 22:55:49 -04:00
// https://github.com/drizzle-team/drizzle-orm/tree/main/examples/cloudflare-d1
2024-04-27 16:22:35 -04:00
// npm run generate
// npx wrangler d1 execute d1-sandbox --local --file=./drizzle/<FILE>
2024-04-16 18:19:34 -04:00
export default {
2024-05-26 18:37:36 -07:00
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
const success = new Response("Success", { status: 200 })
const invalidRequest = new Response("Invalid Request", { status: 400 })
const notFound = new Response("Not Found", { status: 404 })
const methodNotAllowed = new Response("Method Not Allowed", { status: 405 })
const url = new URL(request.url)
const path = url.pathname
const method = request.method
if (request.headers.get("Authorization") !== env.KEY) {
return new Response("Unauthorized", { status: 401 })
}
const db = drizzle(env.DB, { schema })
2024-04-17 21:24:57 -04:00
2024-05-26 18:45:28 -07:00
if (path === "/api/sandbox") {
2024-04-27 16:22:35 -04:00
if (method === "GET") {
2024-05-26 18:37:36 -07:00
const params = url.searchParams
2024-04-27 16:22:35 -04:00
if (params.has("id")) {
2024-05-26 18:37:36 -07:00
const id = params.get("id") as string
2024-04-27 16:22:35 -04:00
const res = await db.query.sandbox.findFirst({
where: (sandbox, { eq }) => eq(sandbox.id, id),
2024-05-01 01:53:49 -04:00
with: {
usersToSandboxes: true,
},
2024-05-26 18:37:36 -07:00
})
return json(res ?? {})
2024-04-27 16:22:35 -04:00
} else {
2024-05-26 18:37:36 -07:00
const res = await db.select().from(sandbox).all()
return json(res ?? {})
2024-04-27 16:22:35 -04:00
}
} else if (method === "DELETE") {
2024-05-26 18:37:36 -07:00
const params = url.searchParams
if (params.has("id")) {
2024-05-26 18:37:36 -07:00
const id = params.get("id") as string
await db
.delete(usersToSandboxes)
.where(eq(usersToSandboxes.sandboxId, id))
await db.delete(sandbox).where(eq(sandbox.id, id))
const deleteStorageRequest = new Request(
`${env.STORAGE_WORKER_URL}/api/project`,
{
method: "DELETE",
body: JSON.stringify({ sandboxId: id }),
headers: {
"Content-Type": "application/json",
Authorization: `${env.KEY}`,
},
}
)
await env.STORAGE.fetch(deleteStorageRequest)
return success
} else {
2024-05-26 18:37:36 -07:00
return invalidRequest
}
} else if (method === "POST") {
2024-05-25 01:16:25 -07:00
const postSchema = z.object({
id: z.string(),
name: z.string().optional(),
visibility: z.enum(["public", "private"]).optional(),
2024-05-26 18:37:36 -07:00
})
const body = await request.json()
const { id, name, visibility } = postSchema.parse(body)
const sb = await db
.update(sandbox)
.set({ name, visibility })
.where(eq(sandbox.id, id))
.returning()
.get()
return success
2024-04-27 16:22:35 -04:00
} else if (method === "PUT") {
const initSchema = z.object({
type: z.enum(["react", "node"]),
name: z.string(),
userId: z.string(),
visibility: z.enum(["public", "private"]),
2024-05-26 18:37:36 -07:00
})
2024-04-22 00:30:50 -04:00
2024-05-26 18:37:36 -07:00
const body = await request.json()
const { type, name, userId, visibility } = initSchema.parse(body)
2024-05-26 18:37:36 -07:00
const allSandboxes = await db.select().from(sandbox).all()
2024-05-05 12:55:34 -07:00
if (allSandboxes.length >= 8) {
2024-05-26 18:37:36 -07:00
return new Response("You reached the maximum # of sandboxes.", {
status: 400,
})
2024-05-05 12:55:34 -07:00
}
2024-05-26 18:37:36 -07:00
const sb = await db
.insert(sandbox)
.values({ type, name, userId, visibility, createdAt: new Date() })
.returning()
.get()
const initStorageRequest = new Request(
`${env.STORAGE_WORKER_URL}/api/init`,
{
method: "POST",
body: JSON.stringify({ sandboxId: sb.id, type }),
headers: {
"Content-Type": "application/json",
Authorization: `${env.KEY}`,
},
}
)
2024-05-26 18:37:36 -07:00
await env.STORAGE.fetch(initStorageRequest)
2024-04-27 16:22:35 -04:00
2024-05-26 18:37:36 -07:00
return new Response(sb.id, { status: 200 })
2024-04-27 00:28:00 -04:00
} else {
2024-05-26 18:37:36 -07:00
return methodNotAllowed
2024-04-27 00:28:00 -04:00
}
2024-05-01 02:22:02 -04:00
} else if (path === "/api/sandbox/share") {
2024-05-01 02:49:25 -04:00
if (method === "GET") {
2024-05-26 18:37:36 -07:00
const params = url.searchParams
2024-05-01 02:49:25 -04:00
if (params.has("id")) {
2024-05-26 18:37:36 -07:00
const id = params.get("id") as string
2024-05-01 02:49:25 -04:00
const res = await db.query.usersToSandboxes.findMany({
where: (uts, { eq }) => eq(uts.userId, id),
2024-05-26 18:37:36 -07:00
})
2024-05-01 02:49:25 -04:00
const owners = await Promise.all(
res.map(async (r) => {
const sb = await db.query.sandbox.findFirst({
where: (sandbox, { eq }) => eq(sandbox.id, r.sandboxId),
with: {
author: true,
},
2024-05-26 18:37:36 -07:00
})
if (!sb) return
return {
id: sb.id,
name: sb.name,
type: sb.type,
author: sb.author.name,
sharedOn: r.sharedOn,
}
2024-05-01 02:49:25 -04:00
})
2024-05-26 18:37:36 -07:00
)
2024-05-01 02:49:25 -04:00
2024-05-26 18:37:36 -07:00
return json(owners ?? {})
} else return invalidRequest
2024-05-01 02:49:25 -04:00
} else if (method === "POST") {
2024-05-01 02:22:02 -04:00
const shareSchema = z.object({
sandboxId: z.string(),
email: z.string(),
2024-05-26 18:37:36 -07:00
})
2024-05-01 01:53:49 -04:00
2024-05-26 18:37:36 -07:00
const body = await request.json()
const { sandboxId, email } = shareSchema.parse(body)
2024-05-01 02:22:02 -04:00
const user = await db.query.user.findFirst({
where: (user, { eq }) => eq(user.email, email),
with: {
2024-05-01 08:20:08 -04:00
sandbox: true,
2024-05-01 02:22:02 -04:00
usersToSandboxes: true,
},
2024-05-26 18:37:36 -07:00
})
2024-05-01 02:22:02 -04:00
if (!user) {
2024-05-26 18:37:36 -07:00
return new Response("No user associated with email.", { status: 400 })
2024-05-01 02:22:02 -04:00
}
2024-05-01 08:20:08 -04:00
if (user.sandbox.find((sb) => sb.id === sandboxId)) {
2024-05-26 18:37:36 -07:00
return new Response("Cannot share with yourself!", { status: 400 })
2024-05-01 08:20:08 -04:00
}
2024-05-01 02:22:02 -04:00
if (user.usersToSandboxes.find((uts) => uts.sandboxId === sandboxId)) {
2024-05-26 18:37:36 -07:00
return new Response("User already has access.", { status: 400 })
2024-05-01 02:22:02 -04:00
}
2024-05-01 01:29:16 -04:00
2024-05-26 18:37:36 -07:00
await db
.insert(usersToSandboxes)
.values({ userId: user.id, sandboxId, sharedOn: new Date() })
.get()
2024-05-01 01:29:16 -04:00
2024-05-26 18:37:36 -07:00
return success
2024-05-01 02:22:02 -04:00
} else if (method === "DELETE") {
const deleteShareSchema = z.object({
sandboxId: z.string(),
userId: z.string(),
2024-05-26 18:37:36 -07:00
})
const body = await request.json()
const { sandboxId, userId } = deleteShareSchema.parse(body)
await db
.delete(usersToSandboxes)
.where(
and(
eq(usersToSandboxes.userId, userId),
eq(usersToSandboxes.sandboxId, sandboxId)
)
)
return success
} else return methodNotAllowed
} else if (path === "/api/sandbox/generate" && method === "POST") {
const generateSchema = z.object({
userId: z.string(),
2024-05-26 18:37:36 -07:00
})
const body = await request.json()
const { userId } = generateSchema.parse(body)
const dbUser = await db.query.user.findFirst({
where: (user, { eq }) => eq(user.id, userId),
2024-05-26 18:37:36 -07:00
})
if (!dbUser) {
2024-05-26 18:37:36 -07:00
return new Response("User not found.", { status: 400 })
}
2024-05-17 22:25:55 -07:00
if (dbUser.generations !== null && dbUser.generations >= 10) {
2024-05-26 18:37:36 -07:00
return new Response("You reached the maximum # of generations.", {
status: 400,
})
}
await db
.update(user)
.set({ generations: sql`${user.generations} + 1` })
.where(eq(user.id, userId))
2024-05-26 18:37:36 -07:00
.get()
2024-05-26 18:37:36 -07:00
return success
} else if (path === "/api/user") {
2024-04-18 15:25:20 -04:00
if (method === "GET") {
2024-05-26 18:37:36 -07:00
const params = url.searchParams
2024-04-18 15:25:20 -04:00
if (params.has("id")) {
2024-05-26 18:37:36 -07:00
const id = params.get("id") as string
const res = await db.query.user.findFirst({
where: (user, { eq }) => eq(user.id, id),
with: {
2024-05-25 20:13:31 -07:00
sandbox: {
orderBy: (sandbox, { desc }) => [desc(sandbox.createdAt)],
},
2024-05-01 01:29:16 -04:00
usersToSandboxes: true,
},
2024-05-26 18:37:36 -07:00
})
return json(res ?? {})
2024-04-17 22:55:02 -04:00
} else {
2024-05-26 18:37:36 -07:00
const res = await db.select().from(user).all()
return json(res ?? {})
2024-04-17 22:55:02 -04:00
}
2024-04-18 15:25:20 -04:00
} else if (method === "POST") {
const userSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
2024-05-26 18:37:36 -07:00
})
2024-04-18 15:25:20 -04:00
2024-05-26 18:37:36 -07:00
const body = await request.json()
const { id, name, email } = userSchema.parse(body)
2024-04-18 15:25:20 -04:00
2024-05-26 18:37:36 -07:00
const res = await db
.insert(user)
.values({ id, name, email })
.returning()
.get()
return json({ res })
2024-05-22 19:35:19 -07:00
} else if (method === "DELETE") {
2024-05-26 18:37:36 -07:00
const params = url.searchParams
2024-05-22 19:35:19 -07:00
if (params.has("id")) {
2024-05-26 18:37:36 -07:00
const id = params.get("id") as string
await db.delete(user).where(eq(user.id, id))
return success
} else return invalidRequest
2024-04-17 21:24:57 -04:00
} else {
2024-05-26 18:37:36 -07:00
return methodNotAllowed
2024-04-17 21:24:57 -04:00
}
2024-05-26 18:37:36 -07:00
} else return notFound
2024-04-17 21:24:57 -04:00
},
2024-05-26 18:37:36 -07:00
}