120 lines
3.1 KiB
TypeScript
120 lines
3.1 KiB
TypeScript
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<typeof columnSchema>) {
|
|
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,
|
|
};
|
|
});
|
|
}
|