Storage#
Rotas
/v1/cloudstore/*· owner/admin · gated porCLOUDSTORE_ENABLED(off → 404)
O que é#
Buckets de objetos gerenciados em cima do Google Cloud Storage. Você cria buckets por projeto e faz upload/download/organização de arquivos pela API — sem tocar no console da GCP. Os buckets ficam no mesmo GCP project que hospeda as instâncias de Database daquele projeto.
O que dá pra fazer#
- Criar/listar/remover buckets; ver o overview (contagem de objetos, bytes totais, settings ao vivo do GCS).
- Upload (multipart), download (stream autenticado ou signed URL keyless), delete.
- Pastas (prefixos
foo/bar/): criar, ver stats (count + bytes), apagar recursivamente. - Power-ops de objeto:
stat(metadata completa),copy,move/rename,meta(editar content-type/cache/metadata/storage-class). - Acesso S3-compatível: emitir chaves HMAC e apontar qualquer SDK do S3/R2
para
storage.googleapis.com.
Como funciona por dentro (na prática)#
- Control-plane + GCS real. A API guarda a control-row do bucket no tenant DB;
o objeto vive no GCS. O nome real do bucket é globalmente único
(
ctc-<hash-projeto>-<nome>) — você sempre usa oidda control-row. - Signed URLs keyless (V4).
objects/signdevolve uma URL que o GCS serve direto (storage.googleapis.com), sem passar pelo Catcher nem expor credencial — ideal para o navegador baixar/exibir. Default TTL 15 min, máx. 7 dias. - Pastas são prefixos. O GCS não tem pastas; uma "pasta" é um placeholder
zero-byte com chave terminada em
/. Os stats e a varredura ignoram o placeholder. - Content-Type normalizado no upload: tipos textuais ganham
; charset=utf-8e tipos vazios/octet-streamsão inferidos da extensão — a signed URL renderiza UTF-8 no navegador sem mojibake. - S3-compat isolado por cliente: cada projeto usa uma SA dedicada
(
ctc-store@<projeto>) com acesso só ao próprio projeto; osecretda chave HMAC só aparece na criação.
Fluxo típico (end-to-end)#
KEY="X-API-Key: ctc_…" ; B=https://data-api.catcher.one ; JS='Content-Type: application/json'
PID=<project-uuid> # mesmo projeto do Database (ver Getting Started)
# 1. Cria o bucket (tenant_id é opcional — dedica o bucket a um cliente final; ver Tenants)
BID=$(curl -sX POST $B/v1/cloudstore/projects/$PID/buckets -H "$KEY" -H "$JS" \
-d '{"name":"assets","location":"southamerica-east1","storage_class":"STANDARD"}' | jq -r .id)
# 2. Upload (multipart). 'key' opcional aceita prefixo (cria a "pasta")
curl -sX POST $B/v1/cloudstore/buckets/$BID/objects -H "$KEY" \
-F "file=@./logo.png" -F "key=brand/logo.png"
# 3. Lista sob um prefixo
curl -s "$B/v1/cloudstore/buckets/$BID/objects?prefix=brand/" -H "$KEY"
# 4. Signed URL para o navegador baixar/exibir (15 min)
curl -s "$B/v1/cloudstore/buckets/$BID/objects/sign?key=brand/logo.png&ttl=900" -H "$KEY"
# → { "url": "https://storage.googleapis.com/…", "expires_in": 900 }
Referência de endpoints#
Buckets#
| Método | Rota | O que faz |
|---|---|---|
POST |
/v1/cloudstore/projects/{pid}/buckets |
Cria (name, location?, storage_class?, tenant_id?) → 201 |
GET |
/v1/cloudstore/projects/{pid}/buckets |
Lista |
GET |
/v1/cloudstore/buckets/{id}/overview |
object_count+total_bytes + settings GCS (versioning, public-access-prevention, UBLA) |
DELETE |
/v1/cloudstore/buckets/{id} |
Remove (GCS + control-row) → 204 |
Isolamento por tenant (end-customers)
Passe tenant_id ao criar o bucket para dedicá-lo a um cliente final: o
bucket fica isolado por IAM no GCS — um bucket por cliente. Sem tenant_id, o
bucket é de nível projeto (compartilhado entre seus clientes). É o mesmo
tenant_id usado no Database, Vector e Cache — ver Tenants.
Objetos#
| Método | Rota | O que faz |
|---|---|---|
GET |
/v1/cloudstore/buckets/{id}/objects?prefix=&limit= |
Lista (key,size,content_type,updated,etag) |
POST |
/v1/cloudstore/buckets/{id}/objects |
Upload multipart (file + key?) |
DELETE |
/v1/cloudstore/buckets/{id}/objects?key= |
Remove → 204 |
GET |
/v1/cloudstore/buckets/{id}/objects/sign?key=&ttl= |
Signed URL (download direto do GCS) |
GET |
/v1/cloudstore/buckets/{id}/objects/download?key= |
Stream pelo control-plane (autenticado) |
GET |
/v1/cloudstore/buckets/{id}/objects/stat?key= |
Metadata completa (generation, md5, crc32c, custom x-goog-meta-*, …) |
POST |
/v1/cloudstore/buckets/{id}/objects/copy |
Duplica ({src,dst}, rewrite server-side) → 201 |
POST |
/v1/cloudstore/buckets/{id}/objects/move |
Move/rename ({src,dst}, copy+delete) → 200 |
PATCH |
/v1/cloudstore/buckets/{id}/objects/meta?key= |
Edita content-type/cache/disposition/language/storage-class/metadata |
Pastas#
| Método | Rota | O que faz |
|---|---|---|
POST |
/v1/cloudstore/buckets/{id}/folders |
Cria pasta vazia ({path}) → 201 {path:"docs/sub/"} |
GET |
/v1/cloudstore/buckets/{id}/folders/stats?path= |
{count, total_bytes} sob o prefixo |
DELETE |
/v1/cloudstore/buckets/{id}/folders?path= |
Apaga recursivamente tudo sob o prefixo → {deleted:N} |
Acesso S3-compatível (HMAC)#
| Método | Rota | O que faz |
|---|---|---|
POST |
/v1/cloudstore/projects/{pid}/hmac-keys |
Emite chave (secret só aqui) |
GET |
/v1/cloudstore/projects/{pid}/hmac-keys |
Lista |
DELETE |
/v1/cloudstore/projects/{pid}/hmac-keys/{accessId} |
Revoga |
Aponte qualquer SDK do S3/R2 para https://storage.googleapis.com com a chave/secret.
Códigos de erro do módulo#
INVALID_BUCKET_NAME (400) · BUCKET_ALREADY_EXISTS (409) · BUCKET_NOT_FOUND
(404) · BUCKET_NOT_READY (409) · OBJECT_NOT_FOUND (404) · NO_READY_PROJECT
(409, sem GCP project pronto no projeto) · S3_ACCESS_DISABLED_BY_POLICY (409,
org policy bloqueia chaves de SA).
Gotchas#
- Use sempre o
idda control-row, não o nome real do GCS (ctc-…). - Para o navegador, prefira
sign(download direto, sem custo de proxy);downloadé o stream autenticado pelo control-plane (útil server-side). - Apagar pasta é recursivo e definitivo —
pathnão pode ser vazio///./..(guard contra apagar o bucket inteiro). movenão é atômico (copy + delete) — em falha no meio, pode sobrar a origem.NO_READY_PROJECT→ o projeto ainda não tem um GCP project provisionado (geralmente porque nenhuma instância de Database foi criada nele ainda).