Files
egfh-website/scripts/bootstrap-directus-sql.mjs
T
jamesp 290ff0bc1e Initial commit
Co-authored-by: Copilot <copilot@github.com>
2026-05-11 15:55:14 -04:00

188 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Bootstrap Directus schema by creating PostgreSQL tables.
* Directus will auto-discover these tables on startup and create the field metadata automatically.
*/
import pkg from 'pg';
const { Pool } = pkg;
const pgConfig = {
host: process.env.POSTGRES_HOST || 'db',
port: parseInt(process.env.POSTGRES_PORT || '5432'),
user: process.env.POSTGRES_USER || 'directus',
password: process.env.POSTGRES_PASSWORD || 'change-me',
database: process.env.POSTGRES_DB || 'directus',
};
const pool = new Pool(pgConfig);
console.log('========================================');
console.log('Directus Schema Bootstrap');
console.log('========================================');
console.log('');
// Define collections and fields
const schema = {
tags: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'name', type: 'varchar(255)', nullable: false, default: null },
{ field: 'slug', type: 'varchar(255)', nullable: false, default: null },
],
news: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'title', type: 'varchar(255)', nullable: false, default: null },
{ field: 'slug', type: 'varchar(255)', nullable: false, default: null },
{ field: 'body', type: 'text', nullable: true, default: null },
{ field: 'summary', type: 'text', nullable: true, default: null },
{ field: 'featured_image', type: 'uuid', nullable: true, default: null },
{ field: 'status', type: 'varchar(255)', nullable: true, default: null },
{ field: 'publish_date', type: 'timestamp with time zone', nullable: true, default: null },
{ field: 'author', type: 'varchar(255)', nullable: true, default: null },
],
events: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'title', type: 'varchar(255)', nullable: false, default: null },
{ field: 'slug', type: 'varchar(255)', nullable: false, default: null },
{ field: 'description', type: 'text', nullable: true, default: null },
{ field: 'start_datetime', type: 'timestamp with time zone', nullable: false, default: null },
{ field: 'end_datetime', type: 'timestamp with time zone', nullable: true, default: null },
{ field: 'location_text', type: 'varchar(255)', nullable: true, default: null },
{ field: 'image', type: 'uuid', nullable: true, default: null },
{ field: 'registration_link', type: 'varchar(500)', nullable: true, default: null },
{ field: 'status', type: 'varchar(255)', nullable: true, default: null },
{ field: 'is_featured', type: 'boolean', nullable: true, default: 'false' },
],
notices: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'title', type: 'varchar(255)', nullable: false, default: null },
{ field: 'message', type: 'text', nullable: false, default: null },
{ field: 'severity', type: 'varchar(20)', nullable: true, default: "'info'" },
{ field: 'start_date', type: 'date', nullable: true, default: null },
{ field: 'end_date', type: 'date', nullable: true, default: null },
{ field: 'active', type: 'boolean', nullable: true, default: 'true' },
{ field: 'priority', type: 'integer', nullable: true, default: '0' },
],
fuel_prices: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'fuel_type', type: 'varchar(255)', nullable: false, default: null },
{ field: 'price_per_litre', type: 'numeric(10,2)', nullable: false, default: null },
{ field: 'currency', type: 'varchar(3)', nullable: true, default: "'GBP'" },
{ field: 'last_updated', type: 'timestamp with time zone', nullable: true, default: null },
{ field: 'notes', type: 'text', nullable: true, default: null },
],
documents: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'title', type: 'varchar(255)', nullable: false, default: null },
{ field: 'file', type: 'uuid', nullable: true, default: null },
{ field: 'category', type: 'varchar(255)', nullable: true, default: null },
{ field: 'description', type: 'text', nullable: true, default: null },
{ field: 'uploaded_at', type: 'timestamp with time zone', nullable: true, default: null },
],
contacts: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'name', type: 'varchar(255)', nullable: false, default: null },
{ field: 'role', type: 'varchar(255)', nullable: true, default: null },
{ field: 'email', type: 'varchar(255)', nullable: false, default: null },
{ field: 'phone', type: 'varchar(20)', nullable: true, default: null },
{ field: 'is_public', type: 'boolean', nullable: true, default: 'true' },
{ field: 'order', type: 'integer', nullable: true, default: '0' },
],
news_tags: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'news_id', type: 'integer', nullable: false, default: null },
{ field: 'tag_id', type: 'integer', nullable: false, default: null },
],
events_tags: [
{ field: 'id', type: 'integer', nullable: false, default: null },
{ field: 'event_id', type: 'integer', nullable: false, default: null },
{ field: 'tag_id', type: 'integer', nullable: false, default: null },
],
};
async function main() {
const client = await pool.connect();
try {
console.log('🔐 Connecting to PostgreSQL...');
await client.query('SELECT 1');
console.log('✓ Connected successfully');
console.log('');
console.log('📋 Creating tables...');
console.log('');
// Create all tables
const created = [];
for (const [collectionName, fields] of Object.entries(schema)) {
const wasCreated = await createTable(client, collectionName, fields);
if (wasCreated) created.push(collectionName);
}
console.log('');
if (created.length > 0) {
console.log('✅ Schema bootstrap completed successfully!');
console.log('');
console.log(`Created ${created.length} tables:`);
created.forEach(name => console.log(` - ${name}`));
console.log('');
console.log('Directus will discover these tables automatically.');
} else {
console.log('️ All tables already exist. Schema is up to date.');
}
} catch (error) {
console.error('❌ Bootstrap failed:', error.message);
console.error(error);
process.exit(1);
} finally {
client.release();
await pool.end();
}
}
async function createTable(client, collectionName, fields) {
// Check if table already exists
const checkResult = await client.query(
`SELECT to_regclass($1)`,
[`public.${collectionName}`]
);
if (checkResult.rows[0].to_regclass) {
console.log(` Table exists: ${collectionName}`);
return false;
}
// Build CREATE TABLE statement
const columnDefs = fields.map(f => {
let def = `"${f.field}" ${f.type}`;
if (!f.nullable) {
def += ' NOT NULL';
}
if (f.default) {
def += ` DEFAULT ${f.default}`;
}
return def;
});
// Add primary key if not already in fields
if (!fields.some(f => f.field === 'id')) {
columnDefs.unshift('"id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY');
}
const createTableSQL = `CREATE TABLE "public"."${collectionName}" (${columnDefs.join(', ')})`;
try {
await client.query(createTableSQL);
console.log(` ✓ Created table: ${collectionName}`);
return true;
} catch (error) {
if (error.code === '42P07') {
// Table already exists
console.log(` Table exists: ${collectionName}`);
return false;
}
throw error;
}
}
main();