changemaker.lite/api/src/modules/pages/pages-admin.routes.ts
2026-03-08 18:11:26 -06:00

141 lines
3.6 KiB
TypeScript

import { Router, Request, Response, NextFunction } from 'express';
import { pagesService } from './pages.service';
import { createLandingPageSchema, updateLandingPageSchema, listLandingPagesSchema } from './pages.schemas';
import { validate } from '../../middleware/validate';
import { authenticate } from '../../middleware/auth.middleware';
import { requireRole } from '../../middleware/rbac.middleware';
import { prisma } from '../../config/database';
import { CONTENT_ROLES } from '../../utils/roles';
const router = Router();
router.use(authenticate);
router.use(requireRole(...CONTENT_ROLES));
// GET /api/pages/view-counts — landing page view counts (last 30d)
router.get(
'/view-counts',
async (_req: Request, res: Response, next: NextFunction) => {
try {
const since = new Date();
since.setDate(since.getDate() - 30);
const rows = await prisma.docsPageView.groupBy({
by: ['path'],
where: {
path: { startsWith: '/p/' },
createdAt: { gte: since },
},
_count: { id: true },
});
const counts: Record<string, number> = {};
for (const row of rows) {
// Extract slug from /p/:slug
const slug = row.path.replace(/^\/p\//, '');
counts[slug] = row._count.id;
}
res.json(counts);
} catch (err) {
next(err);
}
}
);
// POST /api/pages/sync — sync MkDocs overrides (must be before /:id routes)
router.post(
'/sync',
async (_req: Request, res: Response, next: NextFunction) => {
try {
const result = await pagesService.syncOverrides();
res.json(result);
} catch (err) {
next(err);
}
}
);
// POST /api/pages/validate — validate and repair MkDocs exports
router.post(
'/validate',
async (_req: Request, res: Response, next: NextFunction) => {
try {
const result = await pagesService.validateExports();
res.json(result);
} catch (err) {
next(err);
}
}
);
// GET /api/pages — list landing pages with pagination/filters
router.get(
'/',
validate(listLandingPagesSchema, 'query'),
async (req: Request, res: Response, next: NextFunction) => {
try {
const result = await pagesService.findAll(req.query as any);
res.json(result);
} catch (err) {
next(err);
}
}
);
// GET /api/pages/:id — get single landing page
router.get(
'/:id',
async (req: Request, res: Response, next: NextFunction) => {
try {
const id = req.params.id as string;
const page = await pagesService.findById(id);
res.json(page);
} catch (err) {
next(err);
}
}
);
// POST /api/pages — create landing page
router.post(
'/',
validate(createLandingPageSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const page = await pagesService.create(req.body);
res.status(201).json(page);
} catch (err) {
next(err);
}
}
);
// PUT /api/pages/:id — update landing page
router.put(
'/:id',
validate(updateLandingPageSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const id = req.params.id as string;
const page = await pagesService.update(id, req.body);
res.json(page);
} catch (err) {
next(err);
}
}
);
// DELETE /api/pages/:id — delete landing page
router.delete(
'/:id',
async (req: Request, res: Response, next: NextFunction) => {
try {
const id = req.params.id as string;
await pagesService.delete(id);
res.status(204).send();
} catch (err) {
next(err);
}
}
);
export { router as pagesAdminRouter };