dynamic worker routes based on env
This commit is contained in:
parent
6285a68102
commit
6376493ae7
@ -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/<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 });
|
||||
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;
|
||||
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", {
|
||||
const deleteStorageRequest = new Request(
|
||||
`${env.STORAGE_WORKER_URL}/api/project`,
|
||||
{
|
||||
method: "DELETE",
|
||||
body: JSON.stringify({ sandboxId: id }),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
const deleteStorageRes = await env.STORAGE.fetch(deleteStorageRequest);
|
||||
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", {
|
||||
const initStorageRequest = new Request(
|
||||
`${env.STORAGE_WORKER_URL}/api/init`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({ sandboxId: sb.id, type }),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
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
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -10,3 +10,7 @@ services = [{ binding = "STORAGE", service = "storage" }]
|
||||
binding = "DB"
|
||||
database_name = ""
|
||||
database_id = ""
|
||||
|
||||
[vars]
|
||||
KEY = ""
|
||||
STORAGE_WORKER_URL = ""
|
@ -1 +1,2 @@
|
||||
PORT=4000
|
||||
WORKERS_KEY=
|
@ -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",
|
||||
|
@ -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<string> => {
|
||||
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<string> => {
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
@ -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<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 });
|
||||
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 })
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -11,3 +11,6 @@ workers_dev = true
|
||||
binding = 'R2'
|
||||
bucket_name = ''
|
||||
preview_bucket_name = ''
|
||||
|
||||
[vars]
|
||||
KEY = ''
|
@ -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
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
@ -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}</>
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -51,8 +51,7 @@ export default function Loading({
|
||||
</DialogTitle>
|
||||
{didFail ? (
|
||||
<DialogDescription className="pt-2">
|
||||
Try again in a minute, or contact @ishaandey_ on Twitter/X if it
|
||||
still doesn't work.
|
||||
Try again soon.
|
||||
</DialogDescription>
|
||||
) : description ? (
|
||||
<DialogDescription className="pt-2">
|
||||
|
@ -75,13 +75,17 @@ export default function ShareSandboxModal({
|
||||
{data.visibility === "private" ? (
|
||||
<DialogDescription className="text-sm text-muted-foreground">
|
||||
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.
|
||||
</DialogDescription>
|
||||
) : null}
|
||||
</DialogHeader>
|
||||
<div className="flex space-x-4 w-full">
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex w-full">
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="flex w-full"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
@ -101,7 +105,8 @@ export default function ShareSandboxModal({
|
||||
<Button disabled={loading} type="submit" className="">
|
||||
{loading ? (
|
||||
<>
|
||||
<Loader2 className="animate-spin mr-2 h-4 w-4" /> Loading...
|
||||
<Loader2 className="animate-spin mr-2 h-4 w-4" />{" "}
|
||||
Loading...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@ -111,10 +116,18 @@ export default function ShareSandboxModal({
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
<Button onClick={() => {
|
||||
navigator.clipboard.writeText(`https://s.ishaand.com/code/${data.id}`)
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
`${process.env.NEXT_PUBLIC_APP_URL}/code/${data.id}`
|
||||
)
|
||||
toast.success("Link copied to clipboard.")
|
||||
}} size="icon" disabled={loading} variant="outline" className="shrink-0">
|
||||
}}
|
||||
size="icon"
|
||||
disabled={loading}
|
||||
variant="outline"
|
||||
className="shrink-0"
|
||||
>
|
||||
<Link className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -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}`, {
|
||||
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", {
|
||||
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}`)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user