teqo/docs/changelog/2026-08-18-ops52-media.md
Francisco Solla 45e29a7785
All checks were successful
Agent PR Ready + auto-merge / ready-automerge (pull_request) Has been skipped
CI (PR) / docs-guards (pull_request) Successful in 7s
CI (PR) / plans-only-closes (pull_request) Successful in 8s
CI (PR) / scope (pull_request) Successful in 51s
CI (PR) / lint (pull_request) Successful in 1m4s
CI (PR) / format (pull_request) Successful in 1m14s
CI (PR) / typecheck (pull_request) Successful in 39s
CI (PR) / knip (pull_request) Successful in 40s
CI (PR) / cycles (pull_request) Successful in 39s
CI (PR) / unit (pull_request) Successful in 1m0s
CI (PR) / int (pull_request) Successful in 2m39s
CI (PR) / build (pull_request) Successful in 4m2s
CI (PR) / e2e (2) (pull_request) Successful in 6m2s
CI / e2e (1) (push) Successful in 6m39s
CI (PR) / e2e (1) (pull_request) Successful in 7m26s
CI (PR) / checks (pull_request) Successful in 7s
Promote plan-issue Related on main merge / plan-issue-ready (pull_request) Has been skipped
Main merge flips claimed issues to done + in-prod / issue-done (pull_request) Successful in 19s
Agent pool supervisor / tick (pull_request) Successful in 42s
CI / int (push) Successful in 2m52s
CI / static (push) Successful in 2m57s
CI / build (push) Successful in 3m52s
CI / e2e (2) (push) Successful in 5m34s
CI / checks (push) Successful in 2s
CI / deploy homeserver (teqo-1313) (push) Successful in 8m27s
feat(OPS52-media): recupera os objetos de media de produção do WordPress para o bucket (pnpm media:recover)
- scripts/recover-media.mjs: CLI de reconciliação (reconcile/--dry-run/--verify)
  — lê rows de media + posts.coverImage, resolve a capa com a mesma lógica do
  seed (featured media, senão 1ª imagem inline), PutObject com key = filename;
  DB nunca escrito; fail-closed (S3_* completas, ALLOW_REMOTE_DB, echo do alvo,
  timeouts curtos, covers só do origin WP e image/*)
- scripts/lib/wpArticles.mjs: fetch ao vivo do WordPress extraído do seed
  (sem twin), helpers puros de resolução + row→cover mapping (unit-testados)
- seed-posts.mjs: importa o fetch da lib (comportamento idêntico) e usa
  resolveCoverSource em processContent
- runbook de execução no homeserver (impl plan), nota no AGENTS.md, changelog
Closes #10
2026-08-18 08:20:45 -03:00

1.9 KiB

Recently resolved (2026-08-18): OPS52-media — os objetos de media de produção (as ~40 capas dos posts, que existiam como rows com URL relativa /api/media/file/<filename> mas sem arquivo em lugar nenhum) são recuperáveis do WordPress de origem com pnpm media:recover (scripts/recover-media.mjs, novo): lê as rows de media + post.coverImage (mapeamento exato row→slug; fallback pelo filename determinístico <slug>.<ext>), resolve a capa com a MESMA lógica do seed (featured media do REST _embed, senão 1ª imagem inline do corpo — só 7/43 posts têm featured), baixa e faz PutObject no bucket das envs S3_* com key = filename da row, sem nunca escrever no DB (rows e URLs intocadas; o staticHandler do Payload passa a servir). Extraído para scripts/lib/wpArticles.mjs o fetch ao vivo do WordPress (seed-posts agora importa de lá — sem twin; unit-testado). Fail-closed: exige as 4 S3_* completas (resolveS3StorageEnv reusado; recuperação nunca vai pro storage local; --verify dispensa S3 — só HEAD de URLs públicas), ALLOW_REMOTE_DB=true explícito para DB remoto + echo do alvo (DB/bucket/endpoint/origin), timeouts curtos no SDK (@smithy/node-http-handler, 5s/15s) e nos fetches (30s/60s) — endpoint inalcançável falha em segundos em vez de pendurar (a rota pública de media de prod pendurava >25s; evidência de possível falta de rota container→Garage, diagnosticada no runbook do impl plan), e covers constrangidas a http(s) no host do WordPress com só image/* armazenado (sem SSRF/objeto não-imagem no proxy). Modos: reconcile (padrão, upload idempotente por overwrite), --dry-run (plano + headObject; exit 1 se qualquer checagem falhar), --verify (HEAD da URL pública por filename — o aceite da Issue, sem credencial). Runbook de execução no homeserver: docs/plans/ops52-media-recuperar-arquivos-impl.md.