import { Router } from 'express'; import { AppError } from '../lib/errors.js'; import { requireAuth, requirePolicy } from '../middleware/auth.js'; import { requireIdempotencyKey } from '../middleware/idempotency.js'; import { buildIdempotencyKey, readIdempotentResult, writeIdempotentResult } from '../services/idempotency-store.js'; import { commandBaseSchema } from '../contracts/commands/command-base.js'; function parseBody(body) { const parsed = commandBaseSchema.safeParse(body || {}); if (!parsed.success) { throw new AppError('VALIDATION_ERROR', 'Invalid command payload', 400, { issues: parsed.error.issues, }); } return parsed.data; } function createCommandResponse(route, requestId, idempotencyKey) { return { accepted: true, route, commandId: `${route}:${Date.now()}`, idempotencyKey, requestId, }; } function buildCommandHandler(policyAction, policyResource) { return async (req, res, next) => { try { parseBody(req.body); const route = `${req.baseUrl}${req.route.path}`; const compositeKey = buildIdempotencyKey({ userId: req.actor.uid, route, idempotencyKey: req.idempotencyKey, }); const existing = await readIdempotentResult(compositeKey); if (existing) { return res.status(existing.statusCode).json(existing.payload); } const payload = createCommandResponse(route, req.requestId, req.idempotencyKey); const persisted = await writeIdempotentResult({ compositeKey, userId: req.actor.uid, route, idempotencyKey: req.idempotencyKey, payload, statusCode: 200, }); return res.status(persisted.statusCode).json(persisted.payload); } catch (error) { return next(error); } }; } export function createCommandsRouter() { const router = Router(); router.post( '/orders/create', requireAuth, requireIdempotencyKey, requirePolicy('orders.create', 'order'), buildCommandHandler('orders.create', 'order') ); router.post( '/orders/:orderId/update', requireAuth, requireIdempotencyKey, requirePolicy('orders.update', 'order'), buildCommandHandler('orders.update', 'order') ); router.post( '/orders/:orderId/cancel', requireAuth, requireIdempotencyKey, requirePolicy('orders.cancel', 'order'), buildCommandHandler('orders.cancel', 'order') ); router.post( '/shifts/:shiftId/change-status', requireAuth, requireIdempotencyKey, requirePolicy('shifts.change-status', 'shift'), buildCommandHandler('shifts.change-status', 'shift') ); router.post( '/shifts/:shiftId/assign-staff', requireAuth, requireIdempotencyKey, requirePolicy('shifts.assign-staff', 'shift'), buildCommandHandler('shifts.assign-staff', 'shift') ); router.post( '/shifts/:shiftId/accept', requireAuth, requireIdempotencyKey, requirePolicy('shifts.accept', 'shift'), buildCommandHandler('shifts.accept', 'shift') ); return router; }