import type { FastifyInstance } from 'fastify'; import { z } from 'zod'; import { db } from '../db/client.js'; import { assertIdentifier } from '../lib/identifier.js'; const columnSchema = z.object({ name: z.string(), type: z.string(), nullable: z.boolean().optional(), primary: z.boolean().optional(), unique: z.boolean().optional(), defaultTo: z.any().optional(), }); const createTableSchema = z.object({ tableName: z.string(), columns: z.array(columnSchema).min(1), }); const dropTableSchema = z.object({ tableName: z.string(), }); const addColumnSchema = z.object({ tableName: z.string(), column: columnSchema, }); const dropColumnSchema = z.object({ tableName: z.string(), columnName: z.string(), }); const rawDdlSchema = z.object({ sql: z.string().min(1), }); function applyColumn(tableBuilder: any, column: z.infer) { const name = assertIdentifier(column.name, 'column name'); const definition = tableBuilder.specificType(name, column.type); if (column.nullable === false) { definition.notNullable(); } if (column.nullable === true) { definition.nullable(); } if (column.primary) { definition.primary(); } if (column.unique) { definition.unique(); } if (column.defaultTo !== undefined) { definition.defaultTo(column.defaultTo); } } export async function registerDdlRoutes(app: FastifyInstance) { app.post('/api/ddl/create-table', async (request) => { const payload = createTableSchema.parse(request.body); const tableName = assertIdentifier(payload.tableName, 'table name'); await db.schema.createTable(tableName, (table) => { payload.columns.forEach((column) => { applyColumn(table, column); }); }); return { ok: true, action: 'create-table', tableName }; }); app.post('/api/ddl/drop-table', async (request) => { const payload = dropTableSchema.parse(request.body); const tableName = assertIdentifier(payload.tableName, 'table name'); await db.schema.dropTableIfExists(tableName); return { ok: true, action: 'drop-table', tableName }; }); app.post('/api/ddl/add-column', async (request) => { const payload = addColumnSchema.parse(request.body); const tableName = assertIdentifier(payload.tableName, 'table name'); await db.schema.alterTable(tableName, (table) => { applyColumn(table, payload.column); }); return { ok: true, action: 'add-column', tableName, column: payload.column.name }; }); app.post('/api/ddl/drop-column', async (request) => { const payload = dropColumnSchema.parse(request.body); const tableName = assertIdentifier(payload.tableName, 'table name'); const columnName = assertIdentifier(payload.columnName, 'column name'); await db.schema.alterTable(tableName, (table) => { table.dropColumn(columnName); }); return { ok: true, action: 'drop-column', tableName, columnName }; }); app.post('/api/ddl/raw', async (request) => { const payload = rawDdlSchema.parse(request.body); const result = await db.raw(payload.sql); return { ok: true, action: 'raw', result, }; }); }