146 lines
4.3 KiB
TypeScript
146 lines
4.3 KiB
TypeScript
// src/routes/flyer.routes.ts
|
|
import { Router } from 'express';
|
|
import * as db from '../services/db/index.db';
|
|
import { z } from 'zod';
|
|
import { validateRequest } from '../middleware/validation.middleware';
|
|
import { optionalNumeric } from '../utils/zodUtils';
|
|
|
|
const router = Router();
|
|
|
|
// --- Zod Schemas for Flyer Routes ---
|
|
|
|
const getFlyersSchema = z.object({
|
|
query: z.object({
|
|
limit: optionalNumeric({ default: 20, integer: true, positive: true }),
|
|
offset: optionalNumeric({ default: 0, integer: true, nonnegative: true }),
|
|
}),
|
|
});
|
|
|
|
const flyerIdParamSchema = z.object({
|
|
params: z.object({
|
|
id: z.coerce.number().int('Invalid flyer ID provided.').positive('Invalid flyer ID provided.'),
|
|
}),
|
|
});
|
|
|
|
const batchFetchSchema = z.object({
|
|
body: z.object({
|
|
flyerIds: z.array(z.number().int().positive()).min(1, 'flyerIds must be a non-empty array.'),
|
|
}),
|
|
});
|
|
|
|
const batchCountSchema = z.object({
|
|
body: z.object({
|
|
flyerIds: z.array(z.number().int().positive()),
|
|
}),
|
|
});
|
|
|
|
const trackItemSchema = z.object({
|
|
params: z.object({
|
|
itemId: z.coerce.number().int().positive('Invalid item ID provided.'),
|
|
}),
|
|
body: z.object({
|
|
type: z.enum(['view', 'click'], {
|
|
message: 'A valid interaction type ("view" or "click") is required.',
|
|
}),
|
|
}),
|
|
});
|
|
|
|
/**
|
|
* GET /api/flyers - Get a paginated list of all flyers.
|
|
*/
|
|
type GetFlyersRequest = z.infer<typeof getFlyersSchema>;
|
|
router.get('/', validateRequest(getFlyersSchema), async (req, res, next): Promise<void> => {
|
|
const { query } = req as unknown as GetFlyersRequest;
|
|
try {
|
|
const limit = query.limit ? Number(query.limit) : 20;
|
|
const offset = query.offset ? Number(query.offset) : 0;
|
|
const flyers = await db.flyerRepo.getFlyers(req.log, limit, offset);
|
|
res.json(flyers);
|
|
} catch (error) {
|
|
req.log.error({ error }, 'Error fetching flyers in /api/flyers:');
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/flyers/:id - Get a single flyer by its ID.
|
|
*/
|
|
type GetFlyerByIdRequest = z.infer<typeof flyerIdParamSchema>;
|
|
router.get('/:id', validateRequest(flyerIdParamSchema), async (req, res, next): Promise<void> => {
|
|
const { params } = req as unknown as GetFlyerByIdRequest;
|
|
try {
|
|
const flyer = await db.flyerRepo.getFlyerById(params.id);
|
|
res.json(flyer);
|
|
} catch (error) {
|
|
req.log.error({ error, flyerId: params.id }, 'Error fetching flyer by ID:');
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/flyers/:id/items - Get all items for a specific flyer.
|
|
*/
|
|
router.get(
|
|
'/:id/items',
|
|
validateRequest(flyerIdParamSchema),
|
|
async (req, res, next): Promise<void> => {
|
|
const { params } = req as unknown as GetFlyerByIdRequest;
|
|
try {
|
|
const items = await db.flyerRepo.getFlyerItems(params.id, req.log);
|
|
res.json(items);
|
|
} catch (error) {
|
|
req.log.error({ error }, 'Error fetching flyer items in /api/flyers/:id/items:');
|
|
next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
/**
|
|
* POST /api/flyers/items/batch-fetch - Get all items for multiple flyers at once.
|
|
*/
|
|
type BatchFetchRequest = z.infer<typeof batchFetchSchema>;
|
|
router.post(
|
|
'/items/batch-fetch',
|
|
validateRequest(batchFetchSchema),
|
|
async (req, res, next): Promise<void> => {
|
|
const { body } = req as unknown as BatchFetchRequest;
|
|
try {
|
|
const items = await db.flyerRepo.getFlyerItemsForFlyers(body.flyerIds, req.log);
|
|
res.json(items);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
/**
|
|
* POST /api/flyers/items/batch-count - Get the total number of items for multiple flyers.
|
|
*/
|
|
type BatchCountRequest = z.infer<typeof batchCountSchema>;
|
|
router.post(
|
|
'/items/batch-count',
|
|
validateRequest(batchCountSchema),
|
|
async (req, res, next): Promise<void> => {
|
|
const { body } = req as unknown as BatchCountRequest;
|
|
try {
|
|
// The DB function handles an empty array, so we can simplify.
|
|
const count = await db.flyerRepo.countFlyerItemsForFlyers(body.flyerIds ?? [], req.log);
|
|
res.json({ count });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
/**
|
|
* POST /api/flyers/items/:itemId/track - Tracks a user interaction with a flyer item.
|
|
*/
|
|
type TrackItemRequest = z.infer<typeof trackItemSchema>;
|
|
router.post('/items/:itemId/track', validateRequest(trackItemSchema), (req, res): void => {
|
|
const { params, body } = req as unknown as TrackItemRequest;
|
|
db.flyerRepo.trackFlyerItemInteraction(params.itemId, body.type, req.log);
|
|
res.status(202).send();
|
|
});
|
|
|
|
export default router;
|