From 6376493ae731b7eb949ddb707877d16bac03aa77 Mon Sep 17 00:00:00 2001 From: Ishaan Dey Date: Sun, 26 May 2024 18:37:36 -0700 Subject: [PATCH] dynamic worker routes based on env --- ...ler.toml.example => wrangler.example.toml} | 0 backend/database/src/index.ts | 301 +++++++++++------- ...ler.toml.example => wrangler.example.toml} | 4 + backend/server/.env.example | 3 +- backend/server/src/index.ts | 12 +- backend/server/src/utils.ts | 40 ++- backend/storage/src/index.ts | 173 +++++----- ...ler.toml.example => wrangler.example.toml} | 3 + frontend/.env.example | 4 + frontend/app/(app)/code/[id]/page.tsx | 21 +- frontend/app/(app)/dashboard/page.tsx | 14 +- frontend/app/(app)/layout.tsx | 30 +- frontend/app/api/lb-auth/route.ts | 7 +- frontend/components/editor/generate.tsx | 4 +- frontend/components/editor/loading/index.tsx | 3 +- frontend/components/editor/navbar/share.tsx | 29 +- frontend/lib/actions.ts | 39 ++- 17 files changed, 427 insertions(+), 260 deletions(-) rename backend/ai/{wrangler.toml.example => wrangler.example.toml} (100%) rename backend/database/{wrangler.toml.example => wrangler.example.toml} (87%) rename backend/storage/{wrangler.toml.example => wrangler.example.toml} (94%) diff --git a/backend/ai/wrangler.toml.example b/backend/ai/wrangler.example.toml similarity index 100% rename from backend/ai/wrangler.toml.example rename to backend/ai/wrangler.example.toml diff --git a/backend/database/src/index.ts b/backend/database/src/index.ts index 9831589..b6b4271 100644 --- a/backend/database/src/index.ts +++ b/backend/database/src/index.ts @@ -1,16 +1,18 @@ // import type { DrizzleD1Database } from "drizzle-orm/d1"; -import { drizzle } from "drizzle-orm/d1"; -import { json } from "itty-router-extras"; -import { ZodError, z } from "zod"; +import { drizzle } from "drizzle-orm/d1" +import { json } from "itty-router-extras" +import { ZodError, z } from "zod" -import { user, sandbox, usersToSandboxes } from "./schema"; -import * as schema from "./schema"; -import { and, eq, sql } from "drizzle-orm"; +import { user, sandbox, usersToSandboxes } from "./schema" +import * as schema from "./schema" +import { and, eq, sql } from "drizzle-orm" export interface Env { - DB: D1Database; - STORAGE: any; - KV: KVNamespace; + DB: D1Database + STORAGE: any + KV: KVNamespace + KEY: string + STORAGE_WORKER_URL: string } // https://github.com/drizzle-team/drizzle-orm/tree/main/examples/cloudflare-d1 @@ -19,130 +21,163 @@ export interface Env { // npx wrangler d1 execute d1-sandbox --local --file=./drizzle/ export default { - async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { - 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 }); + async fetch( + request: Request, + env: Env, + ctx: ExecutionContext + ): Promise { + 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; + const url = new URL(request.url) + const path = url.pathname + const method = request.method - const db = drizzle(env.DB, { schema }); + if (request.headers.get("Authorization") !== env.KEY) { + return new Response("Unauthorized", { status: 401 }) + } + + const db = drizzle(env.DB, { schema }) if (path === "/api/session") { if (method === "PUT") { - const body = await request.json(); + const body = await request.json() const schema = z.object({ userId: z.string(), sandboxId: z.string(), - }); + }) - const { userId, sandboxId } = schema.parse(body); + const { userId, sandboxId } = schema.parse(body) - await env.KV.put(userId, sandboxId); + await env.KV.put(userId, sandboxId) - return success; + return success } else if (method === "GET") { - const params = url.searchParams; + const params = url.searchParams if (params.has("id")) { - const id = params.get("id") as string; - const sandboxId = await env.KV.get(id); - return json({ sandboxId }); - } else return invalidRequest; + const id = params.get("id") as string + const sandboxId = await env.KV.get(id) + return json({ sandboxId }) + } else return invalidRequest } else if (method === "DELETE") { - const params = url.searchParams; + const params = url.searchParams if (params.has("id")) { - const id = params.get("id") as string; - await env.KV.delete(id); - return success; - } else return invalidRequest; - } else return methodNotAllowed; + const id = params.get("id") as string + await env.KV.delete(id) + return success + } else return invalidRequest + } else return methodNotAllowed } else if (path === "/api/sandbox") { if (method === "GET") { - const params = url.searchParams; + const params = url.searchParams if (params.has("id")) { - const id = params.get("id") as string; + 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 ?? {}); + }) + return json(res ?? {}) } else { - const res = await db.select().from(sandbox).all(); - return json(res ?? {}); + const res = await db.select().from(sandbox).all() + return json(res ?? {}) } } else if (method === "DELETE") { - const params = url.searchParams; + 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 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("https://storage.ishaan1013.workers.dev/api/project", { - method: "DELETE", - body: JSON.stringify({ sandboxId: id }), - headers: { "Content-Type": "application/json" }, - }); - const deleteStorageRes = await env.STORAGE.fetch(deleteStorageRequest); + 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; + return success } else { - return invalidRequest; + 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(); + 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; + return success } else if (method === "PUT") { const initSchema = z.object({ type: z.enum(["react", "node"]), 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 body = await request.json() + const { type, name, userId, visibility } = initSchema.parse(body) - const allSandboxes = await db.select().from(sandbox).all(); + const allSandboxes = await db.select().from(sandbox).all() if (allSandboxes.length >= 8) { - return new Response("You reached the maximum # of sandboxes.", { status: 400 }); + 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 sb = await db + .insert(sandbox) + .values({ type, name, userId, visibility, createdAt: new Date() }) + .returning() + .get() - const initStorageRequest = new Request("https://storage.ishaan1013.workers.dev/api/init", { - method: "POST", - body: JSON.stringify({ sandboxId: sb.id, type }), - headers: { "Content-Type": "application/json" }, - }); + 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); + await env.STORAGE.fetch(initStorageRequest) - return new Response(sb.id, { status: 200 }); + return new Response(sb.id, { status: 200 }) } else { - return methodNotAllowed; + return methodNotAllowed } } else if (path === "/api/sandbox/share") { if (method === "GET") { - const params = url.searchParams; + const params = url.searchParams if (params.has("id")) { - const id = params.get("id") as string; + 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) => { @@ -151,22 +186,28 @@ export default { with: { author: true, }, - }); - if (!sb) return; - return { id: sb.id, name: sb.name, type: sb.type, author: sb.author.name, sharedOn: r.sharedOn }; + }) + 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; + 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 body = await request.json() + const { sandboxId, email } = shareSchema.parse(body) const user = await db.query.user.findFirst({ where: (user, { eq }) => eq(user.email, email), @@ -174,66 +215,78 @@ export default { sandbox: true, usersToSandboxes: true, }, - }); + }) if (!user) { - return new Response("No user associated with email.", { status: 400 }); + 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 }); + 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 }); + return new Response("User already has access.", { status: 400 }) } - await db.insert(usersToSandboxes).values({ userId: user.id, sandboxId, sharedOn: new Date() }).get(); + await db + .insert(usersToSandboxes) + .values({ userId: user.id, sandboxId, sharedOn: new Date() }) + .get() - return success; + 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); + 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))); + await db + .delete(usersToSandboxes) + .where( + and( + eq(usersToSandboxes.userId, userId), + eq(usersToSandboxes.sandboxId, sandboxId) + ) + ) - return success; - } else return methodNotAllowed; + 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 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 }); + 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 }); + 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(); + .get() - return success; + return success } else if (path === "/api/user") { if (method === "GET") { - const params = url.searchParams; + const params = url.searchParams if (params.has("id")) { - const id = params.get("id") as string; + const id = params.get("id") as string const res = await db.query.user.findFirst({ where: (user, { eq }) => eq(user.id, id), with: { @@ -242,34 +295,38 @@ export default { }, usersToSandboxes: true, }, - }); - return json(res ?? {}); + }) + return json(res ?? {}) } else { - const res = await db.select().from(user).all(); - return json(res ?? {}); + 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 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 }); + const res = await db + .insert(user) + .values({ id, name, email }) + .returning() + .get() + return json({ res }) } else if (method === "DELETE") { - const params = url.searchParams; + 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; + const id = params.get("id") as string + await db.delete(user).where(eq(user.id, id)) + return success + } else return invalidRequest } else { - return methodNotAllowed; + return methodNotAllowed } - } else return notFound; + } else return notFound }, -}; +} diff --git a/backend/database/wrangler.toml.example b/backend/database/wrangler.example.toml similarity index 87% rename from backend/database/wrangler.toml.example rename to backend/database/wrangler.example.toml index 27cb2c6..2253c4c 100644 --- a/backend/database/wrangler.toml.example +++ b/backend/database/wrangler.example.toml @@ -10,3 +10,7 @@ services = [{ binding = "STORAGE", service = "storage" }] binding = "DB" database_name = "" database_id = "" + +[vars] +KEY = "" +STORAGE_WORKER_URL = "" diff --git a/backend/server/.env.example b/backend/server/.env.example index 57727ee..d9c3433 100644 --- a/backend/server/.env.example +++ b/backend/server/.env.example @@ -1 +1,2 @@ -PORT=4000 \ No newline at end of file +PORT=4000 +WORKERS_KEY= \ No newline at end of file diff --git a/backend/server/src/index.ts b/backend/server/src/index.ts index 24df452..7d7bdba 100644 --- a/backend/server/src/index.ts +++ b/backend/server/src/index.ts @@ -70,7 +70,12 @@ io.use(async (socket, next) => { const { sandboxId, userId } = parseQuery.data; const dbUser = await fetch( - `https://database.ishaan1013.workers.dev/api/user?id=${userId}` + `${process.env.DATABASE_WORKER_URL}/api/user?id=${userId}`, + { + headers: { + Authorization: `${process.env.WORKERS_KEY}`, + }, + } ); const dbUserJSON = (await dbUser.json()) as User; @@ -400,11 +405,12 @@ io.on("connection", async (socket) => { ) => { // Log code generation credit in DB const fetchPromise = fetch( - `https://database.ishaan1013.workers.dev/api/sandbox/generate`, + `${process.env.DATABASE_WORKER_URL}/api/sandbox/generate`, { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `${process.env.WORKERS_KEY}`, }, body: JSON.stringify({ userId: data.userId, @@ -414,7 +420,7 @@ io.on("connection", async (socket) => { // Generate code from cloudflare workers AI const generateCodePromise = fetch( - `https://ai.ishaan1013.workers.dev/api?fileName=${fileName}&code=${code}&line=${line}&instructions=${instructions}`, + `${process.env.AI_WORKER_URL}/api?fileName=${fileName}&code=${code}&line=${line}&instructions=${instructions}`, { headers: { "Content-Type": "application/json", diff --git a/backend/server/src/utils.ts b/backend/server/src/utils.ts index bbf464f..51e28f9 100644 --- a/backend/server/src/utils.ts +++ b/backend/server/src/utils.ts @@ -12,7 +12,12 @@ dotenv.config(); export const getSandboxFiles = async (id: string) => { const res = await fetch( - `https://storage.ishaan1013.workers.dev/api?sandboxId=${id}` + `${process.env.STORAGE_WORKER_URL}/api?sandboxId=${id}`, + { + headers: { + Authorization: `${process.env.WORKERS_KEY}`, + }, + } ); const data: R2Files = await res.json(); @@ -23,7 +28,12 @@ export const getSandboxFiles = async (id: string) => { export const getFolder = async (folderId: string) => { const res = await fetch( - `https://storage.ishaan1013.workers.dev/api?folderId=${folderId}` + `${process.env.STORAGE_WORKER_URL}/api?folderId=${folderId}`, + { + headers: { + Authorization: `${process.env.WORKERS_KEY}`, + }, + } ); const data: R2Files = await res.json(); @@ -88,7 +98,12 @@ const processFiles = async (paths: string[], id: string) => { const fetchFileContent = async (fileId: string): Promise => { try { const fileRes = await fetch( - `https://storage.ishaan1013.workers.dev/api?fileId=${fileId}` + `${process.env.STORAGE_WORKER_URL}/api?fileId=${fileId}`, + { + headers: { + Authorization: `${process.env.WORKERS_KEY}`, + }, + } ); return await fileRes.text(); } catch (error) { @@ -98,10 +113,11 @@ const fetchFileContent = async (fileId: string): Promise => { }; export const createFile = async (fileId: string) => { - const res = await fetch(`https://storage.ishaan1013.workers.dev/api`, { + const res = await fetch(`${process.env.STORAGE_WORKER_URL}/api`, { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `${process.env.WORKERS_KEY}`, }, body: JSON.stringify({ fileId }), }); @@ -113,10 +129,11 @@ export const renameFile = async ( newFileId: string, data: string ) => { - const res = await fetch(`https://storage.ishaan1013.workers.dev/api/rename`, { + const res = await fetch(`${process.env.STORAGE_WORKER_URL}/api/rename`, { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `${process.env.WORKERS_KEY}`, }, body: JSON.stringify({ fileId, newFileId, data }), }); @@ -124,10 +141,11 @@ export const renameFile = async ( }; export const saveFile = async (fileId: string, data: string) => { - const res = await fetch(`https://storage.ishaan1013.workers.dev/api/save`, { + const res = await fetch(`${process.env.STORAGE_WORKER_URL}/api/save`, { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `${process.env.WORKERS_KEY}`, }, body: JSON.stringify({ fileId, data }), }); @@ -135,10 +153,11 @@ export const saveFile = async (fileId: string, data: string) => { }; export const deleteFile = async (fileId: string) => { - const res = await fetch(`https://storage.ishaan1013.workers.dev/api`, { + const res = await fetch(`${process.env.STORAGE_WORKER_URL}/api`, { method: "DELETE", headers: { "Content-Type": "application/json", + Authorization: `${process.env.WORKERS_KEY}`, }, body: JSON.stringify({ fileId }), }); @@ -147,7 +166,12 @@ export const deleteFile = async (fileId: string) => { export const getProjectSize = async (id: string) => { const res = await fetch( - `https://storage.ishaan1013.workers.dev/api/size?sandboxId=${id}` + `${process.env.STORAGE_WORKER_URL}/api/size?sandboxId=${id}`, + { + headers: { + Authorization: `${process.env.WORKERS_KEY}`, + }, + } ); return (await res.json()).size; }; diff --git a/backend/storage/src/index.ts b/backend/storage/src/index.ts index b5dd2b3..47da23a 100644 --- a/backend/storage/src/index.ts +++ b/backend/storage/src/index.ts @@ -1,150 +1,159 @@ -import { z } from 'zod'; -import startercode from './startercode'; +import { z } from "zod" +import startercode from "./startercode" export interface Env { - R2: R2Bucket; + R2: R2Bucket + KEY: string } export default { - async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { - 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 }); + async fetch( + request: Request, + env: Env, + ctx: ExecutionContext + ): Promise { + 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 }) + } - if (path === '/api/project' && method === 'DELETE') { + const url = new URL(request.url) + const path = url.pathname + const method = request.method + + if (path === "/api/project" && method === "DELETE") { const deleteSchema = z.object({ sandboxId: z.string(), - }); + }) - const body = await request.json(); - const { sandboxId } = deleteSchema.parse(body); + const body = await request.json() + const { sandboxId } = deleteSchema.parse(body) - const res = await env.R2.list({ prefix: 'projects/' + sandboxId }); + const res = await env.R2.list({ prefix: "projects/" + sandboxId }) // delete all files await Promise.all( res.objects.map(async (file) => { - await env.R2.delete(file.key); + await env.R2.delete(file.key) }) - ); + ) - return success; - } else if (path === '/api/size' && method === 'GET') { - const params = url.searchParams; - const sandboxId = params.get('sandboxId'); + return success + } else if (path === "/api/size" && method === "GET") { + const params = url.searchParams + const sandboxId = params.get("sandboxId") if (sandboxId) { - const res = await env.R2.list({ prefix: `projects/${sandboxId}` }); + const res = await env.R2.list({ prefix: `projects/${sandboxId}` }) // sum up the size of all files - let size = 0; + let size = 0 for (const file of res.objects) { - size += file.size; + size += file.size } - return new Response(JSON.stringify({ size }), { status: 200 }); - } else return invalidRequest; - } else if (path === '/api') { - if (method === 'GET') { - const params = url.searchParams; - const sandboxId = params.get('sandboxId'); - const folderId = params.get('folderId'); - const fileId = params.get('fileId'); + return new Response(JSON.stringify({ size }), { status: 200 }) + } else return invalidRequest + } else if (path === "/api") { + if (method === "GET") { + const params = url.searchParams + const sandboxId = params.get("sandboxId") + const folderId = params.get("folderId") + const fileId = params.get("fileId") if (sandboxId) { - const res = await env.R2.list({ prefix: `projects/${sandboxId}` }); - return new Response(JSON.stringify(res), { status: 200 }); + const res = await env.R2.list({ prefix: `projects/${sandboxId}` }) + return new Response(JSON.stringify(res), { status: 200 }) } else if (folderId) { - const res = await env.R2.list({ prefix: folderId }); - return new Response(JSON.stringify(res), { status: 200 }); + const res = await env.R2.list({ prefix: folderId }) + return new Response(JSON.stringify(res), { status: 200 }) } else if (fileId) { - const obj = await env.R2.get(fileId); + const obj = await env.R2.get(fileId) if (obj === null) { - return new Response(`${fileId} not found`, { status: 404 }); + return new Response(`${fileId} not found`, { status: 404 }) } - const headers = new Headers(); - headers.set('etag', obj.httpEtag); - obj.writeHttpMetadata(headers); + const headers = new Headers() + headers.set("etag", obj.httpEtag) + obj.writeHttpMetadata(headers) - const text = await obj.text(); + const text = await obj.text() return new Response(text, { headers, - }); - } else return invalidRequest; - } else if (method === 'POST') { + }) + } else return invalidRequest + } else if (method === "POST") { const createSchema = z.object({ fileId: z.string(), - }); + }) - const body = await request.json(); - const { fileId } = createSchema.parse(body); + const body = await request.json() + const { fileId } = createSchema.parse(body) - await env.R2.put(fileId, ''); + await env.R2.put(fileId, "") - return success; - } else if (method === 'DELETE') { + return success + } else if (method === "DELETE") { const deleteSchema = z.object({ fileId: z.string(), - }); + }) - const body = await request.json(); - const { fileId } = deleteSchema.parse(body); + const body = await request.json() + const { fileId } = deleteSchema.parse(body) - await env.R2.delete(fileId); + await env.R2.delete(fileId) - return success; - } else return methodNotAllowed; - } else if (path === '/api/rename' && method === 'POST') { + return success + } else return methodNotAllowed + } else if (path === "/api/rename" && method === "POST") { const renameSchema = z.object({ fileId: z.string(), newFileId: z.string(), data: z.string(), - }); + }) - const body = await request.json(); - const { fileId, newFileId, data } = renameSchema.parse(body); + const body = await request.json() + const { fileId, newFileId, data } = renameSchema.parse(body) - await env.R2.delete(fileId); - await env.R2.put(newFileId, data); + await env.R2.delete(fileId) + await env.R2.put(newFileId, data) - return success; - } else if (path === '/api/save' && method === 'POST') { + return success + } else if (path === "/api/save" && method === "POST") { const renameSchema = z.object({ fileId: z.string(), data: z.string(), - }); + }) - const body = await request.json(); - const { fileId, data } = renameSchema.parse(body); + const body = await request.json() + const { fileId, data } = renameSchema.parse(body) - await env.R2.put(fileId, data); + await env.R2.put(fileId, data) - return success; - } else if (path === '/api/init' && method === 'POST') { + return success + } else if (path === "/api/init" && method === "POST") { const initSchema = z.object({ sandboxId: z.string(), - type: z.enum(['react', 'node']), - }); + type: z.enum(["react", "node"]), + }) - const body = await request.json(); - const { sandboxId, type } = initSchema.parse(body); + const body = await request.json() + const { sandboxId, type } = initSchema.parse(body) - console.log(startercode[type]); + console.log(startercode[type]) await Promise.all( startercode[type].map(async (file) => { - await env.R2.put(`projects/${sandboxId}/${file.name}`, file.body); + await env.R2.put(`projects/${sandboxId}/${file.name}`, file.body) }) - ); + ) - return success; + return success } else { - return notFound; + return notFound } }, -}; +} diff --git a/backend/storage/wrangler.toml.example b/backend/storage/wrangler.example.toml similarity index 94% rename from backend/storage/wrangler.toml.example rename to backend/storage/wrangler.example.toml index 33ed573..9ac7ba2 100644 --- a/backend/storage/wrangler.toml.example +++ b/backend/storage/wrangler.example.toml @@ -11,3 +11,6 @@ workers_dev = true binding = 'R2' bucket_name = '' preview_bucket_name = '' + +[vars] +KEY = '' diff --git a/frontend/.env.example b/frontend/.env.example index 0323b2c..dbf5621 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -4,6 +4,10 @@ NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY= LIVEBLOCKS_SECRET_KEY= NEXT_PUBLIC_SERVER_PORT=4000 +NEXT_PUBLIC_APP_URL=http://localhost:3000 + +NEXT_PUBLIC_DATABASE_WORKER_URL= +NEXT_PUBLIC_STORAGE_WORKER_URL= NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up diff --git a/frontend/app/(app)/code/[id]/page.tsx b/frontend/app/(app)/code/[id]/page.tsx index 41d51b7..f2979a1 100644 --- a/frontend/app/(app)/code/[id]/page.tsx +++ b/frontend/app/(app)/code/[id]/page.tsx @@ -11,7 +11,12 @@ export const revalidate = 0 const getUserData = async (id: string) => { const userRes = await fetch( - `https://database.ishaan1013.workers.dev/api/user?id=${id}` + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${id}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } ) const userData: User = await userRes.json() return userData @@ -19,7 +24,12 @@ const getUserData = async (id: string) => { const getSandboxData = async (id: string) => { const sandboxRes = await fetch( - `https://database.ishaan1013.workers.dev/api/sandbox?id=${id}` + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/sandbox?id=${id}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } ) const sandboxData: Sandbox = await sandboxRes.json() return sandboxData @@ -33,7 +43,12 @@ const getSharedUsers = async (usersToSandboxes: UsersToSandboxes[]) => { const shared = await Promise.all( usersToSandboxes.map(async (user) => { const userRes = await fetch( - `https://database.ishaan1013.workers.dev/api/user?id=${user.userId}` + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${user.userId}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } ) const userData: User = await userRes.json() return { id: userData.id, name: userData.name } diff --git a/frontend/app/(app)/dashboard/page.tsx b/frontend/app/(app)/dashboard/page.tsx index e64c57a..1f29f96 100644 --- a/frontend/app/(app)/dashboard/page.tsx +++ b/frontend/app/(app)/dashboard/page.tsx @@ -12,12 +12,22 @@ export default async function DashboardPage() { } const userRes = await fetch( - `https://database.ishaan1013.workers.dev/api/user?id=${user.id}` + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${user.id}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } ) const userData = (await userRes.json()) as User const sharedRes = await fetch( - `https://database.ishaan1013.workers.dev/api/sandbox/share?id=${user.id}` + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/sandbox/share?id=${user.id}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } ) const shared = (await sharedRes.json()) as { id: string diff --git a/frontend/app/(app)/layout.tsx b/frontend/app/(app)/layout.tsx index dad9bc4..10506fc 100644 --- a/frontend/app/(app)/layout.tsx +++ b/frontend/app/(app)/layout.tsx @@ -1,30 +1,36 @@ -import { User } from "@/lib/types"; -import { currentUser } from "@clerk/nextjs"; -import { redirect } from "next/navigation"; +import { User } from "@/lib/types" +import { currentUser } from "@clerk/nextjs" +import { redirect } from "next/navigation" export default async function AppAuthLayout({ children, }: { - children: React.ReactNode; + children: React.ReactNode }) { - const user = await currentUser(); + const user = await currentUser() if (!user) { - redirect("/"); + redirect("/") } const dbUser = await fetch( - `https://database.ishaan1013.workers.dev/api/user?id=${user.id}` - ); - const dbUserJSON = (await dbUser.json()) as User; + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${user.id}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } + ) + const dbUserJSON = (await dbUser.json()) as User if (!dbUserJSON.id) { const res = await fetch( - "https://database.ishaan1013.workers.dev/api/user", + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`, { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, }, body: JSON.stringify({ id: user.id, @@ -32,8 +38,8 @@ export default async function AppAuthLayout({ email: user.emailAddresses[0].emailAddress, }), } - ); + ) } - return <>{children}; + return <>{children} } diff --git a/frontend/app/api/lb-auth/route.ts b/frontend/app/api/lb-auth/route.ts index b3567df..409ea87 100644 --- a/frontend/app/api/lb-auth/route.ts +++ b/frontend/app/api/lb-auth/route.ts @@ -18,7 +18,12 @@ export async function POST(request: NextRequest) { } const res = await fetch( - `https://database.ishaan1013.workers.dev/api/user?id=${clerkUser.id}` + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${clerkUser.id}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } ) const user = (await res.json()) as User diff --git a/frontend/components/editor/generate.tsx b/frontend/components/editor/generate.tsx index b1c6123..aef3c33 100644 --- a/frontend/components/editor/generate.tsx +++ b/frontend/components/editor/generate.tsx @@ -60,9 +60,7 @@ export default function GenerateInput({ regenerate?: boolean }) => { if (user.generations >= 10) { - toast.error( - "You reached the maximum # of generations. Contact @ishaandey_ on X/Twitter to reset :)" - ) + toast.error("You reached the maximum # of generations.") return } diff --git a/frontend/components/editor/loading/index.tsx b/frontend/components/editor/loading/index.tsx index 2de37be..eebabc2 100644 --- a/frontend/components/editor/loading/index.tsx +++ b/frontend/components/editor/loading/index.tsx @@ -51,8 +51,7 @@ export default function Loading({ {didFail ? ( - Try again in a minute, or contact @ishaandey_ on Twitter/X if it - still doesn't work. + Try again soon. ) : description ? ( diff --git a/frontend/components/editor/navbar/share.tsx b/frontend/components/editor/navbar/share.tsx index 9e31525..fa02981 100644 --- a/frontend/components/editor/navbar/share.tsx +++ b/frontend/components/editor/navbar/share.tsx @@ -75,13 +75,17 @@ export default function ShareSandboxModal({ {data.visibility === "private" ? ( This sandbox is private. Making it public will allow shared - users to view and collaborate. You can still share & manage access below. + users to view and collaborate. You can still share & manage + access below. ) : null}
- + {loading ? ( <> - Loading... + {" "} + Loading... ) : ( <> @@ -111,11 +116,19 @@ export default function ShareSandboxModal({ -
diff --git a/frontend/lib/actions.ts b/frontend/lib/actions.ts index ea0f573..be1d357 100644 --- a/frontend/lib/actions.ts +++ b/frontend/lib/actions.ts @@ -9,11 +9,12 @@ export async function createSandbox(body: { visibility: string }) { const res = await fetch( - "https://database.ishaan1013.workers.dev/api/sandbox", + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/sandbox`, { method: "PUT", headers: { "Content-Type": "application/json", + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, }, body: JSON.stringify(body), } @@ -27,10 +28,11 @@ export async function updateSandbox(body: { name?: string visibility?: "public" | "private" }) { - await fetch("https://database.ishaan1013.workers.dev/api/sandbox", { + await fetch(`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/sandbox`, { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, }, body: JSON.stringify(body), }) @@ -39,20 +41,27 @@ export async function updateSandbox(body: { } export async function deleteSandbox(id: string) { - await fetch(`https://database.ishaan1013.workers.dev/api/sandbox?id=${id}`, { - method: "DELETE", - }) + await fetch( + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/sandbox?id=${id}`, + { + method: "DELETE", + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } + ) revalidatePath("/dashboard") } export async function shareSandbox(sandboxId: string, email: string) { const res = await fetch( - "https://database.ishaan1013.workers.dev/api/sandbox/share", + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/sandbox/share`, { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, }, body: JSON.stringify({ sandboxId, email }), } @@ -68,13 +77,17 @@ export async function shareSandbox(sandboxId: string, email: string) { } export async function unshareSandbox(sandboxId: string, userId: string) { - await fetch("https://database.ishaan1013.workers.dev/api/sandbox/share", { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ sandboxId, userId }), - }) + await fetch( + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/sandbox/share`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + body: JSON.stringify({ sandboxId, userId }), + } + ) revalidatePath(`/code/${sandboxId}`) }