2024-10-24 12:45:21 -06:00

309 lines
8.0 KiB
TypeScript

// import type { DrizzleD1Database } from "drizzle-orm/d1";
import { drizzle } from "drizzle-orm/d1"
import { json } from "itty-router-extras"
import { z } from "zod"
import { and, eq, sql } from "drizzle-orm"
import * as schema from "./schema"
import { sandbox, user, usersToSandboxes } from "./schema"
export interface Env {
DB: D1Database
STORAGE: any
KEY: string
STORAGE_WORKER_URL: string
}
// https://github.com/drizzle-team/drizzle-orm/tree/main/examples/cloudflare-d1
// npm run generate
// npx wrangler d1 execute d1-sandbox --local --file=./drizzle/<FILE>
export default {
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 })
if (path === "/api/sandbox") {
if (method === "GET") {
const params = url.searchParams
if (params.has("id")) {
const id = params.get("id") as string
const res = await db.query.sandbox.findFirst({
where: (sandbox, { eq }) => eq(sandbox.id, id),
with: {
usersToSandboxes: true,
},
})
return json(res ?? {})
} else {
const res = await db.select().from(sandbox).all()
return json(res ?? {})
}
} else if (method === "DELETE") {
const params = url.searchParams
if (params.has("id")) {
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 {
return invalidRequest
}
} else if (method === "POST") {
const postSchema = z.object({
id: z.string(),
name: z.string().optional(),
visibility: z.enum(["public", "private"]).optional(),
})
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
} else if (method === "PUT") {
const initSchema = z.object({
type: z.string(),
name: z.string(),
userId: z.string(),
visibility: z.enum(["public", "private"]),
})
const body = await request.json()
const { type, name, userId, visibility } = initSchema.parse(body)
const userSandboxes = await db
.select()
.from(sandbox)
.where(eq(sandbox.userId, userId))
.all()
if (userSandboxes.length >= 8) {
return new Response("You reached the maximum # of sandboxes.", {
status: 400,
})
}
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}`,
},
}
)
await env.STORAGE.fetch(initStorageRequest)
return new Response(sb.id, { status: 200 })
} else {
return methodNotAllowed
}
} else if (path === "/api/sandbox/share") {
if (method === "GET") {
const params = url.searchParams
if (params.has("id")) {
const id = params.get("id") as string
const res = await db.query.usersToSandboxes.findMany({
where: (uts, { eq }) => eq(uts.userId, id),
})
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,
},
})
if (!sb) return
return {
id: sb.id,
name: sb.name,
type: sb.type,
author: sb.author.name,
sharedOn: r.sharedOn,
}
})
)
return json(owners ?? {})
} else return invalidRequest
} else if (method === "POST") {
const shareSchema = z.object({
sandboxId: z.string(),
email: z.string(),
})
const body = await request.json()
const { sandboxId, email } = shareSchema.parse(body)
const user = await db.query.user.findFirst({
where: (user, { eq }) => eq(user.email, email),
with: {
sandbox: true,
usersToSandboxes: true,
},
})
if (!user) {
return new Response("No user associated with email.", { status: 400 })
}
if (user.sandbox.find((sb) => sb.id === sandboxId)) {
return new Response("Cannot share with yourself!", { status: 400 })
}
if (user.usersToSandboxes.find((uts) => uts.sandboxId === sandboxId)) {
return new Response("User already has access.", { status: 400 })
}
await db
.insert(usersToSandboxes)
.values({ userId: user.id, sandboxId, sharedOn: new Date() })
.get()
return success
} else if (method === "DELETE") {
const deleteShareSchema = z.object({
sandboxId: z.string(),
userId: z.string(),
})
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(),
})
const body = await request.json()
const { userId } = generateSchema.parse(body)
const dbUser = await db.query.user.findFirst({
where: (user, { eq }) => eq(user.id, userId),
})
if (!dbUser) {
return new Response("User not found.", { status: 400 })
}
if (dbUser.generations !== null && dbUser.generations >= 10) {
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))
.get()
return success
} else if (path === "/api/user") {
if (method === "GET") {
const params = url.searchParams
if (params.has("id")) {
const id = params.get("id") as string
const res = await db.query.user.findFirst({
where: (user, { eq }) => eq(user.id, id),
with: {
sandbox: {
orderBy: (sandbox, { desc }) => [desc(sandbox.createdAt)],
},
usersToSandboxes: true,
},
})
return json(res ?? {})
} else {
const res = await db.select().from(user).all()
return json(res ?? {})
}
} else if (method === "POST") {
const userSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
})
const body = await request.json()
const { id, name, email } = userSchema.parse(body)
const res = await db
.insert(user)
.values({ id, name, email })
.returning()
.get()
return json({ res })
} else if (method === "DELETE") {
const params = url.searchParams
if (params.has("id")) {
const id = params.get("id") as string
await db.delete(user).where(eq(user.id, id))
return success
} else return invalidRequest
} else {
return methodNotAllowed
}
} else return notFound
},
}