This commit is contained in:
Your Name committed 2026-06-13 22:02:04 -04:00
1 parent 20f43bd58d
commit 1ac727f4e5
10 files changed
+306 -122

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
import { Elysia } from "elysia";
import { jwt } from "@elysiajs/jwt";
export const adminOnly = new Elysia({ name: "adminOnly" })
.use(
jwt({
name: "jwt",
secret: process.env.JWT_SECRET || "fallback-secret"
})
)
.onBeforeHandle(async ({ jwt, headers, set }) => {
const auth = headers.authorization;
if (!auth?.startsWith("Bearer ")) {
set.status = 401;
return { error: "Unauthorized: Missing or invalid token format" };
}
const token = auth.substring(7);
const payload = await jwt.verify(token);
if (!payload) {
set.status = 401;
return { error: "Unauthorized: Invalid token" };
}
if ((payload as any).role !== "ADMIN") {
set.status = 401;
return { error: "Unauthorized: Admin access required" };
}
});
+2
View File
@@ -1,5 +1,6 @@
import { Elysia, t } from "elysia";
import prisma from "../lib/prisma";
import { adminOnly } from "../middleware/auth";
export const menusRoute = new Elysia({ prefix: "/menus" })
.get("/", async () => {
@@ -26,6 +27,7 @@ export const menusRoute = new Elysia({ prefix: "/menus" })
}
return menu;
})
.use(adminOnly)
.post("/", async ({ body, set }) => {
try {
const { name, items } = body as { name: string; items?: { label: string; url: string; order: number }[] };
+50 -35
View File
@@ -7,6 +7,14 @@ import {
QuoteCalloutPayloadSchema,
ProductCarouselPayloadSchema
} from "@homaroma/types";
import { adminOnly } from "../middleware/auth";
const RESERVED_SLUGS = [
"shop", "category", "products", "cart", "checkout", "account", "search",
"contact", "login", "admin", "wishlist", "track", "coupons", "scent-guide",
"faq", "about", "profile", "article", "pages", "orders", "customers",
"settings", "menus", "auth", "api"
];
export const pagesRoute = new Elysia({ prefix: "/pages" })
.get("/", async () => {
@@ -118,10 +126,52 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
sections: populatedSections
};
})
.get("/:slug/sections", async ({ params: { slug }, set }) => {
try {
return await prisma.pageSection.findMany({
where: { pageId: Number(slug) },
orderBy: { order: "asc" }
});
} catch (err) {
set.status = 500;
return { error: "Failed to fetch page sections" };
}
})
.post("/newsletter", async ({ body, set }) => {
try {
const { email } = body as { email: string };
if (!email || !email.includes("@")) {
set.status = 400;
return { error: "Invalid email address" };
}
const sub = await prisma.newsletterSubscription.upsert({
where: { email },
update: {},
create: { email }
});
return { success: true, data: sub };
} catch (err) {
console.error("[Newsletter] Error:", err);
set.status = 500;
return { error: "Failed to subscribe" };
}
}, {
body: t.Object({
email: t.String()
})
})
.use(adminOnly)
.post("/", async ({ body, set }) => {
try {
const data = body as any;
const { id, slug, title, isPublished, status, metadata } = data;
if (typeof slug === 'string' && RESERVED_SLUGS.includes(slug.toLowerCase())) {
set.status = 400;
return { error: "Reserved slug cannot be used" };
}
let finalIsPublished = true;
if (status !== undefined) {
finalIsPublished = status === "published";
@@ -165,17 +215,6 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
return { error: "Failed to delete page" };
}
})
.get("/:slug/sections", async ({ params: { slug }, set }) => {
try {
return await prisma.pageSection.findMany({
where: { pageId: Number(slug) },
orderBy: { order: "asc" }
});
} catch (err) {
set.status = 500;
return { error: "Failed to fetch page sections" };
}
})
.post("/:slug/sections", async ({ params: { slug }, body, set }) => {
try {
const pageId = Number(slug);
@@ -280,30 +319,6 @@ export const pagesRoute = new Elysia({ prefix: "/pages" })
}))
})
})
.post("/newsletter", async ({ body, set }) => {
try {
const { email } = body as { email: string };
if (!email || !email.includes("@")) {
set.status = 400;
return { error: "Invalid email address" };
}
const sub = await prisma.newsletterSubscription.upsert({
where: { email },
update: {},
create: { email }
});
return { success: true, data: sub };
} catch (err) {
console.error("[Newsletter] Error:", err);
set.status = 500;
return { error: "Failed to subscribe" };
}
}, {
body: t.Object({
email: t.String()
})
})
.delete("/sections/:sectionId", async ({ params: { sectionId }, set }) => {
try {
await prisma.pageSection.delete({
+2
View File
@@ -1,5 +1,6 @@
import { Elysia, t } from "elysia";
import prisma from "../lib/prisma";
import { adminOnly } from "../middleware/auth";
export const settingsRoute = new Elysia({ prefix: "/settings" })
.get("/", async ({ set }) => {
@@ -17,6 +18,7 @@ export const settingsRoute = new Elysia({ prefix: "/settings" })
return { error: "Failed to fetch settings" };
}
})
.use(adminOnly)
.put("/", async ({ body, set }) => {
try {
const data = body as any;