bootstrap($app); (new LoadConfiguration)->bootstrap($app); $c = $app->make('config')->get('database.connections.'.$app->make('config')->get('database.default')); if (($c['driver'] ?? '') !== 'pgsql' || ! in_array($c['url'] ?? null, [null, ''], true)) { throw new RuntimeException('Explicit PostgreSQL target required'); } $pdo = new PDO('pgsql:host='.$c['host'].';port='.$c['port'].';dbname='.$c['database'], $c['username'], $c['password'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); $pdo->exec('BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY'); try { $result = ['captured_at' => gmdate('c'), 'identity' => $pdo->query("SELECT current_database() AS database,inet_server_port() AS port,current_setting('transaction_read_only') AS read_only")->fetch(PDO::FETCH_ASSOC), 'tables' => [], 'active_writes' => 0]; $tables = $pdo->query("SELECT table_schema,table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema NOT IN ('information_schema','pg_catalog') AND table_schema NOT LIKE 'pg_%' ORDER BY table_schema,table_name")->fetchAll(PDO::FETCH_ASSOC); foreach ($tables as $t) { $qualified = '"'.str_replace('"', '""', $t['table_schema']).'"."'.str_replace('"', '""', $t['table_name']).'"'; $q = $pdo->prepare('SELECT column_name,data_type FROM information_schema.columns WHERE table_schema=? AND table_name=? ORDER BY ordinal_position'); $q->execute([$t['table_schema'], $t['table_name']]); $columns = $q->fetchAll(PDO::FETCH_KEY_PAIR); $h = hash_init('sha256'); $count = 0; foreach ($pdo->query('SELECT row_to_json(t)::text AS row FROM '.$qualified.' t ORDER BY row_to_json(t)::text') as $r) { if ($count++) { hash_update($h, "\n"); } hash_update($h, $r['row']); } $entry = ['rows' => $count, 'sha256' => hash_final($h), 'columns' => $columns]; foreach (['status', 'data_classification', 'malware_scan_status', 'location_type'] as $field) { if (isset($columns[$field])) { $entry['groups'][$field] = $pdo->query('SELECT "'.$field.'", count(*) AS count FROM '.$qualified.' GROUP BY "'.$field.'" ORDER BY "'.$field.'"')->fetchAll(PDO::FETCH_ASSOC); } } $result['tables'][$t['table_schema'].'.'.$t['table_name']] = $entry; } $result['migrations'] = $pdo->query('SELECT migration,batch FROM public.migrations ORDER BY id')->fetchAll(PDO::FETCH_ASSOC); $result['installation_events'] = $pdo->query("SELECT action,count(*) AS count,max(occurred_at) AS last_at FROM public.audit_events WHERE action LIKE '%installed%' GROUP BY action ORDER BY action")->fetchAll(PDO::FETCH_ASSOC); $result['opportunity_classifications'] = $pdo->query("SELECT DISTINCT ON(after_data->>'opportunity_id') after_data->>'opportunity_id' AS id,after_data->>'classification' AS classification FROM public.audit_events WHERE source_system='opportunity-origin-review' ORDER BY after_data->>'opportunity_id',occurred_at DESC,id DESC")->fetchAll(PDO::FETCH_ASSOC); $result['geo_with_geometry'] = $result['tables']['public.geo_geometries']['rows'] ?? null; $result['origin_counts'] = $pdo->query("SELECT metadata->>'declared_origin_country_code' AS code,count(*) AS count FROM public.interest_requests WHERE deleted_at IS NULL GROUP BY metadata->>'declared_origin_country_code'")->fetchAll(PDO::FETCH_ASSOC); $result['features'] = ['education' => $app->make('config')->get('education.enabled'), 'suitecrm' => $app->make('config')->get('suitecrm.enabled'), 'ai' => $app->make('config')->get('investmoz_ai.enabled')]; } finally { $pdo->exec('ROLLBACK'); } $label = $argv[1] ?? 'before'; if (! in_array($label, ['before', 'after'], true)) { throw new RuntimeException('before/after label required'); } $dir = dirname(__DIR__).'/storage/app/private/investmoz-v2/portal-audit'; if (! is_dir($dir)) { mkdir($dir, 0700, true); } $destination = $dir.'/estado-ativo-'.$label.'.json'; if (is_file($destination)) { if ($label === 'before') { throw new RuntimeException('Initial audit snapshot already exists; preserve it and use after'); } $archive = $dir.'/estado-ativo-after-'.gmdate('Ymd_His').'-'.bin2hex(random_bytes(3)).'.json'; if (! copy($destination, $archive)) { throw new RuntimeException('Cannot preserve previous audit snapshot'); } } file_put_contents($destination, json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); echo json_encode(['captured_at' => $result['captured_at'], 'identity' => $result['identity'], 'tables' => count($tables), 'geo_with_geometry' => $result['geo_with_geometry'], 'installation_events' => $result['installation_events'], 'features' => $result['features'], 'active_writes' => 0], JSON_THROW_ON_ERROR).PHP_EOL;