diff --git a/apps/backend/index.ts b/apps/backend/index.ts index f9da50b..c88732d 100644 --- a/apps/backend/index.ts +++ b/apps/backend/index.ts @@ -14,6 +14,7 @@ import dotenv from "dotenv"; import paymentRoutes from "./routes/payment.routes"; import { router as webhookRouter } from "./routes/webhook.routes"; +import { router as packRoutes } from "./routes/packs.routes"; const IMAGE_GEN_CREDITS = 1; const TRAIN_MODEL_CREDITS = 20; @@ -223,13 +224,7 @@ app.post("/pack/generate", authMiddleware, async (req, res) => { }); }); -app.get("/pack/bulk", async (req, res) => { - const packs = await prismaClient.packs.findMany({}); - res.json({ - packs, - }); -}); app.get("/image/bulk", authMiddleware, async (req, res) => { const ids = req.query.ids as string[]; @@ -356,6 +351,7 @@ app.post("/fal-ai/webhook/image", async (req, res) => { app.use("/payment", paymentRoutes); app.use("/api/webhook", webhookRouter); +app.use("/pack", packRoutes); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); diff --git a/apps/backend/middleware.ts b/apps/backend/middleware.ts index ee1eab9..7f3b6cb 100644 --- a/apps/backend/middleware.ts +++ b/apps/backend/middleware.ts @@ -7,6 +7,7 @@ declare global { interface Request { userId?: string; user?: { + role?: "admin" | "user"; email: string; }; } @@ -19,6 +20,7 @@ export async function authMiddleware( next: NextFunction ) { try { + console.log("Authenticating request"); const authHeader = req.headers["authorization"]; const token = authHeader?.split(" ")[1]; @@ -26,7 +28,7 @@ export async function authMiddleware( res.status(401).json({ message: "No token provided" }); return; } - + // Debug logs console.log("Received token:", token); @@ -62,6 +64,7 @@ export async function authMiddleware( // Fetch user details from Clerk const user = await clerkClient.users.getUser(userId); + console.log("User details:", user); const primaryEmail = user.emailAddresses.find( (email) => email.id === user.primaryEmailAddressId ); @@ -70,11 +73,13 @@ export async function authMiddleware( console.error("No email found for user"); res.status(400).json({ message: "User email not found" }); return; + } // Attach the user ID and email to the request req.userId = userId; req.user = { + role: (user.publicMetadata.role as "admin" | "user") || "user", email: primaryEmail.emailAddress, }; @@ -99,3 +104,20 @@ export async function authMiddleware( return; } } + +export async function adminMiddleware( + req: Request, + res: Response, + next: NextFunction +) { + console.log("Checking user role"); + await authMiddleware(req, res, () => { + console.log("User role:", req.user?.role); + if (req.user?.role !== "admin") { + res.status(403).json({ error: "Unauthorized" }); + return; + } + + next(); + }); +} diff --git a/apps/backend/package.json b/apps/backend/package.json index c6d3181..b6411c6 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -4,7 +4,7 @@ "type": "module", "scripts": { "start": "bun run index.ts", - "dev": "bun run index.ts" + "dev": "bun run --watch index.ts" }, "devDependencies": { "@types/bun": "latest" diff --git a/apps/backend/routes/packs.routes.ts b/apps/backend/routes/packs.routes.ts new file mode 100644 index 0000000..0963aa5 --- /dev/null +++ b/apps/backend/routes/packs.routes.ts @@ -0,0 +1,119 @@ +import { prismaClient } from "db"; +import { Router } from "express"; +import { adminMiddleware } from "../middleware"; +import { packSchema } from "common/types"; + +export const router: Router = Router(); + +// Fetch all packs +router.get("/bulk", async (req, res) => { + try { + const packs = await prismaClient.packs.findMany(); + res.status(200).json({ packs }); + } catch (error) { + res.status(500).json({ error: "Error fetching packs" }); + } +}); + +// Fetch a pack by ID +router.get("/:id", async (req, res) => { + try { + const pack = await prismaClient.packs.findUnique({ + where: { + id: req.params.id, + }, + include: { + prompts: true, + }, + }); + + if (!pack) { + res.status(404).json({ error: "Pack not found" }); + return; + } + + res.status(200).json({ pack }); + } catch (error) { + res.status(500).json({ error: "Error fetching pack" }); + } +}); + +// Create a new pack +router.post("/",adminMiddleware, async (req, res) => { + try { + const parsedata = packSchema.safeParse(req.body); + if (!parsedata.success) { + res.status(400).json({ error: parsedata.error }); + return; + } + + const newPack = await prismaClient.packs.create({ + data: { + name: parsedata.data.name, + description: parsedata.data.description, + imageUrl1: parsedata.data.imageUrl1, + imageUrl2: parsedata.data.imageUrl2, + prompts: { + create: parsedata.data.prompts.map((prompt) => ({ + prompt: prompt.prompt, + })), + }, + }, + }); + + res.status(201).json({ pack: newPack }); + } catch (error) { + res.status(500).json({ error: "Error creating pack" }); + } +}); + +// Update a pack by ID +router.put("/:id",adminMiddleware, async (req, res) => { + try { + const parsedata = packSchema.safeParse(req.body); + if (!parsedata.success) { + res.status(400).json({ error: parsedata.error }); + return; + } + const updatedPack = await prismaClient.packs.update({ + where: { + id: parsedata.data.id, + }, + data: { + name: parsedata.data.name, + description: parsedata.data.description, + imageUrl1: parsedata.data.imageUrl1, + imageUrl2: parsedata.data.imageUrl2, + prompts: { + upsert: parsedata.data.prompts.map((prompt) => ({ + where: { id: prompt.id }, + update: { prompt: prompt.prompt }, + create: { prompt: prompt.prompt }, + })), + }, + }, + }); + + res.status(200).json({ + pack: updatedPack, + }); + } catch (error) { + res.status(500).json({ error: "Error updating pack" }); + } +}); + +// Delete a pack by ID +router.delete("/:id",adminMiddleware, async (req, res) => { + try { + await prismaClient.packs.delete({ + where: { + id: req.params.id, + }, + }); + + res.status(204).json({ message: "Pack deleted" }); + } catch (error) { + console.error(error); + res.status(500).json({ error: "Error deleting pack" }); + } +}); diff --git a/apps/studio/.gitignore b/apps/studio/.gitignore new file mode 100644 index 0000000..3a254f0 --- /dev/null +++ b/apps/studio/.gitignore @@ -0,0 +1,3 @@ +node_modules +# Keep environment variables out of version control +.env \ No newline at end of file diff --git a/apps/studio/package.json b/apps/studio/package.json new file mode 100644 index 0000000..3f62d06 --- /dev/null +++ b/apps/studio/package.json @@ -0,0 +1,12 @@ +{ + "name": "studio", + "version": "0.0.0", + "scripts": { + "dev": "prisma studio --schema ../../packages/db/prisma/schema.prisma --port 3005", + "clean": "git clean -xdf .cache .turbo dist node_modules", + "typecheck": "tsc --noEmit --emitDeclarationOnly false" + }, + "devDependencies": { + "prisma": "6.3.1" + } + } \ No newline at end of file diff --git a/apps/studio/tsconfig.json b/apps/studio/tsconfig.json new file mode 100644 index 0000000..61b87a1 --- /dev/null +++ b/apps/studio/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@repo/typescript-config/nextjs.json", + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] + } \ No newline at end of file diff --git a/apps/web/app/dashboard/page.tsx b/apps/web/app/(main)/dashboard/page.tsx similarity index 100% rename from apps/web/app/dashboard/page.tsx rename to apps/web/app/(main)/dashboard/page.tsx diff --git a/apps/web/app/(main)/layout.tsx b/apps/web/app/(main)/layout.tsx new file mode 100644 index 0000000..a3dd5db --- /dev/null +++ b/apps/web/app/(main)/layout.tsx @@ -0,0 +1,12 @@ +import { Appbar } from "@/components/Appbar"; +import { Footer } from "@/components/Footer"; + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( +