EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
apply_schema_to_managed_db.js
Go to the documentation of this file.
1#!/usr/bin/env node
2// Apply schema and migrations to a PostgreSQL database using node-postgres (no psql required)
3// Usage:
4// node apply_schema_to_managed_db.js [DATABASE_URL]
5// If DATABASE_URL arg is omitted, tries:
6// 1) devops/digitalocean/db_credentials.txt (DATABASE_URL=...)
7// 2) process.env.DATABASE_URL
8
9const fs = require('fs');
10const path = require('path');
11const { Pool } = require('pg');
12
13const ROOT = path.resolve(__dirname, '../..');
14const DB_DIR = path.join(ROOT, 'database');
15const CREDS_FILE = path.join(ROOT, 'devops', 'digitalocean', 'db_credentials.txt');
16
17function loadDatabaseUrlFromCreds() {
18 if (!fs.existsSync(CREDS_FILE)) return null;
19 const content = fs.readFileSync(CREDS_FILE, 'utf8');
20 const match = content.match(/^DATABASE_URL=(.*)$/m);
21 return match ? match[1].trim() : null;
22}
23
24async function runSql(pool, filePath) {
25 if (!fs.existsSync(filePath)) {
26 console.log(`[SKIP] ${path.basename(filePath)} not found`);
27 return;
28 }
29 const sql = fs.readFileSync(filePath, 'utf8');
30 if (!sql.trim()) {
31 console.log(`[SKIP] ${path.basename(filePath)} is empty`);
32 return;
33 }
34 console.log(`[APPLY] ${path.basename(filePath)} ...`);
35 try {
36 await pool.query(sql);
37 console.log(`[OK] ${path.basename(filePath)}`);
38 } catch (err) {
39 console.warn(`[WARN] Failed applying ${path.basename(filePath)}: ${err.message}`);
40 }
41}
42
43(async () => {
44 try {
45 let databaseUrl = process.argv[2] || loadDatabaseUrlFromCreds() || process.env.DATABASE_URL;
46 if (!databaseUrl) {
47 console.error('[ERR] No DATABASE_URL provided or found in creds/env.');
48 process.exit(1);
49 }
50
51 // Ensure we control SSL settings via client options, not URL flags
52 databaseUrl = databaseUrl.replace(/([?&])sslmode=require(&|$)/, (m, p1, p2) => (p2 === '&' ? p1 : ''));
53 const pool = new Pool({ connectionString: databaseUrl, ssl: { rejectUnauthorized: false } });
54
55 console.log('[INFO] Testing connection ...');
56 await pool.query('SELECT 1');
57 console.log('[OK] Connected.');
58
59 // Apply init.sql
60 await runSql(pool, path.join(DB_DIR, 'init.sql'));
61
62 // Apply migrations in order (base entities first, then enhancements, then RLS, then dependent features)
63 const migrations = [
64 '2025_11_04_create_base_entities.sql',
65 '2025_10_31_enhance_contracts.sql',
66 '2025_10_31_enhance_invoices_products.sql',
67 '001_add_tenant_id.sql',
68 '2025_11_03_add_purchase_orders.sql',
69 '2025_11_03_add_multi_tenant_rls.sql',
70 '20251103_add_contracts_sla_hours.sql',
71 '20251103_tenant_settings.sql',
72 '2025_11_04_add_missing_user_theme_products_integrations.sql',
73 ];
74
75 for (const m of migrations) {
76 await runSql(pool, path.join(DB_DIR, 'migrations', m));
77 }
78
79 await pool.end();
80 console.log('');
81 console.log('==========================================');
82 console.log('✅ Schema and migrations applied successfully');
83 console.log('==========================================');
84 } catch (err) {
85 console.error('[ERR] Migration failed:', err.message);
86 process.exit(2);
87 }
88})();