All files / routes/admin page-content.ts

90% Statements 27/30
83.33% Branches 5/6
100% Functions 2/2
90% Lines 27/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72                    1x       1x   1x   1x 4x 4x 4x       4x 4x 2x 2x 2x   1x 1x               2x           1x 7x 7x 7x 7x 7x 6x   1x 1x             1x 1x 1x              
import { Hono } from "hono";
import { eq, sql } from "drizzle-orm";
import { z } from "zod";
import { getDb } from "../../db";
import { pageContent } from "../../db/schema";
import { success, handleError, error } from "../../lib/response";
import { createDualCache } from "../../lib/cache";
 
type Env = { Bindings: CloudflareBindings };
 
const faqItemSchema = z.object({
	question: z.string().min(1).max(500),
	answer: z.string().min(1).max(10000),
});
const faqListSchema = z.array(faqItemSchema).max(50);
 
const pageContentRouter = new Hono<Env>();
 
pageContentRouter.get("/:pageKey", async (c) => {
	try {
		const db = getDb(c.env.DB);
		const rows = await db
			.select()
			.from(pageContent)
			.where(eq(pageContent.pageKey, c.req.param("pageKey")));
		const row = rows[0];
		if (row?.faqs) {
			try {
				const faqs = JSON.parse(row.faqs) as { question: string; answer: string }[];
				return success(c, { pageKey: c.req.param("pageKey"), faqs });
			} catch (parseErr) {
				Eif (parseErr instanceof SyntaxError) {
					return c.json(
						{ success: false, error: { code: "FAQ_JSON_CORRUPT", raw: row.faqs } },
						500,
					);
				}
				throw parseErr;
			}
		}
		return success(c, { pageKey: c.req.param("pageKey"), faqs: [] });
	} catch (err) {
		return handleError(c, err);
	}
});
 
pageContentRouter.put("/:pageKey", async (c) => {
	try {
		const db = getDb(c.env.DB);
		const body = await c.req.json<unknown>();
		const parsed = faqListSchema.safeParse((body as { faqs?: unknown })?.faqs);
		if (!parsed.success) {
			return error(c, "VALIDATION_ERROR", "Invalid FAQ payload", 400);
		}
		const faqsJson = JSON.stringify(parsed.data);
		await db
			.insert(pageContent)
			.values({ pageKey: c.req.param("pageKey"), faqs: faqsJson })
			.onConflictDoUpdate({
				target: pageContent.pageKey,
				set: { faqs: faqsJson, dateUpdated: sql`(unixepoch())` },
			});
		const cache = createDualCache(c.env.KV_CACHE);
		await cache.delete(`seo:page:${c.req.param("pageKey")}`);
		return success(c, { pageKey: c.req.param("pageKey"), faqs: parsed.data });
	} catch (err) {
		return handleError(c, err);
	}
});
 
export default pageContentRouter;