- 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
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.