feat: add automatic database migration and seeding on docker startup
All checks were successful
Staging Build / build (push) Successful in 2m47s

This commit is contained in:
DanielS
2026-06-23 03:14:40 +02:00
parent 235efd5a47
commit 756ec5c002
4 changed files with 245 additions and 1 deletions

93
shop/migrate.js Normal file
View File

@@ -0,0 +1,93 @@
const { Client } = require('pg');
const fs = require('fs');
const path = require('path');
async function run() {
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
console.log('DATABASE_URL not set skipping migrations.');
return;
}
console.log('Starting database migrations...');
const client = new Client({
connectionString,
connectionTimeoutMillis: 10000,
});
await client.connect();
// Create schema_migrations table if not exists
await client.query(`
CREATE TABLE IF NOT EXISTS public.schema_migrations (
version VARCHAR(255) PRIMARY KEY,
inserted_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
);
`);
const migrationsDir = path.join(__dirname, 'supabase', 'migrations');
if (!fs.existsSync(migrationsDir)) {
console.warn(`Migrations directory not found at: ${migrationsDir}`);
await client.end();
return;
}
const files = fs.readdirSync(migrationsDir).sort();
for (const file of files) {
if (!file.endsWith('.sql')) continue;
const { rows } = await client.query('SELECT 1 FROM public.schema_migrations WHERE version = $1', [file]);
if (rows.length === 0) {
console.log(`Running migration: ${file}`);
const sql = fs.readFileSync(path.join(migrationsDir, file), 'utf8');
// We run the migration in a single transaction
await client.query('BEGIN');
try {
await client.query(sql);
await client.query('INSERT INTO public.schema_migrations (version) VALUES ($1)', [file]);
await client.query('COMMIT');
console.log(`Migration ${file} completed successfully.`);
} catch (err) {
await client.query('ROLLBACK');
console.error(`Migration ${file} failed, rolled back.`);
throw err;
}
} else {
console.log(`Skipping migration: ${file} (already run)`);
}
}
// Also check if we should run seed.sql (only if we run it for the first time or if products are empty)
const seedFile = path.join(__dirname, 'supabase', 'seed.sql');
if (fs.existsSync(seedFile)) {
try {
const { rows: productRows } = await client.query('SELECT 1 FROM public.products LIMIT 1');
if (productRows.length === 0) {
console.log('No products found. Seeding database with default products...');
const seedSql = fs.readFileSync(seedFile, 'utf8');
await client.query('BEGIN');
try {
await client.query(seedSql);
await client.query('COMMIT');
console.log('Database seeded successfully.');
} catch (err) {
await client.query('ROLLBACK');
console.error('Seeding failed, rolled back.', err);
}
}
} catch (e) {
// In case products table check fails (e.g. initial_schema wasn't run or failed)
console.error('Failed to run seed check/seed SQL', e);
}
}
await client.end();
console.log('Database migrations completed.');
}
run().catch(err => {
console.error('Migration execution failed:', err);
process.exit(1);
});