EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
migrate-customer-portal.js
Go to the documentation of this file.
1require('dotenv').config();
2const { Pool } = require('pg');
3
4// Parse DATABASE_URL from environment
5const DATABASE_URL = process.env.DATABASE_URL || 'postgresql://user:password@localhost:5432/rmm_psa';
6
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 : ''));
9
10const pool = new Pool({
11 connectionString: cleanedUrl,
12 ssl: {
13 rejectUnauthorized: false
14 }
15});
16
17async function migrate() {
18 const client = await pool.connect();
19
20 try {
21 console.log('Starting customer portal database migration...\n');
22
23 // Start transaction
24 await client.query('BEGIN');
25
26 // 1. Create customer_users table
27 console.log('Creating customer_users table...');
28 await client.query(`
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),
37 phone VARCHAR(50),
38 is_primary BOOLEAN DEFAULT false,
39 is_active BOOLEAN DEFAULT true,
40 last_login TIMESTAMP,
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)
47 ON DELETE CASCADE,
48 CONSTRAINT fk_customer_users_customer
49 FOREIGN KEY (customer_id)
50 REFERENCES customers(customer_id)
51 ON DELETE CASCADE
52 )
53 `);
54 console.log('✅ customer_users table created');
55
56 // Create indexes for customer_users
57 console.log('Creating indexes for customer_users...');
58 await client.query(`
59 CREATE INDEX IF NOT EXISTS idx_customer_users_tenant
60 ON customer_users(tenant_id)
61 `);
62 await client.query(`
63 CREATE INDEX IF NOT EXISTS idx_customer_users_customer
64 ON customer_users(customer_id)
65 `);
66 await client.query(`
67 CREATE INDEX IF NOT EXISTS idx_customer_users_email
68 ON customer_users(email)
69 `);
70 console.log('✅ Indexes created for customer_users\n');
71
72 // 2. Create customer_sessions table
73 console.log('Creating customer_sessions table...');
74 await client.query(`
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),
80 user_agent TEXT,
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)
86 ON DELETE CASCADE
87 )
88 `);
89 console.log('✅ customer_sessions table created');
90
91 // Create indexes for customer_sessions
92 console.log('Creating indexes for customer_sessions...');
93 await client.query(`
94 CREATE INDEX IF NOT EXISTS idx_customer_sessions_user
95 ON customer_sessions(customer_user_id)
96 `);
97 await client.query(`
98 CREATE INDEX IF NOT EXISTS idx_customer_sessions_token
99 ON customer_sessions(token_hash)
100 `);
101 await client.query(`
102 CREATE INDEX IF NOT EXISTS idx_customer_sessions_expires
103 ON customer_sessions(expires_at)
104 `);
105 console.log('✅ Indexes created for customer_sessions\n');
106
107 // 3. Create customer_audit_log table
108 console.log('Creating customer_audit_log table...');
109 await client.query(`
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),
116 entity_id INTEGER,
117 details JSONB,
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)
123 ON DELETE CASCADE,
124 CONSTRAINT fk_customer_audit_user
125 FOREIGN KEY (customer_user_id)
126 REFERENCES customer_users(customer_user_id)
127 ON DELETE SET NULL
128 )
129 `);
130 console.log('✅ customer_audit_log table created');
131
132 // Create indexes for customer_audit_log
133 console.log('Creating indexes for customer_audit_log...');
134 await client.query(`
135 CREATE INDEX IF NOT EXISTS idx_customer_audit_tenant
136 ON customer_audit_log(tenant_id)
137 `);
138 await client.query(`
139 CREATE INDEX IF NOT EXISTS idx_customer_audit_user
140 ON customer_audit_log(customer_user_id)
141 `);
142 await client.query(`
143 CREATE INDEX IF NOT EXISTS idx_customer_audit_created
144 ON customer_audit_log(created_at)
145 `);
146 console.log('✅ Indexes created for customer_audit_log\n');
147
148 // 4. Add columns to customers table
149 console.log('Checking customers table for missing columns...');
150
151 // Check if allow_portal_access exists
152 const portalAccessCheck = await client.query(`
153 SELECT column_name
154 FROM information_schema.columns
155 WHERE table_name = 'customers'
156 AND column_name = 'allow_portal_access'
157 `);
158
159 if (portalAccessCheck.rows.length === 0) {
160 await client.query(`
161 ALTER TABLE customers
162 ADD COLUMN allow_portal_access BOOLEAN DEFAULT true
163 `);
164 console.log('✅ Added allow_portal_access column to customers');
165 } else {
166 console.log('⏭️ allow_portal_access column already exists');
167 }
168
169 // Check if portal_notes exists
170 const portalNotesCheck = await client.query(`
171 SELECT column_name
172 FROM information_schema.columns
173 WHERE table_name = 'customers'
174 AND column_name = 'portal_notes'
175 `);
176
177 if (portalNotesCheck.rows.length === 0) {
178 await client.query(`
179 ALTER TABLE customers
180 ADD COLUMN portal_notes TEXT
181 `);
182 console.log('✅ Added portal_notes column to customers');
183 } else {
184 console.log('⏭️ portal_notes column already exists');
185 }
186
187 console.log();
188
189 // Commit transaction
190 await client.query('COMMIT');
191
192 console.log('✅ Migration completed successfully!\n');
193
194 // Verify tables exist
195 console.log('Verifying customer portal tables...');
196 const tableCheck = await client.query(`
197 SELECT table_name
198 FROM information_schema.tables
199 WHERE table_schema = 'public'
200 AND table_name IN ('customer_users', 'customer_sessions', 'customer_audit_log')
201 ORDER BY table_name
202 `);
203
204 console.log('\nCustomer Portal tables:');
205 tableCheck.rows.forEach(row => {
206 console.log(` ✓ ${row.table_name}`);
207 });
208
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}`);
212
213 } catch (error) {
214 // Rollback on error
215 await client.query('ROLLBACK');
216 console.error('\n❌ Migration failed:', error.message);
217 console.error(error.stack);
218 process.exit(1);
219 } finally {
220 client.release();
221 await pool.end();
222 }
223}
224
225// Run migration
226migrate();