docs(OPS52-media): runbook com o fix de infra real (E3 = firewall) #41
2 changed files with 27 additions and 14 deletions
|
|
@ -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 the verifier — full suite green — **plus the deploy step (OPS53, 2026-08-17)**: the `deploy` job (runs-on `host` on the workstation runner) streams `scripts/deploy-homeserver.sh` to the homeserver over SSH; the remote script builds the standalone image at the pushed SHA (BuildKit secrets from `~/stack/teqo-1313.env`), pushes it to the local registry (`localhost:5000`), applies pending migrations through the compose maintenance service `teqo-1313-migrate` **before** the rollout, and recreates the container. Merge em `main` verde == site atualizado. Runbook (rollback, falhas conhecidas): `docs/ops/teqo-1313-deploy.md`. **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 moved to the homeserver Garage S3 in OPS52 (2026-08-18): the `media` collection uploads via `@payloadcms/storage-s3` when the `S3_*` envs are set, and Payload proxies the files through `/api/media/file/...` (the bucket stays private). Production env lives in `~/stack/teqo-1313.env` (bucket `teqo-media`, endpoint tailnet `http://100.119.220.31:3900`, key `teqo-key`); a partial `S3_*` config aborts the boot, and no `S3_*` means local disk (dev/test). Render of the 40 pre-existing media rows is still broken (they reference local `/api/media/file/...` paths with no files anywhere) — recovery is `pnpm media:recover` (OPS52-media: restaura os objetos no bucket a partir do WordPress, sem tocar no DB; runbook em `docs/plans/ops52-media-recuperar-arquivos-impl.md`); the URLs themselves are the same contract the proxy now serves. Never point dev/test databases at the homeserver prod DB either — the local guards (`guard-dev-db`, `assertTestDatabase`) still enforce `localhost`/`_test`-suffixed targets.
|
||||
- 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 the verifier — full suite green — **plus the deploy step (OPS53, 2026-08-17)**: the `deploy` job (runs-on `host` on the workstation runner) streams `scripts/deploy-homeserver.sh` to the homeserver over SSH; the remote script builds the standalone image at the pushed SHA (BuildKit secrets from `~/stack/teqo-1313.env`), pushes it to the local registry (`localhost:5000`), applies pending migrations through the compose maintenance service `teqo-1313-migrate` **before** the rollout, and recreates the container. Merge em `main` verde == site atualizado. Runbook (rollback, falhas conhecidas): `docs/ops/teqo-1313-deploy.md`. **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 moved to the homeserver Garage S3 in OPS52 (2026-08-18): the `media` collection uploads via `@payloadcms/storage-s3` when the `S3_*` envs are set, and Payload proxies the files through `/api/media/file/...` (the bucket stays private). Production env lives in `~/stack/teqo-1313.env` (bucket `teqo-media`, endpoint `http://host.docker.internal:3900` — o container alcança o Garage no host via `extra_hosts: host-gateway` + regras ufw `allow from 10.0.11.0/24 to any port 3900` (INPUT e route/FWD); a workstation/tailnet usa `http://100.119.220.31:3900`, key `teqo-key`); a partial `S3_*` config aborts the boot, and no `S3_*` means local disk (dev/test). The 40 pre-existing media rows are RECOVERED (OPS52-media, 2026-08-18): 38/40 objects restaurados no bucket a partir do WordPress (38 capas servindo 200 em `/api/media/file/...`; exceções sem fonte: `fim-escala-6x1.jpg` e `jorgesolla.jpg`) — re-executar via `pnpm media:recover` (restaura objetos sem tocar no DB; runbook em `docs/plans/ops52-media-recuperar-arquivos-impl.md`); the URLs themselves are the same contract the proxy now serves. 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.
|
||||
|
|
|
|||
|
|
@ -166,8 +166,24 @@ contratos de guard diferentes no mesmo script; o seed é local-first por design)
|
|||
|
||||
## Runbook de execução em produção (pós-merge, humano)
|
||||
|
||||
Ordem obrigatória — o pre-flight separa "arquivo faltando" (esta Issue) de
|
||||
"container prod sem rota ao Garage" (infra, sucessor OPS):
|
||||
> **Estado 2026-08-18 (executado):** 38/40 objetos restaurados e servindo 200 em
|
||||
> `/api/media/file/...`; exceções sem fonte: `fim-escala-6x1.jpg` e
|
||||
> `jorgesolla.jpg` (404 honesto — rows manuais, não referenciadas por posts).
|
||||
> **E3 resolvido na raiz:** o container prod não alcançava o Garage — o
|
||||
> firewall do host derrubava TODA a sub-rede do docker (gateway, LAN e tailnet:
|
||||
> tudo bloqueado — o `headObject` pendurava e a rota de media dava timeout).
|
||||
> Fix aplicado no homeserver (reversível):
|
||||
>
|
||||
> 1. `sudo ufw allow from 10.0.11.0/24 to any port 3900 proto tcp` (INPUT)
|
||||
> 2. `sudo ufw route allow from 10.0.11.0/24 to any port 3900 proto tcp` (FWD)
|
||||
> 3. `extra_hosts: ["host.docker.internal:host-gateway"]` nos serviços
|
||||
> `teqo-1313` e `teqo-1313-migrate` do `~/stack/docker-compose.yml`
|
||||
> (sobrevive aos deploys — o OPS53 edita o compose in-place; backup
|
||||
> `docker-compose.yml.pre-ops52-media-hostgateway`)
|
||||
> 4. `S3_ENDPOINT=http://host.docker.internal:3900` no `~/stack/teqo-1313.env`
|
||||
> (o tailnet IP continua sendo o endpoint da workstation)
|
||||
|
||||
Ordem das próximas execuções (pós-merge, humano):
|
||||
|
||||
1. **Deploy do merge em `main`** (CI OPS53) — o script e a lib chegam ao
|
||||
homeserver com a imagem do SHA.
|
||||
|
|
@ -181,12 +197,8 @@ Ordem obrigatória — o pre-flight separa "arquivo faltando" (esta Issue) de
|
|||
```
|
||||
Esperado: `headObject` de cada filename responde `presente`/`ausente` em
|
||||
segundos. Se TODAS as rows derem `erro` (timeout/ECONNREFUSED), o container
|
||||
não alcança o Garage — diagnosticar antes de recuperar:
|
||||
```bash
|
||||
docker compose exec teqo-1313 node -e "fetch('http://100.119.220.31:3900/').catch(e=>{console.error('SEM ROTA ao Garage:', e.message); process.exit(1)}).then(()=>console.log('Garage alcançável'))"
|
||||
```
|
||||
(o `.env.local` do worktree NUNCA recebe as credenciais de prod — rodar
|
||||
sempre no homeserver).
|
||||
não alcança o Garage — repetir o diagnóstico do estado acima (firewall +
|
||||
extra_hosts + endpoint) antes de recuperar.
|
||||
3. **Recuperar:**
|
||||
```bash
|
||||
ALLOW_REMOTE_DB=true pnpm media:recover
|
||||
|
|
@ -198,13 +210,14 @@ Ordem obrigatória — o pre-flight separa "arquivo faltando" (esta Issue) de
|
|||
```bash
|
||||
ALLOW_REMOTE_DB=true pnpm media:recover --verify
|
||||
```
|
||||
Esperado: `40/40 filenames respondendo 200 em https://jorgesolla1313.com.br`
|
||||
— é o aceite da Issue. Se persistir pendurando (erro/timeout) com objetos no
|
||||
bucket → confirmar container→Garage (passo 2) e abrir sucessor OPS de infra.
|
||||
Esperado: `38/40 filenames respondendo 200 em https://jorgesolla1313.com.br`
|
||||
(as 2 exceções seguem 404 — aceite da Issue contempla o registro honesto).
|
||||
Se persistir pendurando (erro/timeout) com objetos no bucket → confirmar
|
||||
container→Garage (passo 2) — agora a causa raiz conhecida é firewall.
|
||||
5. **Check visual:** home de campanha (seção "Acompanhe de perto") e 1–2
|
||||
páginas de artigo com capa; console do navegador sem erro de imagem.
|
||||
6. **Exceções:** o 1 artigo sem cover no WP (43→42 resolvem) e a 40ª row órfã
|
||||
se não resolver → reportar na Issue #10; o card degrada para a banda cinza
|
||||
6. **Exceções:** `fim-escala-6x1.jpg` e `jorgesolla.jpg` (rows manuais sem
|
||||
fonte no WP) → reportar na Issue #10; o card degrada para a banda cinza
|
||||
existente (sem placeholder, sem placeholder "em breve").
|
||||
|
||||
## Rabbit holes / Não escopo (engenharia)
|
||||
|
|
|
|||
Loading…
Reference in a new issue