NoteBugsDocs

API reference

Storage and files

Where the bytes are written, and where they come back from.

The first two require an admin and are refused in demo mode. /api/files/[name] is the reading door, and it serves both attachments and avatars.

GET/api/storage
Role: Admin

Where attachments and the backup .zip are written.

driver is what was CHOSEN; effectiveDriver and effectiveBackupDriver are where things actually land. They diverge on an incomplete configuration, or when the scope sends only part of the content to the bucket.

Request

curl -s http://localhost:3000/api/storage \
  -H "Authorization: Bearer $TOKEN"

Response200

{
  "driver": "S3",
  "effectiveDriver": "LOCAL",
  "effectiveBackupDriver": "S3",
  "endpoint": "https://br-se1.exemplo-objects.com",
  "region": "br-se1",
  "bucket": "notebugs",
  "accessKeyId": "AKIAEXEMPLO000000000",
  "scope": "BACKUPS",
  "prefix": "uploads/",
  "prefixBackup": "backups/",
  "forcePathStyle": true,
  "hasSecret": true,
  "ready": true,
  "locked": true,
  "envFields": []
}

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account is not an admin. This prefix requires ADMIN in every method, reading included.

The secretAccessKey never comes out: the response carries hasSecret, which says a secret exists without saying which.

PATCH/api/storage
Role: Admin

Configures the storage backend: local disk or an S3-like bucket.

Request body

FieldTypeDescription
driverenumLOCAL · S3
endpointstringEmpty means AWS regional. It takes http://minio:9000 without TLS, which is the commonest case inside a Compose network.When absent: vazio = AWS regional
regionstring
bucketstring
accessKeyIdstring
secretAccessKeystringGoes in and never comes out. An empty string CLEARS the secret; absent does not touch it.
scopeenumWhat goes to the bucket: everything, only attachments or only backups.ALL · IMAGES · BACKUPS
prefixstring
prefixBackupstring
forcePathStylebooleanNeeded by most S3-like services that are not AWS.
lockedbooleanLocks the configuration against accidental change. Alone in the body, it is the only write accepted while it is on.

Request

curl -s -X PATCH http://localhost:3000/api/storage \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "driver": "S3",
    "endpoint": "https://br-se1.exemplo-objects.com",
    "region": "br-se1",
    "bucket": "notebugs",
    "accessKeyId": "AKIAEXEMPLO000000000",
    "secretAccessKey": "o segredo, que entra e nunca sai",
    "scope": "BACKUPS",
    "prefixBackup": "backups/",
    "forcePathStyle": true
  }'

Response200

{
  "driver": "S3",
  "effectiveDriver": "LOCAL",
  "effectiveBackupDriver": "S3",
  "endpoint": "https://br-se1.exemplo-objects.com",
  "region": "br-se1",
  "bucket": "notebugs",
  "accessKeyId": "AKIAEXEMPLO000000000",
  "scope": "BACKUPS",
  "prefix": "uploads/",
  "prefixBackup": "backups/",
  "forcePathStyle": true,
  "hasSecret": true,
  "ready": true,
  "locked": true,
  "envFields": []
}

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account is not an admin. This prefix requires ADMIN in every method, reading included.
403The installation is in demo mode, where backup, storage and accounts are off the air.
409The configuration is locked. Send { "locked": false } alone before editing anything else.
422Something the chosen backend needs is missing: S3 without a bucket, for instance. The check happens after composing with what the container defined.
POST/api/storage/test
Role: Admin

Writes, reads and deletes a test file using the configuration IN THE FORM.

It tests what is in the form, not what is already saved: you can check the credential before storing it. One test file per prefix in use.

Request body

FieldTypeDescription
driverenumLOCAL · S3
endpointstring
regionstring
bucketstring
accessKeyIdstring
secretAccessKeystring
scopeenumALL · IMAGES · BACKUPS
prefixstring
prefixBackupstring
forcePathStyleboolean

Request

# an empty body tests what is in effect right now
curl -s -X POST http://localhost:3000/api/storage/test \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "driver": "LOCAL", "scope": "ALL" }'

Response200

{
  "driver": "LOCAL",
  "detail": "Volume em disco e volume em disco responderam à gravação, à leitura e à remoção.",
  "label": "disco local"
}

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
403The account is not an admin. This prefix requires ADMIN in every method, reading included.
403The installation is in demo mode, where backup, storage and accounts are off the air.
502The bucket refused or did not answer. The 502 says the failure was in the service behind this application; the message carries what S3 returned.

It saves no configuration. An empty body tests what is in effect right now.

GET/api/files/[name]
Role: Viewer

Serves a file: a card attachment, a comment image or an avatar.

Always inline, with Content-Security-Policy: sandbox and nosniff: an attached HTML does not run on the application's domain.

Path parameters

FieldTypeDescription
namerequirednome gerado pelo servidorThe name GENERATED by the server (storedName), which came in the attachment payload. The shape of that name is the barrier against path traversal.

Request

curl -s -o print.png \
  http://localhost:3000/api/files/18ab694d-48ed-4b56-bd2b-3a324fec55de.png \
  -H "Authorization: Bearer $TOKEN"

Response200

content-type: image/png
content-length: 25381
content-disposition: inline; filename*=UTF-8''print.png
content-security-policy: default-src 'none'; sandbox
x-content-type-options: nosniff
cache-control: public, max-age=31536000, immutable

Error responses

CodeWhen it happens
401No credential, or one that no longer holds. Send the session cookie or the Authorization: Bearer header.
404The resource does not exist, or has already been deleted.
422The requested name does not have the shape of a server-generated name. It is the barrier against path traversal, and it holds for any backend.

It resolves the name in Attachment and in the account's avatarName, because the avatar has no attachment row. The bytes come from disk or from the bucket, according to the destination in effect.