Files
Investidor-V2/tools/portal-audit-active.php
T
nala 6ddafc95ba
tests / ci (push) Canceled after 0s
oportunidades 4
2026-10-10 07:03:59 +02:00

70 lines
5.1 KiB
PHP

<?php
use Illuminate\Foundation\Bootstrap\LoadConfiguration;
use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables;
// Metadata, counts and hashes only; no active writes or personal records exported.
require dirname(__DIR__).'/vendor/autoload.php';
$app = require dirname(__DIR__).'/bootstrap/app.php';
(new LoadEnvironmentVariables)->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;