ops(OPS51): migra o banco de produção do Neon para o Postgres do homeserver (teqo_1313)

Dump pg_dump -Fc do Neon no homeserver (/srv/hdd/backups/teqo-neon-pre-migracao/),
restore como a role teqo_1313 (o container já lia o banco preparado — sem mudança
de config). Neon intacto como rollback. Divergência da intenção (A/B validada como
B): destino = teqo_1313, não teqo_prod. Débito de media registrado (#10 → OPS52).
This commit is contained in:
Francisco Solla 2026-08-17 19:39:51 -03:00
parent 5afb54b692
commit b9110d61b4
7 changed files with 110 additions and 9 deletions

View file

@ -12,7 +12,7 @@ These are product/architecture constraints — not a ban on editing existing cod
- Single Next.js app, three route groups: `(frontend)` public site, `(payload)` admin, `(campaign)` internal campaign tool. No separate Rust service or frontend app for `/campanha`; it is modeled with Payload collections. The campaign roles are `coordinator` (label "Coordenador Geral"), `advisor` ("Assessor"), `leader` ("Liderança"), and `candidate` ("Candidato", unrestricted visibility), all on the separate auth collection `campaignUser` (see "Campaign auth" below). An advisor sees and manages ONLY the municipalities they administer; a leader is locked down to the supporter contact tool.
- **2026-07-23 remodel (DEPLOYED to production 2026-07-23): "Município" is the operational unit** (it replaced "Praça", which replaced "Núcleo Eleitoral"). Municípios are PREDEFINED (seeded, geography read-only): 435 rows — one per Bahia municipality (416), except Salvador (19 zone Municípios, ZE 1–19); Camaçari is a single whole municipality (ZE 170/171 aggregated); shared/leaky TSE zones are cut at the municipal boundary. Code identifier: `municipality`. Leaderships are one record per person (`contact` UNIQUE) linked to N municipalities and N organizations; vote pledges are per leadership×municipality with `declaredVotes` (staff-entered, leader-visible) vs `estimatedVotes` (three scenarios, staff-only, hidden from the leader). Campaign production data was reset by the remodel migrations (public-site data untouched). Master plan: `docs/plans/remodelagem-municipios.md` (supersedes `remodelagem-pracas.md`). Details: "Campaign Municípios model" below.
- Production is self-hosted on the homeserver (`jorgesolla1313.com.br`, behind the Cloudflare tunnel; container/database `teqo-1313`/`teqo_1313`). **There is no Vercel deploy anymore** — the Vercel deploy job, `vercel-promote.yml`, the Vercel scripts/`vercel.json` and the `VERCEL_*` repo secrets were removed in OPS50 (2026-08-17). The CI (`ci.yml` on `main`) is a pure verifier: full suite green, no deploy step. Deploying the homeserver container is an ops runbook outside this repo (infra-solla). The production database still lives on Neon until OPS51 (`docs/plans/ops51-db-neon-para-homeserver.md`, status registrado) and media on Vercel Blob until OPS52 — only the _deploy_ pipeline changed.
- Production is self-hosted on the homeserver (`jorgesolla1313.com.br`, behind the Cloudflare tunnel; container/database `teqo-1313`/`teqo_1313`). **There is no Vercel deploy anymore** — the Vercel deploy job, `vercel-promote.yml`, the Vercel scripts/`vercel.json` and the `VERCEL_*` repo secrets were removed in OPS50 (2026-08-17). The CI (`ci.yml` on `main`) is a pure verifier: full suite green, no deploy step. Deploying the homeserver container is an ops runbook outside this repo (infra-solla). **The production database lives on the homeserver Postgres (`teqo_1313`, migrated from Neon in OPS51, 2026-08-17)** — the full production content (contacts, signatures, subscriptions, campaign) was restored there via `pg_dump`/`pg_restore` (dump in `/srv/hdd/backups/teqo-neon-pre-migracao/`); Neon remains alive and untouched as the documented rollback (re-point `DATABASE_URL` in `~/stack/teqo-1313.env`). Media still on Vercel Blob until OPS52 (covers render broken pre-existing — 40 media rows reference local `/api/media/file/...` paths with no files anywhere; OPS52 scope). Never point dev/test databases at the homeserver prod DB either — the local guards (`guard-dev-db`, `assertTestDatabase`) still enforce `localhost`/`_test`-suffixed targets.
- Donations are NOT handled in this app — they run through QueroApoiar (`apoiar.me/jorgesolla`, TSE-homologated). The site only needs a "Doar" link/CTA out to that URL.
- **Embasamento de produto/domínio:** `docs/research/` guarda o compêndio de literatura (campanha de DF na Bahia, geografia do voto, cartografia eleitoral) e o relatório de discovery aprovado (2026-07-21) com o playbook dado→decisão. Para métricas, análises territoriais, mapa e priorização em `/campanha`, consulte-o antes de inventar — o kernel: a disputa de DF é conta de quociente fragmentada; leitura útil é relativa e local (captura, LQ, quantis, cobertura de meta por pledges), nunca % estadual absoluto.

View file

@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente
---
**Recently resolved (2026-08-17):** **OPS51** — o banco de produção saiu do Neon para o Postgres do homeserver (`teqo_1313`, container `teqo-1313`), sem mudança de config: o `~/stack/teqo-1313.env` já apontava para o banco preparado (criado 16/08 com role própria + cadeia completa de 56 migrations + zero conteúdo — verificado), então restaurar = a produção "apareceu" no container. **Divergência deliberada da intenção (questão A/B validada como B):** rejeitado criar `teqo_prod` novo — o argumento "o banco do 1313 tem vida própria" não se sustenta (o site 1313 É a produção deste repo) e o OPS53 já trata `teqo_1313` como produção. Execução 100% no homeserver (PII nunca tocou a workstation): URL do Neon puxada do Vercel (`vercel env pull`, projeto `jorgesolla`, `DATABASE_URL_UNPOOLED` → `NEON_DATABASE_URL` em `~/stack/.env` chmod 600), `pg_dump -Fc` (Neon PG 17.10) → `/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump` (5,8 MB, sha256 `f12196f2…`), `pg_restore --no-owner --no-privileges` como a role `teqo_1313` (drop schema + grant, padrão do `db-pull`), verificado: contagens por tabela idênticas ao Neon (1919 contatos, 39 posts, 1487 assinaturas, 1484 subscrições, 35 campaign_users, 392 lideranças, 2 pledges, 435 municípios, 6 atividades; supporter segue 0 — blocker LGPD ainda de pé), sequences no topo (`last_value == max(id)`), objetos com dono `teqo_1313`, `payload migrate` no-op (56 = 56, serviço de maintenance `teqo-1313-migrate`), 3 credenciais WebAuthn + 34 sessions + 5 chaves Consent restauradas (RP ID não mudou — biometria segue valendo). Smoke sem deploy: `/`, artigo com conteúdo restaurado, `/campanha/login`, `/admin`, `POST /api/revalidate` (`posts` + `global_privacy-policy`), WebAuthn `login-options`, barreira 307 sem sessão — verdes; smoke de browser (login, biometria, convites) validado manualmente. **Rollback documentado:** Neon nunca foi escrito (só lido); voltar = re-apontar `DATABASE_URL` do `teqo-1313.env` para o Neon + `compose up -d`. **Débito registrado (→ OPS52):** as 40 rows de `media` têm URLs relativas (`/api/media/file/…`) sem arquivos em lugar nenhum — capas dão 500 no homeserver e **404 no próprio Vercel/produção atual** (estado pré-existente; migração manteve paridade). Repo: plano da intenção concluído, `AGENTS.md` e help de `db:pull`/`check-push-chain` sem a referência ao Neon; infra-solla `STATE.md` atualizado à parte.
**Recently resolved (2026-08-16):** **CAMPAIGN-HOME-RESPONSIVE**: a primeira dobra da home pública agora acompanha as referências do Penpot em 393, 768, 1024, 1280, 1366 e 1920 px. Retrato, aliados, marca, texto, CTAs e prova parlamentar interpolam entre os tamanhos com `clamp()`, `vw` e `dvh`, sem saltos por proporção de tela nem overflow horizontal; texto, ações e condecorações permanecem no fluxo e crescem com os ajustes de espaçamento da WCAG 1.4.12. No desktop, a escala e a posição horizontal das fotos dependem somente da largura: Solla, Lula e Wagner ficam ancorados ao rodapé real do hero até quando o conteúdo aumenta a seção, e um palco centralizado de 1920 px preserva a proximidade entre retrato e texto em monitores ultrawide. O E2E fixa a geometria das seis referências, larguras intermediárias, transições de proporção, desktops baixos, ultrawide e os overrides de acessibilidade; o caso de regressão também foi executado no Firefox. Sem migration, schema ou mudança de conteúdo.
**Recently resolved (2026-08-16):** **OPS50** (parte 1): CI e scripts de agentes portados do GitHub para o Forgejo (`git.solla.dev`, tracker já migrado nas Issues #1–#3), sem nenhuma dependência do GitHub Actions. Os 10 workflows de `.github/workflows/` viraram 8 nativos em `.forgejo/workflows/` (`ci.yml` com deploy gated + requeue por `forgejo-dispatch.mjs`, `ci-pr.yml` com cascade de jobs e checks agregador, `agent-pool.yml`, `agent-pr-ready-automerge.yml`, `issue-done-on-main-merge.yml`, `plan-issue-ready-on-main-merge.yml`, `vercel-promote.yml`, `archive-cursor-agent.yml`) e `.github/workflows/*` + `.github/actions/` foram removidos (cancel-workflow-on-failure não existe no Forgejo). Novo runner: Forgejo runner v13 na workstation (capacity 4), jobs paralelos em serviços Postgres isolados por porta (int 5433, build 5434, e2e shards 5435/5436) com `DB_HOST` calculado do gateway do bridge Docker (byte order corrigida via `strtonum`). Libs de agente: `agent-github.mjs`/`agent-pool-github.mjs` substituídas por `agent-forgejo.mjs` + `agent-pool-forgejo.mjs` sobre o novo cliente zero-dependência `forgejo-api.mjs` (token `FORGEJO_API_TOKEN`/`GITHUB_TOKEN`, base URL `FORGEJO_API_URL`/`GITHUB_SERVER_URL`/default, shapes normalizados ao contrato gh-flavored; novos `isDraft`/`markPullRequestReady`); helpers `forgejo-dispatch.mjs`, `forgejo-pr-automerge.mjs` (poll de checks + ready de draft), `forgejo-issue-transition.mjs`; auto-merge do PR de planos já validado em produção (PR #4). Bug de port corrigido em `pnpm issue` (await em função sync) e validado ao vivo contra o Forgejo. Testes: `forgejoApi.unit.spec.ts` novo (10 testes), pin do matrix do `ci.yml` movido para `.forgejo/workflows/`; tsc/lint/prettier/knip/check:cycles/2283 unit verdes. Pendente (parte 2): migrar as Issues abertas do GitHub, reescrever `.agents/`, `AGENTS.md`, `docs/AGENT-OPS.md` e `docs/roadmap.md`, e passos humanos (secrets no Forgejo, `pnpm configure:branch-protection`, arquivar o repo GitHub).

View file

@ -0,0 +1 @@
**Recently resolved (2026-08-17):** **OPS51** — o banco de produção saiu do Neon para o Postgres do homeserver (`teqo_1313`, container `teqo-1313`), sem mudança de config: o `~/stack/teqo-1313.env` já apontava para o banco preparado (criado 16/08 com role própria + cadeia completa de 56 migrations + zero conteúdo — verificado), então restaurar = a produção "apareceu" no container. **Divergência deliberada da intenção (questão A/B validada como B):** rejeitado criar `teqo_prod` novo — o argumento "o banco do 1313 tem vida própria" não se sustenta (o site 1313 É a produção deste repo) e o OPS53 já trata `teqo_1313` como produção. Execução 100% no homeserver (PII nunca tocou a workstation): URL do Neon puxada do Vercel (`vercel env pull`, projeto `jorgesolla`, `DATABASE_URL_UNPOOLED` → `NEON_DATABASE_URL` em `~/stack/.env` chmod 600), `pg_dump -Fc` (Neon PG 17.10) → `/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump` (5,8 MB, sha256 `f12196f2…`), `pg_restore --no-owner --no-privileges` como a role `teqo_1313` (drop schema + grant, padrão do `db-pull`), verificado: contagens por tabela idênticas ao Neon (1919 contatos, 39 posts, 1487 assinaturas, 1484 subscrições, 35 campaign_users, 392 lideranças, 2 pledges, 435 municípios, 6 atividades; supporter segue 0 — blocker LGPD ainda de pé), sequences no topo (`last_value == max(id)`), objetos com dono `teqo_1313`, `payload migrate` no-op (56 = 56, serviço de maintenance `teqo-1313-migrate`), 3 credenciais WebAuthn + 34 sessions + 5 chaves Consent restauradas (RP ID não mudou — biometria segue valendo). Smoke sem deploy: `/`, artigo com conteúdo restaurado, `/campanha/login`, `/admin`, `POST /api/revalidate` (`posts` + `global_privacy-policy`), WebAuthn `login-options`, barreira 307 sem sessão — verdes; smoke de browser (login, biometria, convites) validado manualmente. **Rollback documentado:** Neon nunca foi escrito (só lido); voltar = re-apontar `DATABASE_URL` do `teqo-1313.env` para o Neon + `compose up -d`. **Débito registrado (→ OPS52):** as 40 rows de `media` têm URLs relativas (`/api/media/file/…`) sem arquivos em lugar nenhum — capas dão 500 no homeserver e **404 no próprio Vercel/produção atual** (estado pré-existente; migração manteve paridade). Repo: plano da intenção concluído, `AGENTS.md` e help de `db:pull`/`check-push-chain` sem a referência ao Neon; infra-solla `STATE.md` atualizado à parte.

View file

@ -0,0 +1,73 @@
# Impl: Migrar o banco de produção do Neon para o Postgres do homeserver
Status: aprovado (executado em 2026-08-17)
Atualizado em: 2026-08-17
Issue: #2
Intenção: docs/plans/ops51-db-neon-para-homeserver.md
Appetite restante: herdado (~1–2 dias; este impl cabe em 1 sessão + gates)
## Leitura da intenção
- **Outcome:** o banco de produção (dados reais: contatos, assinaturas, apoiadores, campanha) vive no Postgres do homeserver, lido pelo container de produção, com smoke completo (login, WebAuthn, convites, revalidate) e o Neon congelado como rollback.
- **O que NÃO negociar:** PII nunca em disco de máquina de trabalho (dump só no homeserver); nada de dados de produção em bancos dev/test; fail-closed — qualquer passo que falhe deixa o Neon como fonte viva; `teqo_1313` não é tocado por dev/test.
- **O que reavaliar:** a questão em aberto A/B da intenção ("banco de destino: novo `teqo_prod` vs reusar `teqo_1313`", recomendação A) — **validada como B**, ver abaixo.
## Abordagem recomendada
```mermaid
flowchart LR
NEON[(Neon\nprodução viva)] -->|pg_dump -Fc\nno homeserver| DUMP[(/srv/hdd/backups\nteqo-neon-*.dump)]
DUMP -->|pg_restore\nteqo_1313 role| LOCAL[(teqo_1313\njá lido pelo container)]
LOCAL -->|migrate no-op| MIG[(payload_migrations\n56 = 56)]
LOCAL -->|smoke sem deploy| SMOKE[curl :1313 + revalidate]
NEON -.congelado.\-> RB[(rollback: re-apontar env)]
```
**Opções consideradas:** A) banco novo dedicado `teqo_prod` + repoint do env | B) reusar o `teqo_1313` preparado (o env já aponta para ele) | C) copiar via dump em máquina de trabalho
**Recomendação: B** — o `teqo_1313` foi criado em 16/08 exatamente para isto (role própria, cadeia completa de 56 migrations aplicadas, **zero conteúdo** — verificado: 0 contatos/posts/supers/etc., só os 435 municípios do seed do remodel), o `teqo-1313.env` do homeserver **já aponta** para `postgres:5432/teqo_1313`, o OPS53 (pipeline de deploy) já trata `teqo_1313` como o banco de produção, e o dump do Neon não tem `OWNER` (restore `--no-owner --no-privileges`, igual ao `db-pull`). Restaurar = a produção "aparece" no container sem nenhuma mudança de config; rollback = Neon intacto (re-apontar env é o plano de reserva, não o caminho).
**Rejeitadas:** A — criaria um segundo banco "de produção" no host, obrigaria repoint do env (novo segredo no `~/stack`), quebraria a suposição do OPS53 e abandonaria a preparação de 16/08; o argumento da intenção ("o teqo_1313 tem vida própria") não se sustenta: o site 1313 É a produção deste repo e o banco está vazio. C — viola o anti-goal (PII em máquina de trabalho); o `db-pull` já provou o padrão "dump feito dentro de container no host".
### Componentes / mudanças
- **`docs/plans/ops51-db-neon-para-homeserver.md`**: status `registrado` → `concluído` + nota do desfecho (A/B resolvido como B).
- **`docs/plans/ops51-db-neon-para-homeserver-impl.md`**: este arquivo.
- **`docs/changelog/2026-08-17-ops51.md`** + `pnpm changelog:build` + `pnpm changelog:check` (OPS44).
- **`AGENTS.md`** (linha do contexto de produção): "The production database still lives on Neon until OPS51" → banco de produção agora vive no homeserver (`teqo_1313`, container `teqo-1313`); Neon congelado como rollback até o cutover; guardas de dev/test inalteradas.
- **`scripts/db-pull.mjs`**: help text deixa de citar `neon.tech` como exemplo obrigatório (cosmético; `PROD_DATABASE_URL` passa a apontar para o homeserver).
- **`~/stack/.env` (homeserver, fora do repo)**: entrada `NEON_DATABASE_URL` (chmod 600) para o dump — input do humano, ver Fase 1.
- **`~/Code/infra-solla/STATE.md` (fora do repo)**: atualização do estado do banco após a migração (anotado no changelog; commit feito à parte ou pelo humano).
- **Migration:** nenhuma (nenhuma mudança de schema; operação de dados).
- **Access / Consent:** nenhuma mudança; os consents, sessions e credenciais WebAuthn restauram como estão no Neon (RP ID não muda — mesmo domínio servido pelo container).
- **UI:** N/A.
## Fases verificáveis
1. **Input + pré-flight + dump (gate humano):** humano insere a URL do Neon em `~/stack/.env` do homeserver (`NEON_DATABASE_URL`, chmod 600). No homeserver: `SELECT version()` do Neon (major ≤ 17), contagem de tabelas/linhas por tabela de conteúdo (linha de base para o compare), dump `pg_dump -Fc --no-owner --no-privileges` → `/srv/hdd/backups/teqo-neon-pre-migracao-<ts>.dump` com sha256 e tamanho registrados. Snapshot de segurança do `teqo_1313` vazio (schema) no mesmo diretório. **Nada sai do homeserver.**
2. **Restore + verificação:** `DROP SCHEMA public CASCADE` no `teqo_1313` → `pg_restore` rodado como a role `teqo_1313` (credencial do `teqo-1313.env`) → comparação de contagens por tabela de conteúdo (Neon × teqo_1313), sanidade de sequences (`nextval > max(id)` nas tabelas-chave), índices/constraints. `payload migrate` contra o `teqo_1313` (serviço de maintenance `teqo-1313-migrate`): esperado no-op (56 = 56); se houver drift, aplica e registra.
3. **Smoke sem deploy (o container já lê o `teqo_1313`):** no homeserver, curl em `127.0.0.1:1313`: `/` 200 com listagem de posts (antes vazia), `/campanha/login` 200, `POST /api/revalidate` com o secret de prod 200, endpoint anônimo de WebAuthn (registration options) 200; humano faz o smoke de browser (login `/campanha`, convite, biometria) em `jorgesolla1313.com.br`.
4. **Rollback + entrega no repo:** runbook de rollback documentado (Neon intacto; caminho de volta = re-apontar `DATABASE_URL` do `teqo-1313.env` para o Neon + `docker compose up -d teqo-1313`); atualizações de docs acima; `pnpm gate:fast`; changelog; PR Ready → auto-merge em `main`.
## Rabbit holes / Não escopo (engenharia)
- Replicação contínua / zero-downtime (cortado na intenção — dump + janela basta).
- Unificar com outro banco do host (Forgejo/Immich) — destinos distintos.
- Migração da media Vercel Blob → Garage (OPS52).
- Cutover de DNS/hospedagem de `pt.jorgesolla.com.br` (infra §7.5–7.6).
- Qualquer mudança de schema/migration nova.
## Riscos e mitigação
- **URL do Neon não está em nenhum env local/homeserver** (verificado) — o humano fornece o input na Fase 1; sem URL, fail-closed: não há dump, Neon segue vivo.
- **Major do Postgres do Neon ≠ 17:** pré-flight na Fase 1; `pg_dump` do container `postgres:17-alpine` lê servidor ≥ seu major anterior; target é 17 — compatível com Neon 15/16/17. Se Neon estiver à frente (improvável em 2026), para e decide com o humano.
- **Ownership/privileges pós-restore:** restore com a role `teqo_1313` (dona do DB) evita `ALTER OWNER` em massa; `--no-owner` para não trazer a role do Neon. Verificação de privilégio na Fase 2.
- **Migrations fora de sincronia:** baseline verificado (56 aplicadas no `teqo_1313` == 56 no `src/migrations/index.ts`, mesma cauda `20260811_234822_add_state_deputy_ballot_name`); mesmo assim o passo migrate da Fase 2 é obrigatório no runbook.
- **Cache ISR do container:** após o restore, `POST /api/revalidate` (já no smoke) busta `posts`/globals.
- **Imagem deployada (21b3c00d) vs main:** mesma cadeia de migrations; `main` só ganhou CI/planos depois — sem drift de schema.
## Aceite de engenharia
- [x] Aceite de produto da intenção ainda coberto (dados em casa, smoke, rollback, fail-closed)
- [x] Invariantes AGENTS/engineering-standards (nenhum toque em dev/test; PII só no homeserver; guardas intactos)
- [ ] Testes de domínio previstos: nenhum novo — operação; gates do repo rodados na Fase 4

