feat: add automatic database migration and seeding on docker startup
All checks were successful
Staging Build / build (push) Successful in 2m47s
All checks were successful
Staging Build / build (push) Successful in 2m47s
This commit is contained in:
93
shop/migrate.js
Normal file
93
shop/migrate.js
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user