changemaker.lite/api/src/modules/gallery-ads/gallery-ads-admin.routes.ts

141 lines
3.9 KiB
TypeScript

import { Router, Request, Response, NextFunction } from 'express';
import { UserRole } from '@prisma/client';
import { galleryAdsService } from './gallery-ads.service';
import { createAdSchema, updateAdSchema, listAdsSchema, reorderAdsSchema, adAnalyticsQuerySchema } from './gallery-ads.schemas';
import { validate } from '../../middleware/validate';
import { authenticate } from '../../middleware/auth.middleware';
import { requireRole } from '../../middleware/rbac.middleware';
const router = Router();
router.use(authenticate);
router.use(requireRole(UserRole.SUPER_ADMIN));
// GET /api/gallery-ads/admin — list all ads (paginated)
router.get(
'/',
validate(listAdsSchema, 'query'),
async (req: Request, res: Response, next: NextFunction) => {
try {
const result = await galleryAdsService.listAll(req.query as any);
res.json(result);
} catch (err) {
next(err);
}
}
);
// GET /api/gallery-ads/admin/analytics/aggregate — aggregate analytics across all ads
router.get(
'/analytics/aggregate',
validate(adAnalyticsQuerySchema, 'query'),
async (req: Request, res: Response, next: NextFunction) => {
try {
const { days } = req.query as any;
const analytics = await galleryAdsService.getAggregateAnalytics(days);
res.json(analytics);
} catch (err) {
next(err);
}
}
);
// GET /api/gallery-ads/admin/:id/analytics — per-ad time-series analytics
router.get(
'/:id/analytics',
validate(adAnalyticsQuerySchema, 'query'),
async (req: Request, res: Response, next: NextFunction) => {
try {
const id = parseInt(req.params.id as string, 10);
const { days } = req.query as any;
const analytics = await galleryAdsService.getAdAnalytics(id, days);
res.json(analytics);
} catch (err) {
next(err);
}
}
);
// GET /api/gallery-ads/admin/:id — get single ad
router.get('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const id = parseInt(req.params.id as string, 10);
const ad = await galleryAdsService.getById(id);
if (!ad) {
res.status(404).json({ error: 'Ad not found' });
return;
}
res.json(ad);
} catch (err) {
next(err);
}
});
// POST /api/gallery-ads/admin — create ad
router.post(
'/',
validate(createAdSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const ad = await galleryAdsService.create(req.body);
res.status(201).json(ad);
} catch (err) {
next(err);
}
}
);
// PUT /api/gallery-ads/admin/reorder — bulk reorder
router.put(
'/reorder',
validate(reorderAdsSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
await galleryAdsService.reorder(req.body.ids);
res.json({ success: true });
} catch (err) {
next(err);
}
}
);
// PUT /api/gallery-ads/admin/:id — update ad
router.put(
'/:id',
validate(updateAdSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const id = parseInt(req.params.id as string, 10);
const ad = await galleryAdsService.update(id, req.body);
if (!ad) {
res.status(404).json({ error: 'Ad not found' });
return;
}
res.json(ad);
} catch (err) {
if (err instanceof Error && err.message.includes('Cannot change type')) {
res.status(400).json({ error: err.message });
return;
}
next(err);
}
}
);
// DELETE /api/gallery-ads/admin/:id — delete ad
router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const id = parseInt(req.params.id as string, 10);
await galleryAdsService.delete(id);
res.json({ success: true });
} catch (err) {
if (err instanceof Error && err.message.includes('Cannot delete')) {
res.status(400).json({ error: err.message });
return;
}
next(err);
}
});
export { router as galleryAdsAdminRouter };