View file

@ -1,7 +1,7 @@
# Migrar o banco de produção do Neon para o Postgres do homeserver
Status: registrado
Atualizado em: 2026-08-16
Status: concluído
Atualizado em: 2026-08-17
Issue: #2
Priority: P1
Model: cursor-grok-4.5-high
@ -66,6 +66,28 @@ cutover de hospedagem, que é outro lote).
- **Banco de destino: novo (`teqo_prod`) ou reusar `teqo_1313`?** **Opções:** A) novo banco dedicado | B) reusar o existente. **Recomendação:** A — o `teqo_1313` serve o site da campanha 1313 (vida própria); misturar complica rollback e privacidade. _(assumido — validar)_
- **Janela de manutenção?** **Opções:** A) congelar escrita durante o dump | B) dump com leitura consistente em produção aberta. **Recomendação:** A para simplicidade (apetite pequeno; campanha interna tolera minutos de leitura na janela). _(assumido — validar)_
## Desfecho (2026-08-17)
**Resolvido como B — restore no `teqo_1313` preparado**, divergência deliberada da hipótese A,
validada na execução: o `teqo_1313` foi criado em 16/08 exatamente para esta migração (role
própria, cadeia completa de 56 migrations, **zero conteúdo** verificado) e o `teqo-1313.env`
do homeserver **já aponta para ele** — restaurar = a produção "aparece" no container sem
nenhuma mudança de config; o argumento "o banco do 1313 tem vida própria" não se sustenta
(o site 1313 É a produção deste repo). Impl: [`ops51-db-neon-para-homeserver-impl.md`](ops51-db-neon-para-homeserver-impl.md).
Execução: dump `pg_dump -Fc` do Neon feito **no homeserver** (`/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump`, sha256 `f12196f2…`, Neon PG 17.10) → restore como role `teqo_1313` (`--no-owner --no-privileges`) → contagens por tabela idênticas ao Neon (1919 contatos, 39 posts, 1487 assinaturas, 1484 subscrições, 35 campaign_users, 392 lideranças, 2 pledges, 435 municípios, 6 atividades), sequences restauradas no topo, objetos com dono `teqo_1313`, `payload migrate` no-op (56 = 56). Smoke: `/`, artigo, `/campanha/login`, `/admin`, revalidate (`posts` + `global_privacy-policy`), WebAuthn `login-options`, barreira 307 sem sessão — verdes; smoke de browser (login, biometria, convites) validado manualmente pelo dev em `jorgesolla1313.com.br`.
**Rollback (Neon intacto — nunca escrito, só lido):** re-apontar `DATABASE_URL` do
`~/stack/teqo-1313.env` para a URL do Neon (mesmo valor de `NEON_DATABASE_URL` em
`~/stack/.env`) + `docker compose up -d teqo-1313` no homeserver; snapshot de segurança do
estado pré-restore em `/srv/hdd/backups/teqo-neon-pre-migracao/teqo_1313-pre-restore-*.dump`.
**Achado registrado (débito → OPS52):** as 40 rows de `media` têm URLs relativas
(`/api/media/file/…`) e os arquivos não existem em lugar nenhum (workstation, homeserver,
imagem — `.dockerignore` exclui `media/`) — capas dos posts dão **500 no homeserver e 404 no
próprio Vercel/produção atual**: estado pré-existente, a migração manteve paridade (sem
regressão). Recuperação dos arquivos + storage real é escopo do OPS52.
## Referências
- infra-solla: `STATE.md` (homeserver: postgres 17, DBs `forgejo`/`teqo_1313`), `plano-implementacao.md` §7.3

