File Storage ​
AYB storage supports bucket administration, object CRUD, signed URLs, resumable uploads (TUS), and optional CDN URL rewrite/purge.
Enable storage ​
toml
[storage]
enabled = true
backend = "local" # "local" or "s3"
local_path = "./ayb_storage"
max_file_size = "10MB"Route surfaces and auth ​
Bucket admin routes (admin token required) ​
POST /api/storage/bucketsGET /api/storage/bucketsPUT /api/storage/buckets/{name}DELETE /api/storage/buckets/{name}
Notes:
- Bucket CRUD is admin-only.
- Deleting a non-empty bucket returns a conflict unless
force=trueis passed.
Object routes ​
GET /api/storage/{bucket}GET /api/storage/{bucket}/{name}POST /api/storage/{bucket}DELETE /api/storage/{bucket}/{name}POST /api/storage/{bucket}/{name}/sign
When auth is enabled:
- Read routes (
GET) use optional auth. - Write routes (
POSTupload,DELETE,POST .../sign) require admin-or-user auth.
When auth is disabled, routes are mounted directly without auth middleware.
Bucket visibility and signed URL behavior ​
- Buckets with metadata
public=trueexpose public object URLs. - Buckets with no metadata record are treated as implicitly public.
- Signed URL validation on
GET /api/storage/{bucket}/{name}bypasses auth if signature is valid.
Signed URL creation:
- Endpoint:
POST /api/storage/{bucket}/{name}/sign - Request:
{ "expiresIn": <seconds> } - Default expiry:
3600seconds - Maximum expiry:
604800seconds (7 days) - Response shape:
json
{
"url": "/api/storage/<bucket>/<name>?exp=...&sig=..."
}Upload and multipart behavior ​
Upload endpoint: POST /api/storage/{bucket}
Rules:
- Multipart form field
fileis required. - Optional form field
nameoverrides filename. - Request body is capped by
storage.max_file_size. - Content type is inferred from filename extension, then multipart header, else
application/octet-stream.
Resumable uploads (TUS) ​
Routes:
OPTIONS /api/storage/upload/resumablePOST /api/storage/upload/resumable?bucket={bucket}&name={name}HEAD /api/storage/upload/resumable/{id}PATCH /api/storage/upload/resumable/{id}
Behavior:
OPTIONSis intentionally unauthenticated for browser preflight.POST,HEAD, andPATCHrequire admin-or-user auth when auth is enabled.- Required TUS version header:
Tus-Resumable: 1.0.0(create/head/patch). POSTrequiresUpload-Lengthand bucket/name (query or metadata fallback for name).PATCHrequiresContent-Type: application/offset+octet-streamandUpload-Offset.HEADreturns currentUpload-Offsetand totalUpload-Length.
Object storage backends ​
Local:
toml
[storage]
enabled = true
backend = "local"
local_path = "/var/lib/ayb/storage"S3-compatible:
toml
[storage]
enabled = true
backend = "s3"
s3_endpoint = "https://ACCOUNT_ID.r2.cloudflarestorage.com"
s3_bucket = "my-bucket"
s3_region = "auto"
s3_access_key = "..."
s3_secret_key = "..."CDN rewrite and purge providers ​
URL rewriting uses storage.cdn_url.
toml
[storage]
cdn_url = "https://cdn.example.com"
[storage.cdn]
provider = "cloudflare" # or "cloudfront" or "webhook"Supported provider keys:
storage.cdn.cloudflare.zone_idstorage.cdn.cloudflare.api_tokenstorage.cdn.cloudfront.distribution_idstorage.cdn.webhook.endpointstorage.cdn.webhook.signing_secret
Runtime validation requires storage.cdn_url when any CDN provider is configured.
JavaScript SDK examples ​
ts
import { AYBClient } from "@allyourbase/js";
const ayb = new AYBClient("http://localhost:8090");
await ayb.auth.login("[email protected]", "password");
const file = document.querySelector("input[type=file]")!.files![0];
const uploaded = await ayb.storage.upload("avatars", file);
const publicOrOriginURL = ayb.storage.downloadURL("avatars", uploaded.name);
const { items, totalItems } = await ayb.storage.list("avatars", { limit: 50, offset: 0 });
const { url: signedURL } = await ayb.storage.getSignedURL("avatars", uploaded.name, 3600);
await ayb.storage.delete("avatars", uploaded.name);