Files
Investidor-V2/app/Console/Commands/CheckDocumentLibrarySchema.php
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

117 lines
5.4 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Throwable;
class CheckDocumentLibrarySchema extends Command
{
protected $signature = 'portal:check-document-library-schema';
protected $description = 'Confere o esquema e as migrações da biblioteca documental antes da activação';
public function handle(): int
{
$reference = (string) Str::uuid();
try {
$required = [
'portal_resources' => [
'id', 'document_id', 'title', 'category', 'topic', 'legislation', 'essential', 'published',
'source_reference', 'rights_status', 'description', 'document_type', 'document_number',
'document_date', 'source_url', 'language', 'version_label', 'document_group_id',
'applicable_from', 'applicable_until',
'territorial_scope', 'territory_id', 'source_confirmation', 'legal_status', 'last_reviewed_at',
'document_identity_key', 'duplicate_of_resource_id', 'metadata_version', 'created_by',
'last_reviewed_by', 'independent_review',
],
'documents' => [
'id', 'status', 'data_classification', 'sha256', 'deleted_at', 'file_name', 'media_type',
'storage_key', 'size_bytes', 'version_no', 'issuer', 'issued_on', 'valid_until', 'uploaded_by',
'rights_status', 'original_source', 'malware_scan_status', 'malware_scanned_sha256',
'malware_engine', 'malware_engine_version', 'malware_signatures_at', 'malware_scanned_at',
'malware_failure_reason', 'malware_scan_attempts',
],
'portal_resource_links' => ['id', 'resource_id', 'target_type', 'target_key', 'relationship', 'applicability'],
'portal_resource_revisions' => ['id', 'resource_id', 'revision_no', 'change_type', 'snapshot'],
'document_malware_scans' => ['id', 'document_id', 'file_sha256', 'result', 'engine', 'engine_version', 'started_at'],
];
$missing = [];
foreach ($required as $table => $columns) {
if (! Schema::hasTable($table)) {
$missing[] = "tabela {$table}";
continue;
}
foreach (array_diff($columns, Schema::getColumnListing($table)) as $column) {
$missing[] = "{$table}.{$column}";
}
}
$requiredMigrations = [
'2026_10_05_120000_extend_portal_document_catalogue',
'2026_10_05_130000_add_document_malware_scanning',
];
if (! Schema::hasTable('migrations')) {
$missing[] = 'repositório de migrações';
} else {
$ran = DB::table('migrations')->whereIn('migration', $requiredMigrations)->pluck('migration')->all();
foreach (array_diff($requiredMigrations, $ran) as $migration) {
$missing[] = "migração {$migration}";
}
}
if ($missing !== []) {
$this->error('Esquema da biblioteca incompatível. Não activar o catálogo; faltam elementos de esquema ou migrações.');
foreach ($missing as $item) {
$this->line('- '.$item);
}
return self::FAILURE;
}
foreach ([
['portal_resources', 'rights_status', 'unconfirmed'],
['documents', 'rights_status', 'unconfirmed'],
['documents', 'malware_scan_status', 'pending'],
] as [$table, $column, $expectedDefault]) {
$default = DB::selectOne(
'select column_default from information_schema.columns where table_schema = current_schema() and table_name = ? and column_name = ?',
[$table, $column],
)?->column_default;
if (! is_string($default) || ! str_contains(strtolower($default), $expectedDefault)) {
$this->error("Valor predefinido inseguro ou ausente em {$table}.{$column}; preservar os registos e rever a migração.");
return self::FAILURE;
}
}
$invalidCleanRecords = DB::table('documents')->where('malware_scan_status', 'clean')
->where(function ($query): void {
$query->whereNull('malware_scanned_sha256')->orWhereColumn('malware_scanned_sha256', '!=', 'sha256');
})->count();
if ($invalidCleanRecords > 0) {
$this->error('Existem documentos marcados como limpos sem correspondência do hash analisado.');
return self::FAILURE;
}
$this->info('Esquema da biblioteca compatível; verificações de direitos e análise antimalware disponíveis.');
return self::SUCCESS;
} catch (Throwable $exception) {
Log::error('Document library schema compatibility check failed.', ['reference' => $reference, 'exception' => $exception]);
$this->error('Não foi possível verificar a compatibilidade do esquema. Consulte o registo interno. Referência: '.$reference);
return self::FAILURE;
}
}
}