Backup, restauração, export e saúde dos dados¶
Escopo¶
modules/storage/backup.py cria arquivos ZIP portáteis com manifesto e checksum. modules/storage/health.py executa verificações somente leitura. As mesmas capacidades são expostas por CLI e pela API local, com proteções adicionais no fluxo HTTP.
Backup e export¶
Comandos:
python scripts/backup_data.py
python scripts/backup_data.py --export
python scripts/backup_data.py --data-dir data --output caminho/arquivo.zip
Sem --output, os nomes são:
data/backups/sotuhire-data-backup-YYYYMMDD-HHMMSS.zip
data/backups/sotuhire-data-export-YYYYMMDD.zip
Backup e export usam o mesmo formato. O campo kind e o nome distinguem a finalidade.
Manifesto¶
Cada ZIP inclui manifest.json com:
format_version
kind
app_version
schema_version
max_supported_schema_version
created_at
files[]: path, size, sha256
excluded_categories
excluded_files
O banco SQLite é copiado pela API de backup do próprio SQLite antes de entrar no ZIP. Isso evita copiar um arquivo em estado intermediário enquanto WAL/transações estão ativos.
schema_version vem do histórico do banco copiado; vale 0 quando não existe banco. max_supported_schema_version registra a versão máxima entendida pelo aplicativo que gerou o arquivo.
Arquivos elegíveis¶
Extensões aceitas:
.json .jsonl .db .sqlite .sqlite3 .toml .yaml .yml
Diretórios/categorias excluídos incluem:
secrets, secret, credentials, tokens, cookies
scraping-cache, backups, __pycache__
Também são excluídos:
- nomes contendo
api-key,apikey,credential,secret,tokenoucookie; - arquivos ocultos e nomes com
.local.; - arquivos textuais que correspondam a formatos de chave Gemini/OpenAI ou atribuições de credencial/autorização/token;
- arquivos fora da lista de extensões permitidas.
chrome.storage, IndexedDB e storage de terceiros não ficam no diretório de dados e não são coletados.
Limite importante¶
O filtro de conteúdo não inspeciona internamente bancos SQLite. O banco entra inteiro no arquivo; por isso o schema e os serviços não devem persistir chaves. AiRunStore rejeita metadados com aparência de segredo, mas essa defesa não substitui a regra de nunca gravar credenciais em entidades genéricas.
Restauração por CLI¶
Validação sem alterar dados:
python scripts/restore_data.py data/backups/sotuhire-data-backup-YYYYMMDD-HHMMSS.zip
Aplicação explícita:
python scripts/restore_data.py data/backups/sotuhire-data-backup-YYYYMMDD-HHMMSS.zip --apply
Antes de aplicar, o restore verifica:
- existência e validade do
manifest.json; - compatibilidade: o schema do arquivo não pode ser mais novo que o suportado;
- caminhos relativos sem
..ou raiz absoluta; - categoria/extensão permitida;
- presença de cada arquivo declarado;
- SHA-256 de cada conteúdo.
No modo padrão, retorna apenas files_validated; files_restored permanece zero. Com --apply, cria backup preventivo quando o destino já contém dados, extrai em staging temporário e substitui cada arquivo por meio de um arquivo .restore.
A substituição é atômica por arquivo, não por diretório inteiro. Se o sistema operacional falhar no meio de vários arquivos, use pre_restore_backup para recuperar o estado anterior.
API local¶
Endpoints:
GET /api/v1/data/health
GET /api/v1/data/backups
POST /api/v1/data/backups
GET /api/v1/data/backups/{archive_name}
POST /api/v1/data/restore
Criação:
{ "kind": "backup" }
ou:
{ "kind": "export" }
A API não aceita destino arbitrário. Ela cria e lista apenas arquivos em <data-dir>/backups. As respostas expõem archive_name, tamanho, versão, schema, quantidade de arquivos e URL de download — nunca o caminho absoluto local. O download usa Cache-Control: no-store.
A listagem aceita somente nomes SotuHire cujo manifesto possa ser lido. O checksum completo é validado na etapa de restore.
Restore HTTP em duas etapas¶
Dry-run padrão:
{
"archive_name": "sotuhire-data-backup-YYYYMMDD-HHMMSS.zip"
}
Aplicação:
{
"archive_name": "sotuhire-data-backup-YYYYMMDD-HHMMSS.zip",
"apply": true,
"confirmation": "RESTAURAR"
}
O nome precisa obedecer ao padrão SotuHire, ser apenas basename e resolver dentro do diretório gerenciado. A palavra de confirmação é exata. O frontend /privacy exige primeiro o dry-run e abre uma confirmação final antes de aplicar.
No modo Demo, a interface simula o fluxo sem criar, baixar ou restaurar arquivos reais.
Data health¶
Comando:
python scripts/check_data_health.py
python scripts/check_data_health.py --data-dir data --database data/sotuhire.db
O relatório traz checked_at, caminhos locais no CLI, versão do schema, healthy, contagens e issues. A resposta HTTP remove caminhos absolutos.
Verificações dos stores legados¶
- JSON/JSONL inválido;
- IDs duplicados no mesmo store;
- memória sem
source_ref,source_refsousource_id; - datas inválidas em campos conhecidos;
- contagem por arquivo reconhecido.
Verificações SQLite¶
- versão atual versus
LATEST_SCHEMA_VERSIONe consistência entremigration_historyeschema_metadata; - banco inválido ou ilegível, convertido em issue
database_unreadableem vez de erro HTTP não tratado; - histórico e validação de todas as migrações;
foreign_key_checkeintegrity_check;- contagem por tabela;
- datas inválidas em colunas terminadas em
_at; - candidatura sem
job_snapshot_id; - oportunidade sem origem;
- oportunidade ou edital sem snapshot;
- hashes de snapshot repetidos em registros distintos;
- AiRun sem provider/modo ou fallback sem motivo.
Severidade¶
| Severidade | Efeito em healthy |
Exemplo |
|---|---|---|
info |
não altera | banco ainda não criado |
warning |
não altera | candidatura legada sem snapshot |
error |
define healthy=false |
JSON/banco ilegível, schema divergente, falha de integridade |
Um relatório saudável pode conter warnings. O health check não corrige, apaga ou mescla registros.
Limitações do health check¶
- a inspeção legada é estrutural e não valida toda regra de negócio de cada módulo;
- o flatten de JSON cobre raiz e listas de primeiro nível, não qualquer objeto profundamente aninhado;
- alguns conjuntos de issues são limitados aos primeiros 20 exemplos;
- o hash armazenado não é recalculado para todo registro genérico;
- warnings precisam de revisão humana; não autorizam merge ou descarte automático.
Ordem operacional recomendada¶
health read-only
→ migration dry-run
→ revisar rejeições/duplicatas/warnings
→ backup explícito
→ apply autorizado
→ verify
→ health novamente
→ teste de restore em diretório temporário
Consulte também o checklist de migração limpa.
Testes¶
pytest tests/test_storage_backup_restore.py
pytest tests/test_api_data_reliability.py
cd apps/web && npx playwright test tests/e2e/data-reliability.spec.ts --project=chromium
Esses testes usam dados temporários/fictícios. Eles não significam que um restore ou --apply tenha sido executado sobre dados reais.