# Agente sobe imagens na galeria

Objetivo: o agent envia imagens para uma galeria da CDN e entrega a URL pública ao app, sem montar URL à mão.

Token necessário: `galleries:manage` (criar galeria), `assets:write` (enviar), `assets:read` (consultar); para gravar a URL no serviço, `projects:write` e `env:write`.

## 1. Criar a galeria

Guarde o `key` devolvido: ele vai nas rotas. Cada chamada cria uma galeria nova; use `Idempotency-Key` para repetir com segurança.

```bash
curl -sS -X POST "$NEBULAR_API/v1/galleries" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" \
  -H "Idempotency-Key: receita-imagens-$(date +%s)-$RANDOM" \
  -H "Content-Type: application/json" \
  -d '{"name": "Imagens do site"}'
```
```json
{"id": "gal_01J9ZK5Q", "key": "imagens-do-site-k7p2", "name": "Imagens do site"}
```

| Erro | Ação |
|---|---|
| `403 forbidden` | falta `galleries:manage` (ou o token é restrito a galerias): peça outro token |
| `429` | espere `Retry-After` e repita |

## 2. Enviar, esperar e ler a URL

Envie com `Idempotency-Key` derivada do conteúdo. `201` já vem publicado; `202` é `pending` (não reenvie): consulte o arquivo com backoff (2 s, dobrando até 30 s) até `scan.status` ser `clean`. Só então `alias_url` e `url` existem. Use sempre o valor devolvido.

```bash
GAL=$(curl -sS -X POST "$NEBULAR_API/v1/galleries" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: receita-imagens-b-$(date +%s)-$RANDOM" \
  -d '{"name": "Imagens do site"}' | jq -r '.key')

code=$(curl -sS -o resp.json -w '%{http_code}' -X POST "$NEBULAR_API/v1/galleries/$GAL/assets" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" \
  -H "Idempotency-Key: hero-$(cksum < hero.png | tr ' ' '-')" \
  -F "file=@hero.png" -F "name=site/hero.png")
echo "upload: $code"
delay=2
for attempt in 1 2 3 4 5 6 7 8; do
  status=$(curl -sS "$NEBULAR_API/v1/galleries/$GAL/assets/site%2Fhero.png" \
    -H "Authorization: Bearer $NEBULAR_TOKEN" | jq -r '.asset.current.scan.status')
  [ "$status" = "pending" ] || break
  sleep "$delay"; delay=$(( delay * 2 > 30 ? 30 : delay * 2 ))
done
[ "$status" = "clean" ]
curl -sS "$NEBULAR_API/v1/galleries/$GAL/assets/site%2Fhero.png" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" | jq -r '.asset.alias_url'
```

| Resposta | Ação |
|---|---|
| `413 payload_too_large` / `quota_exceeded` | reduza a imagem (25 MB, 40 MP) ou libere espaço |
| `415`, `422` | arquivo recusado: não reenvie o mesmo; veja a [tabela de erros](/erros/tabela/) |
| `409 idempotency_conflict` | chave reutilizada com outro arquivo: gere outra |
| `scan.status = rejected` | pare: o arquivo foi recusado, `scan.reason` diz o motivo |

Detalhes de `pending`, lote e troca de imagem mantendo a `alias_url`: [Galerias e arquivos](/cdn/galerias/).

## 3. Entregar a URL ao serviço

Guarde a `alias_url` como variável do serviço (`env:write`); o app lê `ASSET_BASE_URL` e monta os caminhos. A alias sempre aponta para a versão atual, então trocar a imagem não exige novo deploy. Mudar o valor de uma variável marca o serviço como "precisa de redeploy" para pegar o valor novo.

```bash
NAME="imagens-$(date +%s)-$RANDOM"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: proj-$NAME" -d "{\"name\": \"$NAME\"}" | jq -r '.id')
S=$(curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -d '{"visibility": "public", "source": {"type": "upload"}}' | jq -r '.id')
curl -sS -X PUT "$NEBULAR_API/v1/projects/$P/services/$S/variables/ASSET_BASE_URL" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -d '{"value": "https://media.nebularcdn.com/imagens-do-site-k7p2/site/"}'
```

No exemplo real, use o prefixo da `alias_url` obtida no passo 2 (a última linha do bloco anterior).

| Erro | Ação |
|---|---|
| `403 forbidden` | falta `projects:write` ou `env:write` |
| `404 not_found` | projeto/serviço inexistente ou de outro time: confira os ids; não tente adivinhar |
| `400 invalid_request` | nome do serviço fora de `^[a-z][a-z0-9-]{0,30}$` ou campo inválido |
