import { FastifyInstance } from "fastify" import { db } from "../db" import { customers, projects, timeEntries } from "../db/schema" import { eq, and, inArray, desc } from "drizzle-orm" import { z } from "zod" const CustomerSchema = z.object({ name: z.string().min(1) }) const CustomerUpdateSchema = CustomerSchema.partial() export default async function customerRoutes(fastify: FastifyInstance) { fastify.addHook("preHandler", async (request, reply) => { try { await request.jwtVerify() } catch (err) { return reply.code(401).send({ message: "Unauthorized" }) } }) fastify.get("/", async (request, reply) => { const { onlyActive } = request.query as { onlyActive?: string } const isActiveDefault = onlyActive !== "false" const results = await db .select() .from(customers) .where(isActiveDefault ? eq(customers.active, true) : undefined) .orderBy(customers.name) return results }) fastify.get("/:id", async (request, reply) => { const { id } = request.params as { id: string } const [customer] = await db .select() .from(customers) .where(eq(customers.id, id)) .limit(1) if (!customer) { return reply.code(404).send({ message: "Customer not found" }) } return customer }) fastify.get("/:id/projects", async (request, reply) => { const { id } = request.params as { id: string } const results = await db .select() .from(projects) .where(eq(projects.customerId, id)) .orderBy(projects.name) return results }) fastify.get("/:id/time-entries", async (request, reply) => { const { id } = request.params as { id: string } const customerProjects = await db .select({ id: projects.id }) .from(projects) .where(eq(projects.customerId, id)) if (customerProjects.length === 0) { return [] } const projectIds = customerProjects.map((p) => p.id) const results = await db .select() .from(timeEntries) .where(inArray(timeEntries.projectId, projectIds)) .orderBy(desc(timeEntries.startTime)) .limit(50) return results }) fastify.post("/", async (request, reply) => { const body = CustomerSchema.parse(request.body) const [customer] = await db .insert(customers) .values({ name: body.name }) .returning() return reply.code(201).send(customer) }) fastify.patch("/:id", async (request, reply) => { const { id } = request.params as { id: string } const body = CustomerUpdateSchema.parse(request.body) const [customer] = await db .update(customers) .set(body) .where(eq(customers.id, id)) .returning() if (!customer) { return reply.code(404).send({ message: "Customer not found" }) } return customer }) fastify.delete("/:id", async (request, reply) => { const { id } = request.params as { id: string } const [customer] = await db .update(customers) .set({ active: false }) .where(eq(customers.id, id)) .returning() if (!customer) { return reply.code(404).send({ message: "Customer not found" }) } return reply.code(204).send() }) }