diff --git a/services/s3.service.js b/services/s3.service.js index ff47e32..bd46d3e 100644 --- a/services/s3.service.js +++ b/services/s3.service.js @@ -176,14 +176,24 @@ async function getSignedDownloadUrl(key, expiresInSeconds = 3600) { // ─── getPublicUrl ───────────────────────────────────────────────────────────── // -// Browser-facing object URL, unsigned. garage-anon-proxy (fronting -// S3_PUBLIC_URL) authenticates every request server-side with real -// credentials, so a client-side presign adds nothing but a broken request — -// see getSignedDownloadUrl() above. Use this wherever a URL is handed -// directly to the browser (e.g. thumbnail previews). +// Browser-facing pre-signed GET URL, signed against S3_PUBLIC_URL. Safe again +// now that garage-anon-proxy detects a valid query-string SigV4 signature and +// forwards it unmodified instead of re-signing on top of it (see +// isPresignedRequest() in garage-anon-proxy/server.js) — that mismatch used to +// produce Garage's 400 "Header `x-amz-date` should be signed". Use this +// wherever a URL is handed directly to the browser (e.g. thumbnail previews). // -async function getPublicUrl(key, bucket = DEFAULT_BUCKET) { - return buildPublicUrl(key, bucket); +// expiresInSeconds defaults to 4h to outlive the longest cache window a caller +// hands this URL out under (client media token TTL — see media.controller.js), +// so a cached thumbnail_url never outlives its own signature. +// +async function getPublicUrl(key, bucket = DEFAULT_BUCKET, expiresInSeconds = 4 * 60 * 60) { + const client = getPublicClient(); + return getSignedUrl( + client, + new GetObjectCommand({ Bucket: bucket, Key: key }), + { expiresIn: expiresInSeconds } + ); } // ─── getObjectStream ────────────────────────────────────────────────────────