84 lines
3.5 KiB
TypeScript
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;
|