mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 07:41:23 +00:00
feat: Presigned object URLs (s3 driver)
POST /buckets/:id/object-presigns mints SigV4 query-signed GET/PUT URLs so clients move bytes directly against S3 — large transfers skip the backend entirely. - storage.Presigner optional interface; S3Store implements, local driver returns a clear error (no external surface to sign). - TTL 60s–1h (default 15m); download requires read role + existing object row (no key probing); upload requires write role. - S3_PUBLIC_ENDPOINT rewrites the client-facing host for internal S3_ENDPOINTs — signatures bind the host, URLs are valid only where minted. - CLI: primora objects:presign <bucket> <key> [--upload] [--ttl]. Verified live against cloudserver: GET fetched the object with no auth headers; PUT pushed 1 MB direct to S3; unsigned list → 403; untracked-key download presign → 404. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
a84049b8fc
commit
fd36389328
22 files changed
+446
-7
No files matched your search
@@ -677,6 +677,37 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BucketObject"
|
||||
/buckets/{bucketID}/object-presigns:
|
||||
post:
|
||||
tags: [Storage]
|
||||
operationId: presignBucketObject
|
||||
description: >-
|
||||
Mint a short-lived URL the client calls against object storage
|
||||
directly — keeps large transfers off the backend. Requires
|
||||
BACKEND_STORAGE_DRIVER=s3; the URL's host comes from
|
||||
S3_PUBLIC_ENDPOINT (or S3_ENDPOINT when unset). Signatures bind the
|
||||
host — a presigned URL is valid only at the endpoint it was minted
|
||||
for.
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- apiKeyAuth: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/BucketID"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PresignObjectRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: Presigned URL
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PresignedURL"
|
||||
"404":
|
||||
description: Bucket or object not found (download)
|
||||
/buckets/{bucketID}:
|
||||
patch:
|
||||
tags: [Storage]
|
||||
@@ -3130,6 +3161,36 @@ components:
|
||||
format: uuid
|
||||
nullable: true
|
||||
required: [objectKey, newObjectKey]
|
||||
PresignObjectRequest:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
description: Object key inside the bucket.
|
||||
op:
|
||||
type: string
|
||||
enum: [download, upload]
|
||||
default: download
|
||||
description: download → GET URL (object must exist); upload → PUT URL (creates/overwrites).
|
||||
ttl_seconds:
|
||||
type: integer
|
||||
minimum: 60
|
||||
maximum: 3600
|
||||
default: 900
|
||||
required: [key]
|
||||
PresignedURL:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: Fully-signed URL — call it with the returned method, no auth header needed.
|
||||
method:
|
||||
type: string
|
||||
enum: [GET, PUT]
|
||||
expires_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required: [url, method, expires_at]
|
||||
PaginationMetadata:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in new issue
Block a user