# Prompt para Adicionar Upload de Arquivo com Cloudflare R2

> Prompt de IA para copiar e colar que adiciona URLs de upload pré-assinadas do Cloudflare R2 a um projeto Next.js App Router com validação no servidor.

**Type:** Prompt  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** Next.js, Cloudflare, TypeScript  
**Difficulty:** medium  
**Updated:** 2026-06-08

---

Forneça este prompt ao seu agente para implementar uploads diretos para o R2 usando URLs
pré-assinadas — mantendo os segredos no lado do servidor e evitando o erro comum de rotear os bytes do
arquivo através do servidor Next.js.

## Prompt Principal

```txt title="Main Prompt"
You are working in a Next.js App Router project using TypeScript.

Task: add Cloudflare R2 file upload via presigned PUT URLs.

Requirements:
- Install `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner` (R2 is S3-compatible).
- Create a Server Action in `src/lib/actions/get-upload-url.ts` that:
  - Accepts `{ filename: string; contentType: string; sizeBytes: number }`.
  - Validates: `sizeBytes` must be <= 10_485_760 (10 MB); `contentType` must start with `image/`.
  - Uses `PutObjectCommand` + `getSignedUrl` to generate a 60-second presigned URL.
  - Returns `{ uploadUrl: string; publicUrl: string; key: string }`.
- Store credentials in `.env`: `R2_ACCOUNT_ID`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`,
  `R2_BUCKET_NAME`, `R2_PUBLIC_URL`.
- Create a client component `src/components/FileUploader.tsx` that:
  - Renders a file `<input accept="image/*" />`.
  - On change: calls the Server Action to get the presigned URL, then does a `fetch` PUT directly
    to R2, then shows the `publicUrl` as a preview.
  - Shows upload progress via `XMLHttpRequest` `progress` events (not fetch, which lacks progress).
- Do not add any other dependencies. Do not modify `next.config.ts` unless required for image domains.

Stop and list all planned file changes before writing any code.
```

## Notas de Implementação

- O R2 usa o SDK S3 com `endpoint: https://<accountId>.r2.cloudflarestorage.com` e
  `region: 'auto'`. Esquecer `region: 'auto'` causa um erro de assinatura.
- URLs pré-assinadas são geradas no lado do servidor; o cliente nunca vê `R2_SECRET_ACCESS_KEY`.
- Para acesso público ao bucket, ative a opção "Acesso público" do bucket R2 no painel do Cloudflare
  e forneça a URL `r2.dev` resultante como `R2_PUBLIC_URL`.

## Alterações de Arquivos Esperadas

```txt
src/lib/actions/get-upload-url.ts      (new)
src/lib/r2.ts                          (new — S3Client singleton)
src/components/FileUploader.tsx        (new — Client Component)
.env.example                           (edited)
package.json                           (edited)
next.config.ts                         (edited — add r2.dev to images.remotePatterns)
```

## Critérios de Aceitação

- Um arquivo selecionado em `FileUploader` aciona uma busca por URL pré-assinada e, em seguida, faz upload diretamente para o R2.
- Arquivos maiores que 10 MB são rejeitados antes de qualquer requisição de rede.
- Tipos MIME que não são imagens são rejeitados no lado do servidor e retornam um erro equivalente a 400.
- A prévia da URL pública renderiza a imagem enviada após a conclusão do upload.

## Comandos de Teste

```bash
bun run typecheck
bun run dev
# upload a PNG < 10 MB via the component and confirm it appears in R2 bucket
# attempt a 15 MB file and confirm rejection
# attempt a .pdf and confirm rejection
```

## Erros Comuns de IA

- Definir `region: 'us-east-1'` em vez de `'auto'`, causando erros `SignatureDoesNotMatch`.
- Rotear os bytes do arquivo através da Server Action do Next.js em vez de fazer fetch diretamente para o R2.
- Esquecer de chamar `'use server'` no topo do arquivo de ações.
- Usar `fetch` para o upload e perder eventos de progresso.

## Prompt de Correção

```txt title="Fix Prompt"
The upload fails with `SignatureDoesNotMatch` or the file routes through the server.
Fix in order:
1. In `src/lib/r2.ts`, set `region: 'auto'` and `endpoint: https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`.
2. In `FileUploader.tsx`, use `XMLHttpRequest` with a `progress` event to PUT directly to the
   presigned URL — not fetch, and not a Server Action for the actual upload bytes.
Show corrected diff only.
```