Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions .github/workflows/openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: OpenAPI

on:
pull_request:
paths:
- "src/**"
- "package.json"
- "package-lock.json"
- "static/api.json"
- "static/api-admin.json"
- ".github/workflows/openapi.yml"
push:
branches:
- master
workflow_dispatch:

permissions:
contents: read

concurrency:
group: openapi-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
drift_check:
name: Spec drift check
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Verify committed OpenAPI snapshots exist
run: |
missing=()
for f in static/api.json static/api-admin.json; do
if ! git ls-files --error-unmatch "$f" > /dev/null 2>&1; then
missing+=("$f")
fi
done
if [ "${#missing[@]}" -gt 0 ]; then
echo "::error::OpenAPI snapshots are not yet committed to the repository."
for f in "${missing[@]}"; do
echo " missing: $f"
done
echo ""
echo "Bootstrap one-time: run 'npm run docs:export' locally and commit"
echo "static/api.json and static/api-admin.json. Or download the"
echo "'openapi-specs' artifact from a previous run on this PR and"
echo "commit those files."
exit 1
fi

- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Set up Node and npm
uses: ./.github/actions/setup-node-npm
with:
node-version: "24"
- name: Install dependencies
run: npm ci

- name: Regenerate OpenAPI specs
run: npm run docs:export
env:
ANON_KEY: ${{ secrets.ANON_KEY }}
SERVICE_KEY: ${{ secrets.SERVICE_KEY }}
TENANT_ID: ${{ secrets.TENANT_ID }}
REGION: ${{ secrets.REGION }}
POSTGREST_URL: ${{ secrets.POSTGREST_URL }}
GLOBAL_S3_BUCKET: ${{ secrets.GLOBAL_S3_BUCKET }}
PGRST_JWT_SECRET: ${{ secrets.PGRST_JWT_SECRET }}
AUTHENTICATED_KEY: ${{ secrets.AUTHENTICATED_KEY }}
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1/postgres
PGOPTIONS: -c search_path=storage,public
FILE_SIZE_LIMIT: "52428800"
STORAGE_BACKEND: s3
ENABLE_IMAGE_TRANSFORMATION: true
VECTOR_ENABLED: true
ICEBERG_ENABLED: true

- name: Fail if committed spec is out of date
run: |
if ! git diff --exit-code -- static/api.json static/api-admin.json; then
echo ""
echo "::error::The OpenAPI spec is out of date."
echo "Run 'npm run docs:export' locally and commit the updated"
echo "static/api.json and static/api-admin.json files."
exit 1
fi

