import type { FastifyInstance } from "fastify"; import { z } from "zod"; import { createDatabase, createDatabaseUser, deleteDatabase, deleteDatabaseUser } from "../services/do"; import { addCustomerToPool, removeCustomerFromPool } from "../services/pgbouncer"; import { addCustomerChart, removeCustomerChart } from "../services/git"; import { setupCustomerDatabase, teardownCustomerDatabase } from "../services/db"; const ProvisionSchema = z.object({ name: z.string().min(2).max(32).regex(/^[a-z0-9-]+$/, "lowercase letters, numbers, and hyphens only"), appVersion: z.string().default("latest"), }); export async function customerRoutes(app: FastifyInstance) { app.post("/customers", async (req, reply) => { const body = ProvisionSchema.parse(req.body); const slug = body.name; app.log.info({ slug }, "provisioning customer"); const [, user] = await Promise.all([ createDatabase(slug), createDatabaseUser(slug), ]); await setupCustomerDatabase(slug, user.name); await addCustomerToPool(slug, user.password); addCustomerChart(slug, body.appVersion); app.log.info({ slug }, "customer provisioned"); return reply.code(201).send({ slug, status: "provisioned" }); }); app.delete("/customers/:slug", async (req, reply) => { const { slug } = req.params as { slug: string }; app.log.info({ slug }, "deprovisioning customer"); removeCustomerChart(slug); await removeCustomerFromPool(slug); await teardownCustomerDatabase(slug, slug); await Promise.all([ deleteDatabase(slug), deleteDatabaseUser(slug), ]); app.log.info({ slug }, "customer deprovisioned"); return reply.code(204).send(); }); }