/** * Migration Example: Applying Centralized RLS Policies * * This migration demonstrates how to replace vertical-specific RLS policies * with the centralized shared library policies. * * BEFORE: Each vertical had duplicated RLS functions and policies * AFTER: Use shared-libs centralized RLS policies */ import { Pool } from 'pg'; import { applyCompleteRlsPolicies, applyCompletePoliciesForSchema, batchApplyRlsPolicies, getSchemaRlsStatus, } from '@erp-suite/core'; /** * Migration UP: Apply RLS policies to all tables */ export async function up(pool: Pool): Promise { console.log('Starting RLS migration...'); // STEP 1: Install RLS helper functions (run once per database) console.log('Installing RLS helper functions...'); // Note: This would be done via SQL file first: // psql -d database -f apps/shared-libs/core/database/policies/rls-policies.sql // STEP 2: Apply RLS to ERP Core tables console.log('Applying RLS to ERP Core tables...'); const coreSchemas = [ { schema: 'core', tables: ['partners', 'addresses', 'product_categories', 'tags', 'sequences', 'attachments', 'notes'], }, { schema: 'inventory', tables: ['products', 'warehouses', 'locations', 'lots', 'pickings', 'stock_moves'], }, { schema: 'sales', tables: ['sales_orders', 'quotations', 'pricelists'], }, { schema: 'purchase', tables: ['purchase_orders', 'rfqs', 'vendor_pricelists'], }, { schema: 'financial', tables: ['accounts', 'invoices', 'payments', 'journal_entries'], }, ]; for (const { schema, tables } of coreSchemas) { await applyCompletePoliciesForSchema(pool, schema, tables); console.log(` Applied RLS to ${schema} schema (${tables.length} tables)`); } // STEP 3: Apply RLS to vertical-specific tables console.log('Applying RLS to vertical tables...'); // Example: Construccion vertical const construccionTables = [ { schema: 'construction', table: 'projects', tenantColumn: 'constructora_id' }, { schema: 'construction', table: 'estimates', tenantColumn: 'constructora_id' }, { schema: 'construction', table: 'work_orders', tenantColumn: 'constructora_id' }, { schema: 'hse', table: 'incidents', tenantColumn: 'constructora_id' }, ]; await batchApplyRlsPolicies(pool, construccionTables); console.log(` Applied RLS to construccion vertical (${construccionTables.length} tables)`); // Example: Mecanicas-diesel vertical const mecanicasTables = [ { schema: 'mechanics', table: 'work_orders' }, { schema: 'mechanics', table: 'vehicles' }, { schema: 'mechanics', table: 'parts' }, ]; await batchApplyRlsPolicies(pool, mecanicasTables); console.log(` Applied RLS to mecanicas-diesel vertical (${mecanicasTables.length} tables)`); // STEP 4: Verify RLS application console.log('Verifying RLS policies...'); const coreStatus = await getSchemaRlsStatus(pool, 'core'); const enabledCount = coreStatus.filter(s => s.rlsEnabled).length; console.log(` Core schema: ${enabledCount}/${coreStatus.length} tables have RLS enabled`); console.log('RLS migration completed successfully!'); } /** * Migration DOWN: Remove RLS policies */ export async function down(pool: Pool): Promise { console.log('Rolling back RLS migration...'); // Import cleanup functions const { dropAllRlsPolicies, disableRls } = await import('@erp-suite/core'); // Remove RLS from all tables const schemas = ['core', 'inventory', 'sales', 'purchase', 'financial', 'construction', 'hse', 'mechanics']; for (const schema of schemas) { const tablesQuery = ` SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE' `; const result = await pool.query(tablesQuery, [schema]); for (const row of result.rows) { const table = row.table_name; try { const count = await dropAllRlsPolicies(pool, schema, table); if (count > 0) { await disableRls(pool, schema, table); console.log(` Removed RLS from ${schema}.${table} (${count} policies)`); } } catch (error) { console.error(` Error removing RLS from ${schema}.${table}:`, error.message); } } } console.log('RLS migration rollback completed!'); } /** * Example usage in a migration framework */ export default { up, down, }; // TypeORM migration class format export class ApplyRlsPolicies1234567890123 { public async up(queryRunner: any): Promise { // Get pool from queryRunner or create one const pool = queryRunner.connection.driver.master; await up(pool); } public async down(queryRunner: any): Promise { const pool = queryRunner.connection.driver.master; await down(pool); } }