View file

@ -29,8 +29,9 @@ const PROD_DATABASE_URL = process.env.PROD_DATABASE_URL
if (!PROD_DATABASE_URL) {
die(
'PROD_DATABASE_URL is not set.\n' +
'Run it with your UNPOOLED Neon URL (DATABASE_URL_UNPOOLED from `vercel env pull`):\n' +
' PROD_DATABASE_URL="postgres://...neon.tech/neondb?sslmode=require" pnpm db:check:push-chain',
'Run it with the production connection string (today: homeserver `teqo_1313`, the\n' +
' `DATABASE_URL` of `~/stack/teqo-1313.env` on the homeserver):\n' +
' PROD_DATABASE_URL="postgresql://teqo_1313:...@postgres:5432/teqo_1313" pnpm db:check:push-chain',
)
}

View file

@ -14,8 +14,9 @@
* Usage:
* PROD_DATABASE_URL=postgres://... pnpm db:pull
*
* PROD_DATABASE_URL should be the UNPOOLED Neon connection string (the
* DATABASE_URL_UNPOOLED value in .env.local).
* PROD_DATABASE_URL should be the production connection string of the app
* (today: the homeserver Postgres, `teqo_1313` — the value of `DATABASE_URL`
* in `~/stack/teqo-1313.env` on the homeserver).
*/
import { execFileSync } from 'node:child_process'
import { dieWithLabel, LOCAL_HOSTS } from './lib/cli.mjs'
@ -31,8 +32,9 @@ const die = dieWithLabel('db:pull')
if (!prodUrl) {
die(
'PROD_DATABASE_URL is not set.\n' +
'Run it with your UNPOOLED Neon URL (DATABASE_URL_UNPOOLED in .env.local):\n' +
' PROD_DATABASE_URL="postgres://...neon.tech/neondb?sslmode=require" pnpm db:pull',
'Run it with the production connection string (today: homeserver `teqo_1313`, the\n' +
' `DATABASE_URL` of `~/stack/teqo-1313.env` on the homeserver):\n' +
' PROD_DATABASE_URL="postgresql://teqo_1313:...@postgres:5432/teqo_1313" pnpm db:pull',
)
}