PSA/server/scripts/bypass-tenant-onboarding.ts
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

172 lines
6.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* CLI script to bypass tenant onboarding by setting onboarding_skipped flag to true
* for all tenant records in the tenant_settings table
*/
import knex from 'knex';
import { parse } from 'ts-command-line-args';
import * as dotenv from 'dotenv';
import * as path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Load environment variables
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
interface Args {
tenant?: string;
dryRun?: boolean;
help?: boolean;
}
const args = parse<Args>(
{
tenant: { type: String, optional: true, description: 'Specific tenant ID to update (optional, updates all if not provided)' },
dryRun: { type: Boolean, optional: true, alias: 'd', description: 'Preview changes without applying them' },
help: { type: Boolean, optional: true, alias: 'h', description: 'Show help' }
},
{
helpArg: 'help',
headerContentSections: [
{
header: 'Bypass Tenant Onboarding',
content: 'Sets onboarding_skipped flag to true for tenant records in tenant_settings table'
}
],
argv: process.argv.slice(2)
}
);
async function main() {
// Create database connection
// For local development, use localhost instead of Docker service names
const isDocker = process.env.DOCKER_ENV === 'true';
const dbHost = isDocker ? (process.env.PGBOUNCER_HOST || 'pgbouncer') : 'localhost';
const dbPort = isDocker ? (process.env.PGBOUNCER_PORT || '6432') : '5432';
// Try to read password from secrets file if available
let dbPassword = process.env.DB_PASSWORD_ADMIN || process.env.POSTGRES_PASSWORD || 'abcd1234!';
try {
const fs = await import('fs');
const secretsPath = path.resolve(__dirname, '../../secrets/postgres_password');
if (fs.existsSync(secretsPath)) {
dbPassword = fs.readFileSync(secretsPath, 'utf8').trim();
}
} catch (error) {
// Fall back to environment variables
}
const db = knex({
client: 'pg',
connection: {
host: dbHost,
port: parseInt(dbPort),
database: process.env.DB_NAME_SERVER || 'server',
user: process.env.DB_USER || 'postgres',
password: dbPassword
}
});
try {
console.log('🔍 Checking tenant_settings table...\n');
// Build query conditions
let query = db('tenant_settings');
if (args.tenant) {
query = query.where('tenant', args.tenant);
console.log(`📋 Targeting specific tenant: ${args.tenant}`);
} else {
console.log('📋 Targeting all tenants');
}
// Get current state
const currentRecords = await query.select('tenant', 'onboarding_skipped', 'onboarding_completed');
if (currentRecords.length === 0) {
console.log('⚠️ No tenant_settings records found');
if (args.tenant) {
console.log(` Tenant '${args.tenant}' may not exist`);
}
return;
}
console.log(`\n📊 Found ${currentRecords.length} tenant_settings record(s):`);
console.log('┌─────────────────────────────────────────┬─────────────────────┬────────────────────┐');
console.log('│ Tenant ID │ Onboarding Skipped │ Onboarding Complete│');
console.log('├─────────────────────────────────────────┼─────────────────────┼────────────────────┤');
currentRecords.forEach(record => {
const tenantId = record.tenant.padEnd(39);
const skipped = (record.onboarding_skipped ? 'true' : 'false').padEnd(19);
const completed = (record.onboarding_completed ? 'true' : 'false').padEnd(18);
console.log(`${tenantId}${skipped}${completed}`);
});
console.log('└─────────────────────────────────────────┴─────────────────────┴────────────────────┘');
// Filter records that need updating
const recordsToUpdate = currentRecords.filter(record => !record.onboarding_skipped);
if (recordsToUpdate.length === 0) {
console.log('\n✅ All tenant records already have onboarding_skipped=true');
return;
}
console.log(`\n🎯 ${recordsToUpdate.length} record(s) need to be updated:`);
recordsToUpdate.forEach(record => {
console.log(`${record.tenant} (currently: onboarding_skipped=${record.onboarding_skipped})`);
});
if (args.dryRun) {
console.log('\n🔍 DRY RUN MODE - No changes will be made');
console.log(' Run without --dry-run to apply changes');
return;
}
// Apply updates
console.log('\n🔧 Updating tenant_settings records...');
const updateQuery = db('tenant_settings')
.update({
onboarding_skipped: true,
updated_at: db.fn.now()
});
if (args.tenant) {
updateQuery.where('tenant', args.tenant);
}
// Only update records where onboarding_skipped is currently false
updateQuery.where('onboarding_skipped', false);
const updatedCount = await updateQuery;
console.log(`\n✅ Successfully updated ${updatedCount} tenant_settings record(s)`);
console.log(' All targeted tenants now have onboarding_skipped=true');
// Verify the changes
const verifyRecords = await db('tenant_settings')
.select('tenant', 'onboarding_skipped', 'updated_at')
.where(args.tenant ? { tenant: args.tenant } : {})
.orderBy('updated_at', 'desc');
console.log('\n📋 Final state verification:');
verifyRecords.forEach(record => {
const timestamp = new Date(record.updated_at).toISOString();
console.log(`${record.tenant}: onboarding_skipped=${record.onboarding_skipped} (updated: ${timestamp})`);
});
} catch (error) {
console.error('\n❌ Failed to update tenant onboarding settings:', error);
process.exit(1);
} finally {
await db.destroy();
}
}
main();