OPS52 — Migrar a media de produção do Vercel Blob para o Garage S3 #27

Merged
fsolla merged 2 commits from OPS52-migrar-a-media-de-producao-do-vercel-blob-para-o-garag into main 2026-08-18 02:45:52 +00:00
Owner

Closes #3 (OPS52)

O que mudou

  • Storage da media: Vercel Blob → Garage S3 (@payloadcms/storage-s3 no lugar de @payloadcms/storage-vercel-blob), bucket teo-media do homeserver (key teqo-key já existente).
  • Bucket privado + proxy do Payload: o Garage v2.1.0 não suporta acesso anônimo na API S3 (verificado no source/CLI), então o staticHandler do plugin streama os objetos em /api/media/file/<filename> — media.url continua relativa, o mesmo contrato das 40 rows pré-existentes (recuperação = Issue #10, dependente).
  • Fail-closed: resolveS3StorageEnv (src/utilities/mediaStorage.ts, unit-testado) — envs S3_* parciais → boot abortado com a lista exata de faltantes; nenhuma → storage local (dev/test).
  • Seed de posts: pre-delete do Blob removido (o S3 sobrescreve o key determinístico nativamente — idempotência intacta).
  • Worktree: s3EnvCopiedLines all-or-nothing em scripts/lib/worktree-env.mjs (unit-testada) no lugar do copy de BLOB_READ_WRITE_TOKEN.
  • Envs: S3_BUCKET, S3_ENDPOINT, S3_REGION (default garage), S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY (.env.example, .env.test); .cursor/cloud-setup.sh sem o sed morto do blob.
  • Deps: -@payloadcms/storage-vercel-blob -@vercel/blob +@payloadcms/storage-s3@3.82.0; importMap regenerado.
  • Sem migration (config-only). Docs: impl plan (revisão A→B documentada), AGENTS.md, changelog.

Verificação

  • Prova de contrato real: PUT/GET/DELETE via SDK contra o Garage (tailnet) + media criada pelo Payload com S3 ativo → GET /api/media/file/... → 200 image/png; bucket devolvido a 0 objects.
  • Gate local completo verde (lint/format/typecheck/knip/cycles/unit 2278/int 791/build/e2e).
  • Runbook de cutover (5 envs S3_* no ~/stack/teqo-1313.env antes do deploy) no impl plan.
Closes #3 (OPS52) ## O que mudou - **Storage da media: Vercel Blob → Garage S3** (`@payloadcms/storage-s3` no lugar de `@payloadcms/storage-vercel-blob`), bucket `teo-media` do homeserver (key `teqo-key` já existente). - **Bucket privado + proxy do Payload:** o Garage v2.1.0 não suporta acesso anônimo na API S3 (verificado no source/CLI), então o `staticHandler` do plugin streama os objetos em `/api/media/file/<filename>` — `media.url` continua relativa, o **mesmo contrato das 40 rows pré-existentes** (recuperação = Issue #10, dependente). - **Fail-closed:** `resolveS3StorageEnv` (`src/utilities/mediaStorage.ts`, unit-testado) — envs `S3_*` parciais → boot abortado com a lista exata de faltantes; nenhuma → storage local (dev/test). - **Seed de posts:** pre-delete do Blob removido (o S3 sobrescreve o key determinístico nativamente — idempotência intacta). - **Worktree:** `s3EnvCopiedLines` all-or-nothing em `scripts/lib/worktree-env.mjs` (unit-testada) no lugar do copy de `BLOB_READ_WRITE_TOKEN`. - **Envs:** `S3_BUCKET`, `S3_ENDPOINT`, `S3_REGION` (default `garage`), `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` (`.env.example`, `.env.test`); `.cursor/cloud-setup.sh` sem o sed morto do blob. - **Deps:** `-@payloadcms/storage-vercel-blob -@vercel/blob +@payloadcms/storage-s3@3.82.0`; importMap regenerado. - **Sem migration** (config-only). Docs: impl plan (revisão A→B documentada), AGENTS.md, changelog. ## Verificação - Prova de contrato real: PUT/GET/DELETE via SDK contra o Garage (tailnet) + media criada pelo Payload com S3 ativo → `GET /api/media/file/...` → 200 image/png; bucket devolvido a 0 objects. - Gate local completo verde (lint/format/typecheck/knip/cycles/unit 2278/int 791/build/e2e). - Runbook de cutover (5 envs `S3_*` no `~/stack/teqo-1313.env` antes do deploy) no impl plan.
fsolla added 2 commits 2026-08-18 01:59:20 +00:00
- payload.config.ts: vercelBlobStorage -> s3Storage (@payloadcms/storage-s3)
  com endpoint Garage (forcePathStyle, region garage); bucket privado — o
  staticHandler do plugin streama os objetos em /api/media/file/<filename>,
  mantendo o contrato de URL relativa das 40 rows pré-existentes
- src/utilities/mediaStorage.ts: resolveS3StorageEnv fail-closed (S3_* parcial
  -> throw no boot com a lista exata; nenhuma -> storage local dev/test)
- seed-posts: remove pre-delete do @vercel/blob (S3 sobrescreve nativamente)
- worktree-env.mjs: s3EnvCopiedLines all-or-nothing (unit-testada)
- envs S3_BUCKET/S3_ENDPOINT/S3_REGION/S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY
  no .env.example/.env.test; cloud-setup.sh sem o sed morto do blob
- deps: -storage-vercel-blob -@vercel/blob +storage-s3; importMap regenerado
- docs: impl plan (revisão A->B: Garage v2 sem acesso anônimo na API S3),
  AGENTS.md, changelog 2026-08-18-ops52
fix(OPS52): S3_WORKTREE_REQUIRED_KEYS sem export (knip)
All checks were successful
CI (PR) / build (pull_request) Successful in 2m47s
CI (PR) / e2e (1) (pull_request) Successful in 9m6s
Agent PR Ready + auto-merge / ready-automerge (pull_request) Has been skipped
CI (PR) / e2e (2) (pull_request) Successful in 8m8s
CI (PR) / checks (pull_request) Successful in 1s
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 12s
CI (PR) / scope (pull_request) Successful in 42s
Agent pool supervisor / tick (pull_request) Successful in 42s
CI (PR) / lint (pull_request) Successful in 45s
CI (PR) / docs-guards (pull_request) Successful in 7s
CI (PR) / plans-only-closes (pull_request) Successful in 9s
CI (PR) / format (pull_request) Successful in 1m1s
CI (PR) / typecheck (pull_request) Successful in 37s
CI (PR) / knip (pull_request) Successful in 34s
CI (PR) / cycles (pull_request) Successful in 35s
CI (PR) / unit (pull_request) Successful in 58s
CI (PR) / int (pull_request) Successful in 2m2s
89e89c5118
fsolla merged commit 54058da059 into main 2026-08-18 02:45:52 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: fsolla/teqo#27
No description provided.