import { Router, type IRouter } from "express"; import { eq, and } from "drizzle-orm"; import { db, toolsTable, toolCostsTable } from "@workspace/db"; import { requireAuth } from "../middleware/auth"; import { requireFeature } from "../middleware/feature"; import { writeAuditLog } from "../lib/audit"; const router: IRouter = Router(); router.get("/tools/:id/costs", async (req, res): Promise => { const toolId = Number(req.params.id); if (isNaN(toolId)) { res.status(400).json({ error: "Invalid id" }); return; } const [tool] = await db.select().from(toolsTable).where(eq(toolsTable.id, toolId)); if (!tool) { res.status(404).json({ error: "Tool not found" }); return; } const costs = await db .select() .from(toolCostsTable) .where(eq(toolCostsTable.toolId, toolId)) .orderBy(toolCostsTable.createdAt); res.json(costs); }); router.post("/tools/:id/costs", requireAuth, requireFeature("costs"), async (req, res): Promise => { const toolId = Number(req.params.id); if (isNaN(toolId)) { res.status(400).json({ error: "Invalid id" }); return; } const [tool] = await db.select().from(toolsTable).where(eq(toolsTable.id, toolId)); if (!tool) { res.status(404).json({ error: "Tool not found" }); return; } const { licenseType, billingPeriod, cost, currency, renewalDate, notes } = req.body; const [entry] = await db.insert(toolCostsTable).values({ toolId, licenseType: licenseType ?? "free", billingPeriod: billingPeriod ?? null, cost: cost ?? null, currency: currency ?? "EUR", renewalDate: renewalDate ? new Date(renewalDate) : null, notes: notes ?? null, createdBy: Number(req.session.user!.sub), }).returning(); await writeAuditLog(req, "tool_cost", entry.id, "create", { toolId, licenseType, cost }); res.status(201).json(entry); }); router.patch("/costs/:id", requireAuth, async (req, res): Promise => { const id = Number(req.params.id); if (isNaN(id)) { res.status(400).json({ error: "Invalid id" }); return; } const [existing] = await db.select().from(toolCostsTable).where(eq(toolCostsTable.id, id)); if (!existing) { res.status(404).json({ error: "Cost entry not found" }); return; } const { licenseType, billingPeriod, cost, currency, renewalDate, notes } = req.body; const updateData: Record = {}; if (licenseType !== undefined) updateData.licenseType = licenseType; if (billingPeriod !== undefined) updateData.billingPeriod = billingPeriod; if (cost !== undefined) updateData.cost = cost; if (currency !== undefined) updateData.currency = currency; if (renewalDate !== undefined) updateData.renewalDate = renewalDate ? new Date(renewalDate) : null; if (notes !== undefined) updateData.notes = notes; const [updated] = await db.update(toolCostsTable).set(updateData).where(eq(toolCostsTable.id, id)).returning(); await writeAuditLog(req, "tool_cost", id, "update", { toolId: existing.toolId, ...updateData }); res.json(updated); }); router.delete("/costs/:id", requireAuth, async (req, res): Promise => { const id = Number(req.params.id); if (isNaN(id)) { res.status(400).json({ error: "Invalid id" }); return; } const [existing] = await db.select().from(toolCostsTable).where(eq(toolCostsTable.id, id)); if (!existing) { res.status(404).json({ error: "Cost entry not found" }); return; } await writeAuditLog(req, "tool_cost", id, "delete", { toolId: existing.toolId }); await db.delete(toolCostsTable).where(eq(toolCostsTable.id, id)); res.sendStatus(204); }); export default router;