Files
tool-evaluator/artifacts/api-server/src/routes/costs.ts
T

84 lines
3.5 KiB
TypeScript

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<void> => {
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<void> => {
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<void> => {
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<string, unknown> = {};
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<void> => {
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;