Files
HoruxDespachosNuevo/apps/api/src/controllers/admin-addons.controller.ts

87 lines
3.0 KiB
TypeScript

import type { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { prisma } from '../config/database.js';
import { isPlatformStaff } from '../utils/platform-admin.js';
import { AppError } from '../middlewares/error.middleware.js';
import { auditFromReq } from '../utils/audit.js';
async function requireStaff(req: Request) {
if (!req.user?.userId) throw new AppError(401, 'No autenticado');
const isStaff = await isPlatformStaff(req.user.userId);
if (!isStaff) throw new AppError(403, 'Acceso restringido a staff de plataforma');
}
const updateSchema = z.object({
nombre: z.string().min(1).max(200).optional(),
precio: z.number().nonnegative().optional(),
active: z.boolean().optional(),
});
/** Lista todo el catálogo de add-ons (incluye inactivos). */
export async function listCatalogo(req: Request, res: Response, next: NextFunction) {
try {
await requireStaff(req);
const items = await prisma.planAddonCatalogo.findMany({
orderBy: { codename: 'asc' },
include: {
_count: { select: { subscriptionAddons: { where: { status: { in: ['authorized', 'pending'] } } } } },
},
});
return res.json({
data: items.map(i => ({
id: i.id,
codename: i.codename,
nombre: i.nombre,
verticalProfile: i.verticalProfile,
precio: Number(i.precio),
frecuencia: i.frecuencia,
active: i.active,
delta: i.delta,
createdAt: i.createdAt.toISOString(),
suscripcionesActivas: i._count.subscriptionAddons,
})),
});
} catch (err) { return next(err); }
}
export async function updateCatalogoItem(req: Request, res: Response, next: NextFunction) {
try {
await requireStaff(req);
const id = String(req.params.id);
const data = updateSchema.parse(req.body);
const before = await prisma.planAddonCatalogo.findUnique({ where: { id } });
if (!before) throw new AppError(404, 'Add-on no encontrado');
const updated = await prisma.planAddonCatalogo.update({
where: { id },
data: {
...(data.nombre !== undefined ? { nombre: data.nombre } : {}),
...(data.precio !== undefined ? { precio: data.precio } : {}),
...(data.active !== undefined ? { active: data.active } : {}),
},
});
auditFromReq(req, 'addon.catalogo_updated', {
entityType: 'PlanAddonCatalogo',
entityId: id,
metadata: {
codename: before.codename,
before: { nombre: before.nombre, precio: Number(before.precio), active: before.active },
after: { nombre: updated.nombre, precio: Number(updated.precio), active: updated.active },
},
});
return res.json({
id: updated.id,
codename: updated.codename,
nombre: updated.nombre,
precio: Number(updated.precio),
frecuencia: updated.frecuencia,
active: updated.active,
});
} catch (err: any) {
if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message));
return next(err);
}
}