1require('dotenv').config();
2const { Pool } = require('pg');
4// Parse DATABASE_URL from environment
5const DATABASE_URL = process.env.DATABASE_URL || 'postgresql://user:password@localhost:5432/rmm_psa';
7// Strip sslmode=require from URL to ensure pg respects explicit ssl options
8const cleanedUrl = DATABASE_URL.replace(/([?&])sslmode=require(&|$)/, (m, p1, p2) => (p2 === '&' ? p1 : ''));
10const pool = new Pool({
11 connectionString: cleanedUrl,
13 rejectUnauthorized: false
17async function migrate() {
18 const client = await pool.connect();
21 console.log('Starting customer portal database migration...\n');
24 await client.query('BEGIN');
26 // 1. Create customer_users table
27 console.log('Creating customer_users table...');
29 CREATE TABLE IF NOT EXISTS customer_users (
30 customer_user_id SERIAL PRIMARY KEY,
31 tenant_id UUID NOT NULL,
32 customer_id INTEGER NOT NULL,
33 email VARCHAR(255) UNIQUE NOT NULL,
34 password_hash VARCHAR(255) NOT NULL,
35 first_name VARCHAR(100),
36 last_name VARCHAR(100),
38 is_primary BOOLEAN DEFAULT false,
39 is_active BOOLEAN DEFAULT true,
41 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
42 updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
43 UNIQUE(tenant_id, customer_id, email),
44 CONSTRAINT fk_customer_users_tenant
45 FOREIGN KEY (tenant_id)
46 REFERENCES tenants(tenant_id)
48 CONSTRAINT fk_customer_users_customer
49 FOREIGN KEY (customer_id)
50 REFERENCES customers(customer_id)
54 console.log('✅ customer_users table created');
56 // Create indexes for customer_users
57 console.log('Creating indexes for customer_users...');
59 CREATE INDEX IF NOT EXISTS idx_customer_users_tenant
60 ON customer_users(tenant_id)
63 CREATE INDEX IF NOT EXISTS idx_customer_users_customer
64 ON customer_users(customer_id)
67 CREATE INDEX IF NOT EXISTS idx_customer_users_email
68 ON customer_users(email)
70 console.log('✅ Indexes created for customer_users\n');
72 // 2. Create customer_sessions table
73 console.log('Creating customer_sessions table...');
75 CREATE TABLE IF NOT EXISTS customer_sessions (
76 session_id SERIAL PRIMARY KEY,
77 customer_user_id INTEGER NOT NULL,
78 token_hash VARCHAR(255) NOT NULL UNIQUE,
79 ip_address VARCHAR(50),
81 expires_at TIMESTAMP NOT NULL,
82 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
83 CONSTRAINT fk_customer_sessions_user
84 FOREIGN KEY (customer_user_id)
85 REFERENCES customer_users(customer_user_id)
89 console.log('✅ customer_sessions table created');
91 // Create indexes for customer_sessions
92 console.log('Creating indexes for customer_sessions...');
94 CREATE INDEX IF NOT EXISTS idx_customer_sessions_user
95 ON customer_sessions(customer_user_id)
98 CREATE INDEX IF NOT EXISTS idx_customer_sessions_token
99 ON customer_sessions(token_hash)
102 CREATE INDEX IF NOT EXISTS idx_customer_sessions_expires
103 ON customer_sessions(expires_at)
105 console.log('✅ Indexes created for customer_sessions\n');
107 // 3. Create customer_audit_log table
108 console.log('Creating customer_audit_log table...');
110 CREATE TABLE IF NOT EXISTS customer_audit_log (
111 log_id SERIAL PRIMARY KEY,
112 tenant_id UUID NOT NULL,
113 customer_user_id INTEGER,
114 action VARCHAR(100) NOT NULL,
115 entity_type VARCHAR(50),
118 ip_address VARCHAR(50),
119 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
120 CONSTRAINT fk_customer_audit_tenant
121 FOREIGN KEY (tenant_id)
122 REFERENCES tenants(tenant_id)
124 CONSTRAINT fk_customer_audit_user
125 FOREIGN KEY (customer_user_id)
126 REFERENCES customer_users(customer_user_id)
130 console.log('✅ customer_audit_log table created');
132 // Create indexes for customer_audit_log
133 console.log('Creating indexes for customer_audit_log...');
135 CREATE INDEX IF NOT EXISTS idx_customer_audit_tenant
136 ON customer_audit_log(tenant_id)
139 CREATE INDEX IF NOT EXISTS idx_customer_audit_user
140 ON customer_audit_log(customer_user_id)
143 CREATE INDEX IF NOT EXISTS idx_customer_audit_created
144 ON customer_audit_log(created_at)
146 console.log('✅ Indexes created for customer_audit_log\n');
148 // 4. Add columns to customers table
149 console.log('Checking customers table for missing columns...');
151 // Check if allow_portal_access exists
152 const portalAccessCheck = await client.query(`
154 FROM information_schema.columns
155 WHERE table_name = 'customers'
156 AND column_name = 'allow_portal_access'
159 if (portalAccessCheck.rows.length === 0) {
161 ALTER TABLE customers
162 ADD COLUMN allow_portal_access BOOLEAN DEFAULT true
164 console.log('✅ Added allow_portal_access column to customers');
166 console.log('⏭️ allow_portal_access column already exists');
169 // Check if portal_notes exists
170 const portalNotesCheck = await client.query(`
172 FROM information_schema.columns
173 WHERE table_name = 'customers'
174 AND column_name = 'portal_notes'
177 if (portalNotesCheck.rows.length === 0) {
179 ALTER TABLE customers
180 ADD COLUMN portal_notes TEXT
182 console.log('✅ Added portal_notes column to customers');
184 console.log('⏭️ portal_notes column already exists');
189 // Commit transaction
190 await client.query('COMMIT');
192 console.log('✅ Migration completed successfully!\n');
194 // Verify tables exist
195 console.log('Verifying customer portal tables...');
196 const tableCheck = await client.query(`
198 FROM information_schema.tables
199 WHERE table_schema = 'public'
200 AND table_name IN ('customer_users', 'customer_sessions', 'customer_audit_log')
204 console.log('\nCustomer Portal tables:');
205 tableCheck.rows.forEach(row => {
206 console.log(` ✓ ${row.table_name}`);
209 // Check customer_users count
210 const userCount = await client.query('SELECT COUNT(*) FROM customer_users');
211 console.log(`\nCurrent customer portal users: ${userCount.rows[0].count}`);
215 await client.query('ROLLBACK');
216 console.error('\n❌ Migration failed:', error.message);
217 console.error(error.stack);