import { Hono } from 'hono'; import { db, dbGet, dbAll, faqQuestions } from '../db/index.js'; import { eq, asc } from 'drizzle-orm'; import { requireAuth } from '../lib/auth.js'; import { getNow, generateId } from '../lib/utils.js'; const faqRouter = new Hono(); // ==================== Public Routes ==================== // Get FAQ list for public (only enabled; optional filter for homepage) faqRouter.get('/', async (c) => { const homepage = c.req.query('homepage') === 'true'; let query = (db as any) .select() .from(faqQuestions) .where(eq((faqQuestions as any).enabled, 1)) .orderBy(asc((faqQuestions as any).rank), asc((faqQuestions as any).createdAt)); const rows = await dbAll(query); let items = rows; if (homepage) { items = rows.filter((r: any) => r.showOnHomepage === true || r.showOnHomepage === 1 || r.show_on_homepage === true || r.show_on_homepage === 1); } return c.json({ faqs: items.map((r: any) => ({ id: r.id, question: r.question, questionEs: r.questionEs ?? r.question_es ?? null, answer: r.answer, answerEs: r.answerEs ?? r.answer_es ?? null, rank: r.rank ?? 0, })), }); }); // ==================== Admin Routes ==================== // Get all FAQ questions for admin (all, ordered by rank) faqRouter.get('/admin/list', requireAuth(['admin']), async (c) => { const rows = await dbAll( (db as any) .select() .from(faqQuestions) .orderBy(asc((faqQuestions as any).rank), asc((faqQuestions as any).createdAt)) ); const list = rows.map((r: any) => ({ id: r.id, question: r.question, questionEs: r.questionEs ?? r.question_es ?? null, answer: r.answer, answerEs: r.answerEs ?? r.answer_es ?? null, enabled: r.enabled === true || r.enabled === 1, showOnHomepage: r.showOnHomepage === true || r.showOnHomepage === 1 || r.show_on_homepage === true || r.show_on_homepage === 1, rank: r.rank ?? 0, createdAt: r.createdAt, updatedAt: r.updatedAt, })); return c.json({ faqs: list }); }); // Get one FAQ by id (admin) faqRouter.get('/admin/:id', requireAuth(['admin']), async (c) => { const { id } = c.req.param(); const row = await dbGet( (db as any).select().from(faqQuestions).where(eq((faqQuestions as any).id, id)) ); if (!row) { return c.json({ error: 'FAQ not found' }, 404); } return c.json({ faq: { id: row.id, question: row.question, questionEs: row.questionEs ?? row.question_es ?? null, answer: row.answer, answerEs: row.answerEs ?? row.answer_es ?? null, enabled: row.enabled === true || row.enabled === 1, showOnHomepage: row.showOnHomepage === true || row.showOnHomepage === 1 || row.show_on_homepage === true || row.show_on_homepage === 1, rank: row.rank ?? 0, createdAt: row.createdAt, updatedAt: row.updatedAt, }, }); }); // Create FAQ (admin) faqRouter.post('/admin', requireAuth(['admin']), async (c) => { const body = await c.req.json(); const { question, questionEs, answer, answerEs, enabled, showOnHomepage } = body; if (!question || typeof question !== 'string' || !answer || typeof answer !== 'string') { return c.json({ error: 'Question and answer (EN) are required' }, 400); } const now = getNow(); const id = generateId(); const allForRank = await dbAll( (db as any).select({ rank: (faqQuestions as any).rank }).from(faqQuestions) ); const maxRank = allForRank.length ? Math.max(...allForRank.map((r: any) => Number(r.rank ?? 0))) : 0; const nextRank = maxRank + 1; await (db as any).insert(faqQuestions).values({ id, question: String(question).trim(), questionEs: questionEs != null ? String(questionEs).trim() : null, answer: String(answer).trim(), answerEs: answerEs != null ? String(answerEs).trim() : null, enabled: enabled !== false ? 1 : 0, showOnHomepage: showOnHomepage === true ? 1 : 0, rank: nextRank, createdAt: now, updatedAt: now, }); const created = await dbGet( (db as any).select().from(faqQuestions).where(eq((faqQuestions as any).id, id)) ); return c.json( { faq: { id: created.id, question: created.question, questionEs: created.questionEs ?? created.question_es ?? null, answer: created.answer, answerEs: created.answerEs ?? created.answer_es ?? null, enabled: created.enabled === true || created.enabled === 1, showOnHomepage: created.showOnHomepage === true || created.showOnHomepage === 1 || created.show_on_homepage === true || created.show_on_homepage === 1, rank: created.rank ?? 0, createdAt: created.createdAt, updatedAt: created.updatedAt, }, }, 201 ); }); // Update FAQ (admin) faqRouter.put('/admin/:id', requireAuth(['admin']), async (c) => { const { id } = c.req.param(); const body = await c.req.json(); const { question, questionEs, answer, answerEs, enabled, showOnHomepage } = body; const existing = await dbGet( (db as any).select().from(faqQuestions).where(eq((faqQuestions as any).id, id)) ); if (!existing) { return c.json({ error: 'FAQ not found' }, 404); } const updateData: Record = { updatedAt: getNow(), }; if (question !== undefined) updateData.question = String(question).trim(); if (questionEs !== undefined) updateData.questionEs = questionEs == null ? null : String(questionEs).trim(); if (answer !== undefined) updateData.answer = String(answer).trim(); if (answerEs !== undefined) updateData.answerEs = answerEs == null ? null : String(answerEs).trim(); if (typeof enabled === 'boolean') updateData.enabled = enabled ? 1 : 0; if (typeof showOnHomepage === 'boolean') updateData.showOnHomepage = showOnHomepage ? 1 : 0; await (db as any).update(faqQuestions).set(updateData).where(eq((faqQuestions as any).id, id)); const updated = await dbGet( (db as any).select().from(faqQuestions).where(eq((faqQuestions as any).id, id)) ); return c.json({ faq: { id: updated.id, question: updated.question, questionEs: updated.questionEs ?? updated.question_es ?? null, answer: updated.answer, answerEs: updated.answerEs ?? updated.answer_es ?? null, enabled: updated.enabled === true || updated.enabled === 1, showOnHomepage: updated.showOnHomepage === true || updated.showOnHomepage === 1 || updated.show_on_homepage === true || updated.show_on_homepage === 1, rank: updated.rank ?? 0, createdAt: updated.createdAt, updatedAt: updated.updatedAt, }, }); }); // Delete FAQ (admin) faqRouter.delete('/admin/:id', requireAuth(['admin']), async (c) => { const { id } = c.req.param(); const existing = await dbGet( (db as any).select().from(faqQuestions).where(eq((faqQuestions as any).id, id)) ); if (!existing) { return c.json({ error: 'FAQ not found' }, 404); } await (db as any).delete(faqQuestions).where(eq((faqQuestions as any).id, id)); return c.json({ message: 'FAQ deleted' }); }); // Reorder FAQs (admin) – body: { ids: string[] } (ordered list of ids) faqRouter.post('/admin/reorder', requireAuth(['admin']), async (c) => { const body = await c.req.json(); const { ids } = body; if (!Array.isArray(ids) || ids.length === 0) { return c.json({ error: 'ids array is required' }, 400); } const now = getNow(); for (let i = 0; i < ids.length; i++) { await (db as any) .update(faqQuestions) .set({ rank: i, updatedAt: now }) .where(eq((faqQuestions as any).id, ids[i])); } const rows = await dbAll( (db as any) .select() .from(faqQuestions) .orderBy(asc((faqQuestions as any).rank), asc((faqQuestions as any).createdAt)) ); const list = rows.map((r: any) => ({ id: r.id, question: r.question, questionEs: r.questionEs ?? r.question_es ?? null, answer: r.answer, answerEs: r.answerEs ?? r.answer_es ?? null, enabled: r.enabled === true || r.enabled === 1, showOnHomepage: r.showOnHomepage === true || r.showOnHomepage === 1 || r.show_on_homepage === true || r.show_on_homepage === 1, rank: r.rank ?? 0, createdAt: r.createdAt, updatedAt: r.updatedAt, })); return c.json({ faqs: list }); }); export default faqRouter;