mirror of
https://github.com/Nezumi-2711/google-drive-s3.git
synced 2026-09-22 13:38:30 +00:00
1219 lines
40 KiB
YAML
1219 lines
40 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/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 }
|