mirror of
https://github.com/Nezumi-2711/google-drive-s3.git
synced 2026-09-22 13:38:30 +00:00
1446 lines
49 KiB
YAML
1446 lines
49 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Google Drive S3 API
|
|
version: 1.0.0
|
|
description: |
|
|
An intentionally small S3-compatible object-storage API backed by Google Drive.
|
|
Authenticate every non-public request with AWS Signature Version 4. Browser clients
|
|
should receive short-lived presigned URLs from a backend-for-frontend, never credentials.
|
|
servers:
|
|
- url: https://{worker-host}
|
|
variables:
|
|
worker-host:
|
|
default: your-worker.example.workers.dev
|
|
security:
|
|
- sigv4: []
|
|
tags:
|
|
- name: Objects
|
|
- name: Buckets
|
|
- name: Multipart uploads
|
|
- name: Dashboard Auth
|
|
- name: Dashboard Status
|
|
paths:
|
|
/auth/login:
|
|
post:
|
|
tags: [Dashboard Auth]
|
|
operationId: authLogin
|
|
summary: Login to dashboard
|
|
description: Authenticate with SHA-256 hashed dashboard password.
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/LoginRequest' }
|
|
responses:
|
|
'200':
|
|
description: Login successful.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/LoginResponse' }
|
|
'400':
|
|
description: Invalid request.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'401':
|
|
description: Invalid password.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'429':
|
|
description: Too many failed attempts.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'503':
|
|
description: Dashboard authentication not configured.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/auth/session:
|
|
get:
|
|
tags: [Dashboard Auth]
|
|
operationId: authSession
|
|
summary: Verify session token
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Session is valid.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
valid: { type: boolean, example: true }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'503':
|
|
description: Dashboard authentication not configured.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/auth/logout:
|
|
post:
|
|
tags: [Dashboard Auth]
|
|
operationId: authLogout
|
|
summary: Logout of dashboard session
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'204':
|
|
description: Successfully logged out.
|
|
'503':
|
|
description: Dashboard authentication not configured.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/status:
|
|
get:
|
|
tags: [Dashboard Status]
|
|
operationId: getGatewayStatus
|
|
summary: Get gateway and storage health status
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Gateway status and Drive account/quota overview.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/GatewayStatus' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'503':
|
|
description: Dashboard authentication not configured.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/integration:
|
|
get:
|
|
tags: [Dashboard Status]
|
|
operationId: getIntegrationInfo
|
|
summary: Get S3 connection details and access keys
|
|
description: Everything an external S3 client needs — endpoint, region, path-style flag, bucket list and the current access keys (metadata only, no secrets).
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Integration info with access key metadata.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/IntegrationInfo' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'503':
|
|
description: Storage root folder or authentication not configured.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/integration/keys:
|
|
post:
|
|
tags: [Dashboard Status]
|
|
operationId: createAccessKey
|
|
summary: Create a named S3 access key pair
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/CreateAccessKeyRequest' }
|
|
responses:
|
|
'201':
|
|
description: Key pair created. The secret is only returned here.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/S3AccessKeyFull' }
|
|
'400':
|
|
description: Invalid label or the maximum of 5 live keys was reached.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/integration/keys/{id}/rotate:
|
|
post:
|
|
tags: [Dashboard Status]
|
|
operationId: rotateAccessKey
|
|
summary: Rotate an access key with an optional grace period
|
|
description: Creates a replacement key carrying the same label. The old key is deleted immediately when graceSeconds is 0, otherwise it keeps authenticating until the grace period elapses. Changes propagate within 60 seconds due to edge caching.
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/RotateAccessKeyRequest' }
|
|
responses:
|
|
'200':
|
|
description: Rotation result with the new full pair and the old key's retirement timestamp.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/RotateAccessKeyResponse' }
|
|
'400':
|
|
description: Invalid graceSeconds value.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'404':
|
|
description: Access key not found.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/integration/keys/{id}/secret:
|
|
get:
|
|
tags: [Dashboard Status]
|
|
operationId: revealAccessKeySecret
|
|
summary: Reveal an access key secret
|
|
description: Rate-limited to 20 reveals per IP per 60 seconds.
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: The secret access key.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AccessKeySecret' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'404':
|
|
description: Access key not found.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'429':
|
|
description: Too many reveal attempts.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/integration/keys/{id}:
|
|
delete:
|
|
tags: [Dashboard Status]
|
|
operationId: revokeAccessKey
|
|
summary: Revoke an access key immediately
|
|
description: Deleting the last remaining key locks out all S3 clients until a new key is created. Propagates within 60 seconds.
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'204':
|
|
description: Access key revoked.
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'404':
|
|
description: Access key not found.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/buckets:
|
|
get:
|
|
tags: [Dashboard Status]
|
|
operationId: getBucketStats
|
|
summary: Get bucket list and object metrics
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- name: refresh
|
|
in: query
|
|
description: Set to 1 to bypass KV cache.
|
|
required: false
|
|
schema: { type: string, example: "1" }
|
|
responses:
|
|
'200':
|
|
description: Bucket statistics and aggregate totals.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/BucketStats' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'503':
|
|
description: Storage root folder or authentication not configured.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
post:
|
|
tags: [Dashboard Status]
|
|
operationId: createBucket
|
|
summary: Create a new bucket
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/CreateBucketRequest' }
|
|
responses:
|
|
'201':
|
|
description: Bucket created.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/BucketRecord' }
|
|
'400':
|
|
description: Invalid bucket name.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'409':
|
|
description: Bucket already exists.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'503':
|
|
description: Storage root folder or authentication not configured.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/buckets/{name}:
|
|
parameters:
|
|
- name: name
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
patch:
|
|
tags: [Dashboard Status]
|
|
operationId: updateBucket
|
|
summary: Update bucket settings (toggle public read or rename)
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/UpdateBucketRequest' }
|
|
responses:
|
|
'200':
|
|
description: Bucket updated.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/BucketRecord' }
|
|
'400':
|
|
description: Invalid request.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'404':
|
|
description: Bucket not found.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'409':
|
|
description: Target bucket name conflict.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
delete:
|
|
tags: [Dashboard Status]
|
|
operationId: deleteBucket
|
|
summary: Delete an empty bucket
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'204':
|
|
description: Bucket deleted.
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'404':
|
|
description: Bucket not found.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'409':
|
|
description: Bucket is not empty.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/import-candidates:
|
|
get:
|
|
tags: [Dashboard Status]
|
|
operationId: listImportCandidates
|
|
summary: List Drive folders available for import
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: List of folders under Drive root.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/ImportCandidatesResponse' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/import:
|
|
post:
|
|
tags: [Dashboard Status]
|
|
operationId: importBuckets
|
|
summary: Import folders from Drive root into storage root folder
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/ImportBucketsRequest' }
|
|
responses:
|
|
'200':
|
|
description: Import results.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/ImportResult' }
|
|
'400':
|
|
description: Invalid body.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
'401':
|
|
description: Invalid or expired session.
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/AuthError' }
|
|
/api/objects:
|
|
get:
|
|
tags: [Objects API]
|
|
operationId: listObjectsApi
|
|
summary: List objects and folders in a bucket (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: prefix
|
|
schema: { type: string, default: "" }
|
|
- in: query
|
|
name: delimiter
|
|
schema: { type: string, default: "/" }
|
|
responses:
|
|
'200':
|
|
description: Object and folder listing.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
bucket: { type: string }
|
|
prefix: { type: string }
|
|
delimiter: { type: string, nullable: true }
|
|
folders:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
prefix: { type: string }
|
|
name: { type: string }
|
|
objects:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
key: { type: string }
|
|
name: { type: string }
|
|
size: { type: integer }
|
|
contentType: { type: string }
|
|
lastModified: { type: string, nullable: true }
|
|
etag: { type: string }
|
|
truncated: { type: boolean }
|
|
'400':
|
|
description: Missing query parameters.
|
|
'401':
|
|
description: Unauthorized.
|
|
'404':
|
|
description: Bucket not found.
|
|
delete:
|
|
tags: [Objects API]
|
|
operationId: deleteObjectApi
|
|
summary: Delete an object (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: key
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'204':
|
|
description: Object deleted.
|
|
'401':
|
|
description: Unauthorized.
|
|
'404':
|
|
description: Bucket or object not found.
|
|
/api/objects/metadata:
|
|
get:
|
|
tags: [Objects API]
|
|
operationId: getObjectMetadataApi
|
|
summary: Get object metadata (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: key
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Object metadata.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
key: { type: string }
|
|
name: { type: string }
|
|
size: { type: integer }
|
|
contentType: { type: string }
|
|
lastModified: { type: string, nullable: true }
|
|
etag: { type: string }
|
|
'404':
|
|
description: Object not found.
|
|
/api/objects/content:
|
|
get:
|
|
tags: [Objects API]
|
|
operationId: getObjectContentApi
|
|
summary: Download object content (Dashboard Session Auth or Ticket)
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: key
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: ticket
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Binary object data.
|
|
'206':
|
|
description: Partial content.
|
|
'404':
|
|
description: Object not found.
|
|
put:
|
|
tags: [Objects API]
|
|
operationId: putObjectContentApi
|
|
summary: Direct single-part upload (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: key
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Object uploaded.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
key: { type: string }
|
|
etag: { type: string }
|
|
size: { type: integer }
|
|
/api/objects/folder:
|
|
post:
|
|
tags: [Objects API]
|
|
operationId: createFolderApi
|
|
summary: Create a folder (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [bucket, prefix]
|
|
properties:
|
|
bucket: { type: string }
|
|
prefix: { type: string }
|
|
responses:
|
|
'201':
|
|
description: Folder created.
|
|
'409':
|
|
description: Folder already exists.
|
|
delete:
|
|
tags: [Objects API]
|
|
operationId: deleteFolderApi
|
|
summary: Delete a folder (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: prefix
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: recursive
|
|
schema: { type: string, enum: ["0", "1"] }
|
|
responses:
|
|
'204':
|
|
description: Folder deleted.
|
|
'409':
|
|
description: Folder is not empty.
|
|
/api/objects/download-ticket:
|
|
post:
|
|
tags: [Objects API]
|
|
operationId: createDownloadTicket
|
|
summary: Create a temporary single-use download ticket (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [bucket, key]
|
|
properties:
|
|
bucket: { type: string }
|
|
key: { type: string }
|
|
responses:
|
|
'201':
|
|
description: Download ticket created.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
ticket: { type: string }
|
|
downloadUrl: { type: string }
|
|
expiresIn: { type: integer }
|
|
/api/objects/uploads:
|
|
post:
|
|
tags: [Objects API]
|
|
operationId: initiateMultipartApi
|
|
summary: Initiate a multipart upload (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [bucket, key]
|
|
properties:
|
|
bucket: { type: string }
|
|
key: { type: string }
|
|
contentType: { type: string }
|
|
responses:
|
|
'201':
|
|
description: Upload initiated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
uploadId: { type: string }
|
|
bucket: { type: string }
|
|
key: { type: string }
|
|
partSize: { type: integer }
|
|
delete:
|
|
tags: [Objects API]
|
|
operationId: abortMultipartApi
|
|
summary: Abort a multipart upload (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: key
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: uploadId
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'204':
|
|
description: Upload aborted.
|
|
/api/objects/uploads/part:
|
|
put:
|
|
tags: [Objects API]
|
|
operationId: uploadPartApi
|
|
summary: Upload a part (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: key
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: uploadId
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: partNumber
|
|
required: true
|
|
schema: { type: integer }
|
|
responses:
|
|
'200':
|
|
description: Part uploaded.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
partNumber: { type: integer }
|
|
etag: { type: string }
|
|
/api/objects/uploads/complete:
|
|
post:
|
|
tags: [Objects API]
|
|
operationId: completeMultipartApi
|
|
summary: Complete a multipart upload (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [bucket, key, uploadId, parts]
|
|
properties:
|
|
bucket: { type: string }
|
|
key: { type: string }
|
|
uploadId: { type: string }
|
|
parts:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [partNumber, etag]
|
|
properties:
|
|
partNumber: { type: integer }
|
|
etag: { type: string }
|
|
totalSize: { type: integer }
|
|
responses:
|
|
'200':
|
|
description: Upload completed.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
key: { type: string }
|
|
etag: { type: string }
|
|
/api/objects/uploads/parts:
|
|
get:
|
|
tags: [Objects API]
|
|
operationId: listPartsApi
|
|
summary: List uploaded parts (Dashboard Session Auth)
|
|
security:
|
|
- sessionBearer: []
|
|
parameters:
|
|
- in: query
|
|
name: bucket
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: key
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: uploadId
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Parts list.
|
|
/{bucket}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/Bucket'
|
|
get:
|
|
tags: [Buckets]
|
|
operationId: listObjects
|
|
summary: List objects in a bucket
|
|
description: List objects recursively, or use `delimiter=/` to return immediate folders as `CommonPrefixes`.
|
|
parameters:
|
|
- $ref: '#/components/parameters/Prefix'
|
|
- $ref: '#/components/parameters/Delimiter'
|
|
responses:
|
|
'200':
|
|
description: Object listing.
|
|
content:
|
|
application/xml:
|
|
schema: { $ref: '#/components/schemas/ListBucketResult' }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'405': { $ref: '#/components/responses/MethodNotAllowed' }
|
|
head:
|
|
tags: [Buckets]
|
|
operationId: headBucket
|
|
summary: Check bucket access
|
|
responses:
|
|
'200': { description: The bucket is configured and accessible. }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'405': { $ref: '#/components/responses/MethodNotAllowed' }
|
|
/{bucket}?uploads:
|
|
parameters:
|
|
- $ref: '#/components/parameters/Bucket'
|
|
get:
|
|
tags: [Multipart uploads]
|
|
operationId: listMultipartUploads
|
|
summary: List multipart uploads
|
|
description: Always returns an empty list; active multipart uploads cannot be enumerated.
|
|
responses:
|
|
'200':
|
|
description: Empty multipart upload listing.
|
|
content:
|
|
application/xml:
|
|
schema: { $ref: '#/components/schemas/ListMultipartUploadsResult' }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
/{bucket}/{key}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/Bucket'
|
|
- $ref: '#/components/parameters/Key'
|
|
put:
|
|
tags: [Objects]
|
|
operationId: putObject
|
|
summary: Create or replace an object
|
|
parameters:
|
|
- $ref: '#/components/parameters/ContentType'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/octet-stream:
|
|
schema: { type: string, format: binary }
|
|
'*/*':
|
|
schema: { type: string, format: binary }
|
|
responses:
|
|
'200':
|
|
description: Object uploaded.
|
|
headers:
|
|
ETag: { $ref: '#/components/headers/ETag' }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
get:
|
|
tags: [Objects]
|
|
operationId: getObject
|
|
summary: Download an object
|
|
parameters:
|
|
- name: Range
|
|
in: header
|
|
schema: { type: string, example: bytes=0-1023 }
|
|
responses:
|
|
'200':
|
|
description: Full object data.
|
|
headers:
|
|
ETag: { $ref: '#/components/headers/ETag' }
|
|
Content-Length: { $ref: '#/components/headers/ContentLength' }
|
|
Last-Modified: { $ref: '#/components/headers/LastModified' }
|
|
Accept-Ranges: { schema: { type: string, example: bytes } }
|
|
content:
|
|
application/octet-stream:
|
|
schema: { type: string, format: binary }
|
|
'206':
|
|
description: Requested byte range.
|
|
headers:
|
|
Content-Range: { schema: { type: string, example: bytes 0-1023/4096 } }
|
|
Content-Length: { $ref: '#/components/headers/ContentLength' }
|
|
content:
|
|
application/octet-stream:
|
|
schema: { type: string, format: binary }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'404': { $ref: '#/components/responses/NoSuchKey' }
|
|
head:
|
|
tags: [Objects]
|
|
operationId: headObject
|
|
summary: Get object metadata
|
|
responses:
|
|
'200':
|
|
description: Object metadata in headers.
|
|
headers:
|
|
ETag: { $ref: '#/components/headers/ETag' }
|
|
Content-Length: { $ref: '#/components/headers/ContentLength' }
|
|
Last-Modified: { $ref: '#/components/headers/LastModified' }
|
|
Accept-Ranges: { schema: { type: string, example: bytes } }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'404': { $ref: '#/components/responses/NoSuchKey' }
|
|
delete:
|
|
tags: [Objects]
|
|
operationId: deleteObject
|
|
summary: Delete an object
|
|
responses:
|
|
'204': { description: Object deleted. }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'404': { $ref: '#/components/responses/NoSuchKey' }
|
|
/{bucket}/{key}?uploads:
|
|
parameters:
|
|
- $ref: '#/components/parameters/Bucket'
|
|
- $ref: '#/components/parameters/Key'
|
|
post:
|
|
tags: [Multipart uploads]
|
|
operationId: createMultipartUpload
|
|
summary: Initiate a multipart upload
|
|
parameters:
|
|
- $ref: '#/components/parameters/ContentType'
|
|
responses:
|
|
'200':
|
|
description: Upload initiated.
|
|
content:
|
|
application/xml:
|
|
schema: { $ref: '#/components/schemas/InitiateMultipartUploadResult' }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
/{bucket}/{key}?uploadId={uploadId}&partNumber={partNumber}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/Bucket'
|
|
- $ref: '#/components/parameters/Key'
|
|
- $ref: '#/components/parameters/UploadId'
|
|
- $ref: '#/components/parameters/PartNumber'
|
|
put:
|
|
tags: [Multipart uploads]
|
|
operationId: uploadPart
|
|
summary: Upload one multipart part
|
|
description: Parts must arrive strictly sequentially beginning with part number 1.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/octet-stream:
|
|
schema: { type: string, format: binary }
|
|
responses:
|
|
'200':
|
|
description: Part accepted.
|
|
headers:
|
|
ETag: { $ref: '#/components/headers/ETag' }
|
|
'400': { $ref: '#/components/responses/InvalidArgument' }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'404': { $ref: '#/components/responses/NoSuchUpload' }
|
|
'503': { $ref: '#/components/responses/SlowDown' }
|
|
/{bucket}/{key}?uploadId={uploadId}:
|
|
parameters:
|
|
- $ref: '#/components/parameters/Bucket'
|
|
- $ref: '#/components/parameters/Key'
|
|
- $ref: '#/components/parameters/UploadId'
|
|
post:
|
|
tags: [Multipart uploads]
|
|
operationId: completeMultipartUpload
|
|
summary: Complete a multipart upload
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/xml:
|
|
schema: { $ref: '#/components/schemas/CompleteMultipartUpload' }
|
|
responses:
|
|
'200':
|
|
description: Upload completed.
|
|
content:
|
|
application/xml:
|
|
schema: { $ref: '#/components/schemas/CompleteMultipartUploadResult' }
|
|
'400': { $ref: '#/components/responses/InvalidArgument' }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'404': { $ref: '#/components/responses/NoSuchUpload' }
|
|
get:
|
|
tags: [Multipart uploads]
|
|
operationId: listParts
|
|
summary: List uploaded parts
|
|
parameters:
|
|
- name: part-number-marker
|
|
in: query
|
|
schema: { type: integer, minimum: 0, default: 0 }
|
|
- name: max-parts
|
|
in: query
|
|
schema: { type: integer, minimum: 1, maximum: 1000, default: 1000 }
|
|
responses:
|
|
'200':
|
|
description: Current part list.
|
|
content:
|
|
application/xml:
|
|
schema: { $ref: '#/components/schemas/ListPartsResult' }
|
|
'400': { $ref: '#/components/responses/InvalidArgument' }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'404': { $ref: '#/components/responses/NoSuchUpload' }
|
|
delete:
|
|
tags: [Multipart uploads]
|
|
operationId: abortMultipartUpload
|
|
summary: Abort a multipart upload
|
|
responses:
|
|
'204': { description: Upload aborted. }
|
|
'403': { $ref: '#/components/responses/AccessDenied' }
|
|
'404': { $ref: '#/components/responses/NoSuchUpload' }
|
|
components:
|
|
securitySchemes:
|
|
sigv4:
|
|
type: apiKey
|
|
in: header
|
|
name: Authorization
|
|
description: AWS Signature Version 4 header authentication or equivalent `X-Amz-*` presigned query parameters.
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
description: Session token returned from `/auth/login`.
|
|
parameters:
|
|
Bucket:
|
|
name: bucket
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
Key:
|
|
name: key
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
Prefix:
|
|
name: prefix
|
|
in: query
|
|
schema: { type: string, default: '' }
|
|
Delimiter:
|
|
name: delimiter
|
|
in: query
|
|
schema: { type: string, example: / }
|
|
UploadId:
|
|
name: uploadId
|
|
in: query
|
|
required: true
|
|
schema: { type: string }
|
|
PartNumber:
|
|
name: partNumber
|
|
in: query
|
|
required: true
|
|
schema: { type: integer, minimum: 1, maximum: 10000 }
|
|
ContentType:
|
|
name: Content-Type
|
|
in: header
|
|
schema: { type: string, example: application/octet-stream }
|
|
headers:
|
|
ETag:
|
|
schema: { type: string, example: '"d41d8cd98f00b204e9800998ecf8427e"' }
|
|
ContentLength:
|
|
schema: { type: integer, minimum: 0 }
|
|
LastModified:
|
|
schema: { type: string, format: date-time }
|
|
responses:
|
|
AccessDenied:
|
|
description: Authentication failed, the access key is not recognized, the presigned URL expired, or bucket access is denied.
|
|
content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
|
|
NoSuchKey:
|
|
description: The object does not exist.
|
|
content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
|
|
NoSuchUpload:
|
|
description: The multipart upload does not exist.
|
|
content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
|
|
InvalidArgument:
|
|
description: A required parameter or body is invalid.
|
|
content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
|
|
NotImplemented:
|
|
description: This S3 operation is not supported.
|
|
content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
|
|
SlowDown:
|
|
description: Parts are out of sequence or the upload is busy; retry later.
|
|
content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
|
|
MethodNotAllowed:
|
|
description: The method/path combination is unsupported.
|
|
content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
|
|
schemas:
|
|
LoginRequest:
|
|
type: object
|
|
required: [passwordHash]
|
|
properties:
|
|
passwordHash:
|
|
type: string
|
|
description: SHA-256 hash of the dashboard password in lowercase hex.
|
|
example: ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f
|
|
LoginResponse:
|
|
type: object
|
|
required: [token, expiresIn]
|
|
properties:
|
|
token:
|
|
type: string
|
|
description: Session token.
|
|
expiresIn:
|
|
type: integer
|
|
description: Token TTL in seconds.
|
|
example: 43200
|
|
GatewayStatus:
|
|
type: object
|
|
required: [gateway, drive, checkedAt]
|
|
properties:
|
|
gateway:
|
|
type: object
|
|
required: [status, region, multipartEnabled, etagStyle, docsEnabled, buckets, publicReadBuckets, rootFolder, corsOrigins, credentials]
|
|
properties:
|
|
status: { type: string, enum: [ok, degraded] }
|
|
region: { type: string, example: auto }
|
|
multipartEnabled: { type: boolean }
|
|
etagStyle: { type: string, enum: [md5, multipart] }
|
|
docsEnabled: { type: boolean }
|
|
buckets: { type: array, items: { type: string } }
|
|
publicReadBuckets: { type: array, items: { type: string } }
|
|
rootFolder:
|
|
type: object
|
|
required: [name, id, configured]
|
|
properties:
|
|
name: { type: string, nullable: true }
|
|
id: { type: string, nullable: true }
|
|
configured: { type: boolean }
|
|
corsOrigins: { type: array, items: { type: string } }
|
|
credentials:
|
|
type: object
|
|
required: [s3Keys, googleOAuth, dashboardPassword]
|
|
properties:
|
|
s3Keys: { type: boolean }
|
|
googleOAuth: { type: boolean }
|
|
dashboardPassword: { type: boolean }
|
|
drive:
|
|
type: object
|
|
required: [connected, account, quota, error]
|
|
properties:
|
|
connected: { type: boolean }
|
|
account:
|
|
type: object
|
|
nullable: true
|
|
properties:
|
|
email: { type: string, nullable: true }
|
|
displayName: { type: string, nullable: true }
|
|
quota:
|
|
type: object
|
|
nullable: true
|
|
properties:
|
|
limit: { type: integer, nullable: true }
|
|
usage: { type: integer }
|
|
usageInDrive: { type: integer }
|
|
usageInDriveTrash: { type: integer }
|
|
free: { type: integer, nullable: true }
|
|
percentUsed: { type: number, nullable: true }
|
|
error: { type: string, nullable: true }
|
|
checkedAt:
|
|
type: string
|
|
format: date-time
|
|
BucketStats:
|
|
type: object
|
|
required: [buckets, totals, cachedAt]
|
|
properties:
|
|
buckets:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [name, objectCount, totalSize, lastModified, truncated, publicRead, error]
|
|
properties:
|
|
name: { type: string }
|
|
objectCount: { type: integer }
|
|
totalSize: { type: integer }
|
|
lastModified: { type: string, format: date-time, nullable: true }
|
|
truncated: { type: boolean }
|
|
publicRead: { type: boolean }
|
|
error: { type: string, nullable: true }
|
|
totals:
|
|
type: object
|
|
required: [buckets, objectCount, totalSize]
|
|
properties:
|
|
buckets: { type: integer }
|
|
objectCount: { type: integer }
|
|
totalSize: { type: integer }
|
|
cachedAt:
|
|
type: string
|
|
format: date-time
|
|
BucketRecord:
|
|
type: object
|
|
required: [name, folderId, publicRead, createdTime]
|
|
properties:
|
|
name: { type: string, example: assets }
|
|
folderId: { type: string, example: 1A2b3C4d5E6f }
|
|
publicRead: { type: boolean, example: false }
|
|
createdTime: { type: string, format: date-time, nullable: true }
|
|
CreateBucketRequest:
|
|
type: object
|
|
required: [name]
|
|
properties:
|
|
name: { type: string, example: assets }
|
|
publicRead: { type: boolean, default: false }
|
|
UpdateBucketRequest:
|
|
type: object
|
|
properties:
|
|
publicRead: { type: boolean }
|
|
name: { type: string }
|
|
ImportCandidate:
|
|
type: object
|
|
required: [name, folderId, objectCount]
|
|
properties:
|
|
name: { type: string, example: legacy-photos }
|
|
folderId: { type: string, example: 1A2b3C4d5E6f }
|
|
objectCount: { type: integer, example: 42 }
|
|
ImportCandidatesResponse:
|
|
type: object
|
|
required: [candidates]
|
|
properties:
|
|
candidates:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/ImportCandidate' }
|
|
ImportBucketsRequest:
|
|
type: object
|
|
required: [names]
|
|
properties:
|
|
names:
|
|
type: array
|
|
items: { type: string }
|
|
ImportResult:
|
|
type: object
|
|
required: [imported, failed]
|
|
properties:
|
|
imported:
|
|
type: array
|
|
items: { type: string }
|
|
failed:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [name, error]
|
|
properties:
|
|
name: { type: string }
|
|
error: { type: string }
|
|
AuthError:
|
|
type: object
|
|
required: [message]
|
|
properties:
|
|
message:
|
|
type: string
|
|
example: Invalid password
|
|
S3Error:
|
|
type: object
|
|
xml: { name: Error }
|
|
required: [Code, Message, RequestId]
|
|
properties:
|
|
Code: { type: string, xml: { name: Code } }
|
|
Message: { type: string, xml: { name: Message } }
|
|
Resource: { type: string, xml: { name: Resource } }
|
|
RequestId: { type: string, xml: { name: RequestId } }
|
|
Object:
|
|
type: object
|
|
xml: { name: Contents }
|
|
properties:
|
|
Key: { type: string }
|
|
LastModified: { type: string, format: date-time }
|
|
ETag: { type: string }
|
|
Size: { type: integer }
|
|
StorageClass: { type: string, example: STANDARD }
|
|
ListBucketResult:
|
|
type: object
|
|
xml: { name: ListBucketResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
|
|
properties:
|
|
Name: { type: string }
|
|
Prefix: { type: string }
|
|
Delimiter: { type: string }
|
|
MaxKeys: { type: integer, example: 1000 }
|
|
IsTruncated: { type: boolean }
|
|
Contents: { type: array, items: { $ref: '#/components/schemas/Object' }, xml: { wrapped: false } }
|
|
InitiateMultipartUploadResult:
|
|
type: object
|
|
xml: { name: InitiateMultipartUploadResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
|
|
required: [Bucket, Key, UploadId]
|
|
properties:
|
|
Bucket: { type: string }
|
|
Key: { type: string }
|
|
UploadId: { type: string }
|
|
CompleteMultipartUpload:
|
|
type: object
|
|
xml: { name: CompleteMultipartUpload }
|
|
required: [Part]
|
|
properties:
|
|
Part:
|
|
type: array
|
|
minItems: 1
|
|
xml: { wrapped: false }
|
|
items:
|
|
type: object
|
|
xml: { name: Part }
|
|
required: [PartNumber, ETag]
|
|
properties:
|
|
PartNumber: { type: integer }
|
|
ETag: { type: string }
|
|
CompleteMultipartUploadResult:
|
|
type: object
|
|
xml: { name: CompleteMultipartUploadResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
|
|
properties:
|
|
Location: { type: string }
|
|
Bucket: { type: string }
|
|
Key: { type: string }
|
|
ETag: { type: string }
|
|
ListPartsResult:
|
|
type: object
|
|
xml: { name: ListPartsResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
|
|
properties:
|
|
Bucket: { type: string }
|
|
Key: { type: string }
|
|
UploadId: { type: string }
|
|
NextPartNumberMarker: { type: integer }
|
|
IsTruncated: { type: boolean }
|
|
Part:
|
|
type: array
|
|
xml: { wrapped: false }
|
|
items:
|
|
type: object
|
|
properties:
|
|
PartNumber: { type: integer }
|
|
LastModified: { type: string, format: date-time }
|
|
ETag: { type: string }
|
|
Size: { type: integer }
|
|
ListMultipartUploadsResult:
|
|
type: object
|
|
xml: { name: ListMultipartUploadsResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
|
|
properties:
|
|
Bucket: { type: string }
|
|
KeyMarker: { type: string }
|
|
UploadIdMarker: { type: string }
|
|
NextKeyMarker: { type: string }
|
|
NextUploadIdMarker: { type: string }
|
|
MaxUploads: { type: integer, example: 1000 }
|
|
IsTruncated: { type: boolean, example: false }
|
|
S3AccessKeyMetadata:
|
|
type: object
|
|
required: [accessKeyId, label, createdAt, expiresAt]
|
|
properties:
|
|
accessKeyId: { type: string, example: GDS7Q2JXK4M9VBTZRWEA }
|
|
label: { type: string, example: rclone-backup }
|
|
createdAt: { type: string, format: date-time }
|
|
expiresAt: { type: string, format: date-time, nullable: true, description: Set when the key is retiring after a rotation. }
|
|
S3AccessKeyFull:
|
|
type: object
|
|
required: [accessKeyId, secretAccessKey, label, createdAt, expiresAt]
|
|
properties:
|
|
accessKeyId: { type: string, example: GDS7Q2JXK4M9VBTZRWEA }
|
|
secretAccessKey: { type: string, description: Only returned on creation and reveal. }
|
|
label: { type: string, example: rclone-backup }
|
|
createdAt: { type: string, format: date-time }
|
|
expiresAt: { type: string, format: date-time, nullable: true }
|
|
CreateAccessKeyRequest:
|
|
type: object
|
|
required: [label]
|
|
properties:
|
|
label:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 32
|
|
pattern: '^[a-zA-Z0-9 _-]+$'
|
|
example: n8n-media-uploads
|
|
RotateAccessKeyRequest:
|
|
type: object
|
|
required: [graceSeconds]
|
|
properties:
|
|
graceSeconds:
|
|
type: integer
|
|
enum: [0, 3600, 86400, 604800]
|
|
description: 0 revokes the old key immediately; otherwise the old key keeps working for this long.
|
|
default: 86400
|
|
RotateAccessKeyResponse:
|
|
type: object
|
|
required: [created, previous]
|
|
properties:
|
|
created: { $ref: '#/components/schemas/S3AccessKeyFull' }
|
|
previous:
|
|
type: object
|
|
required: [accessKeyId, expiresAt]
|
|
properties:
|
|
accessKeyId: { type: string }
|
|
expiresAt: { type: string, format: date-time, nullable: true }
|
|
AccessKeySecret:
|
|
type: object
|
|
required: [secretAccessKey]
|
|
properties:
|
|
secretAccessKey: { type: string }
|
|
IntegrationInfo:
|
|
type: object
|
|
required: [endpoint, region, forcePathStyle, buckets, publicReadBuckets, multipartEnabled, etagStyle, corsOrigins, docsUrl, openApiUrl, accessKeys, limits]
|
|
properties:
|
|
endpoint: { type: string, format: uri, example: https://s3-google-drive.example.workers.dev }
|
|
region: { type: string, example: auto }
|
|
forcePathStyle: { type: boolean, const: true }
|
|
buckets: { type: array, items: { type: string } }
|
|
publicReadBuckets: { type: array, items: { type: string } }
|
|
multipartEnabled: { type: boolean }
|
|
etagStyle: { type: string, enum: [md5, multipart] }
|
|
corsOrigins: { type: array, items: { type: string } }
|
|
docsUrl: { type: string, nullable: true }
|
|
openApiUrl: { type: string, nullable: true }
|
|
accessKeys:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/S3AccessKeyMetadata' }
|
|
limits:
|
|
type: object
|
|
required: [maxAccessKeys, keyPropagationSeconds, presignExpiryMaxSeconds]
|
|
properties:
|
|
maxAccessKeys: { type: integer, example: 5 }
|
|
keyPropagationSeconds: { type: integer, example: 60, description: Edge-cache TTL — revoke/rotate take up to this long to propagate. }
|
|
presignExpiryMaxSeconds: { type: integer, example: 604800 }
|