- name: Upload spec artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: openapi-specs
path: |
static/api.json
static/api-admin.json
if-no-files-found: warn
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ dist/
.env
.env.*
!.*.sample
static/api.json
static/api-admin.json
data/
bin/
coverage/
Expand Down
6 changes: 5 additions & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
"**/*.mjs",
"**/*.json",
"**/*.jsonc",
"!!package-lock.json"
"!!package-lock.json",
// Generated OpenAPI snapshots — emitted minified by `npm run docs:export`
// and verified by the spec drift gate; not hand-formatted.
"!static/api.json",
"!static/api-admin.json"
]
},
"vcs": {
Expand Down
1 change: 1 addition & 0 deletions static/api-admin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"openapi":"3.0.3","info":{"title":"Supabase Storage Admin API","description":"Admin API documentation for Supabase Storage","version":"0.0.0"},"components":{"securitySchemes":{"apiKeyAuth":{"type":"apiKey","in":"header","name":"ApiKey"}},"schemas":{}},"paths":{"/tenants/":{"get":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/tenants":{"head":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}":{"get":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"post":{"tags":["tenant"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"anonKey":{"type":"string"},"databaseUrl":{"type":"string"},"databasePoolUrl":{"type":"string","nullable":true},"maxConnections":{"type":"number"},"jwks":{"type":"object","nullable":true},"fileSizeLimit":{"type":"number"},"jwtSecret":{"type":"string"},"serviceKey":{"type":"string"},"tracingMode":{"type":"string"},"disableEvents":{"type":"array","items":{"type":"string"},"nullable":true},"features":{"type":"object","properties":{"imageTransformation":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxResolution":{"type":"number","nullable":true}}},"purgeCache":{"type":"object","properties":{"enabled":{"type":"boolean"}}},"s3Protocol":{"type":"object","properties":{"enabled":{"type":"boolean"}}},"icebergCatalog":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxNamespaces":{"type":"number"},"maxTables":{"type":"number"},"maxCatalogs":{"type":"number"}}},"vectorBuckets":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxBuckets":{"type":"number"},"maxIndexes":{"type":"number"}}}}}},"required":["anonKey","databaseUrl","jwtSecret","serviceKey"]}}}},"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"patch":{"tags":["tenant"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"anonKey":{"type":"string"},"databaseUrl":{"type":"string"},"databasePoolUrl":{"type":"string","nullable":true},"maxConnections":{"type":"number"},"jwks":{"type":"object","nullable":true},"fileSizeLimit":{"type":"number"},"jwtSecret":{"type":"string"},"serviceKey":{"type":"string"},"tracingMode":{"type":"string"},"disableEvents":{"type":"array","items":{"type":"string"},"nullable":true},"features":{"type":"object","properties":{"imageTransformation":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxResolution":{"type":"number","nullable":true}}},"purgeCache":{"type":"object","properties":{"enabled":{"type":"boolean"}}},"s3Protocol":{"type":"object","properties":{"enabled":{"type":"boolean"}}},"icebergCatalog":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxNamespaces":{"type":"number"},"maxTables":{"type":"number"},"maxCatalogs":{"type":"number"}}},"vectorBuckets":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxBuckets":{"type":"number"},"maxIndexes":{"type":"number"}}}}}}}}}},"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"put":{"tags":["tenant"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"anonKey":{"type":"string"},"databaseUrl":{"type":"string"},"databasePoolUrl":{"type":"string","nullable":true},"maxConnections":{"type":"number"},"jwks":{"type":"object","nullable":true},"fileSizeLimit":{"type":"number"},"jwtSecret":{"type":"string"},"serviceKey":{"type":"string"},"tracingMode":{"type":"string"},"disableEvents":{"type":"array","items":{"type":"string"},"nullable":true},"features":{"type":"object","properties":{"imageTransformation":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxResolution":{"type":"number","nullable":true}}},"purgeCache":{"type":"object","properties":{"enabled":{"type":"boolean"}}},"s3Protocol":{"type":"object","properties":{"enabled":{"type":"boolean"}}},"icebergCatalog":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxNamespaces":{"type":"number"},"maxTables":{"type":"number"},"maxCatalogs":{"type":"number"}}},"vectorBuckets":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxBuckets":{"type":"number"},"maxIndexes":{"type":"number"}}}}}},"required":["anonKey","databaseUrl","jwtSecret","serviceKey"]}}}},"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"delete":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/migrations":{"get":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"post":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/migrations/reset":{"post":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/migrations/jobs":{"get":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"delete":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/health":{"get":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["tenant"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/buckets/{bucketId}/orphan-objects":{"get":{"tags":["object"],"description":"List Orphaned Objects","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false},{"schema":{"type":"boolean"},"in":"query","name":"keepTmpTable","required":false},{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true},{"schema":{"type":"string"},"in":"path","name":"bucketId","required":true}],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["object"],"description":"List Orphaned Objects","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false},{"schema":{"type":"boolean"},"in":"query","name":"keepTmpTable","required":false},{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true},{"schema":{"type":"string"},"in":"path","name":"bucketId","required":true}],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"delete":{"tags":["object"],"description":"Sync Orphaned Objects","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"deleteDbKeys":{"type":"boolean"},"deleteS3Keys":{"type":"boolean"},"tmpTable":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true},{"schema":{"type":"string"},"in":"path","name":"bucketId","required":true}],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/jwks":{"post":{"tags":["jwks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"jwk":{"type":"object","properties":{"kty":{"type":"string"}},"required":["kty"]},"kind":{"type":"string"}},"required":["jwk","kind"]}}}},"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/jwks/{kid}":{"put":{"tags":["jwks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"}},"required":["active"]}}}},"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true},{"schema":{"type":"string"},"in":"path","name":"kid","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/{tenantId}/jwks/url-signing/roll":{"post":{"tags":["jwks"],"security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/tenants/jwks/generate-all-missing":{"post":{"tags":["jwks"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"get":{"tags":["jwks"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["jwks"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/migrations/migrate/fleet":{"post":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/migrations/reset/fleet":{"post":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/migrations/active":{"get":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"delete":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/migrations/progress":{"get":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/migrations/failed":{"get":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["migration"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/s3/{tenantId}/credentials":{"post":{"tags":["s3-credentials"],"description":"Create S3 Credentials","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","minLength":3,"maxLength":2000},"claims":{"type":"object","properties":{"role":{"type":"string"},"sub":{"type":"string"}},"required":["role"],"additionalProperties":true}},"required":["description"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"get":{"tags":["s3-credentials"],"description":"List S3 Credentials","parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["s3-credentials"],"description":"List S3 Credentials","parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"delete":{"tags":["s3-credentials"],"description":"Delete S3 Credentials","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true}],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/queue/move":{"post":{"tags":["queue"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fromQueue":{"type":"string"},"toQueue":{"type":"string"},"deleteJobsFromOriginalQueue":{"type":"boolean","default":false}},"required":["fromQueue","toQueue"]}}}},"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/metrics/config":{"get":{"tags":["metrics"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"head":{"tags":["metrics"],"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"put":{"tags":["metrics"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"metrics":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"}},"required":["name","enabled"]}}},"required":["metrics"]}}}},"security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Default Response"}}}}},"tags":[{"name":"tenant","description":"Tenant management"},{"name":"object","description":"Object management"},{"name":"jwks","description":"JWKS configuration"},{"name":"migration","description":"Database migrations"},{"name":"s3-credentials","description":"S3 credentials management"},{"name":"queue","description":"Queue management"},{"name":"metrics","description":"Metrics configuration"}]}
1 change: 1 addition & 0 deletions static/api.json

Large diffs are not rendered by default.

Loading