import { AwsClient } from 'https://esm.sh/aws4fetch@1.0.17' const CUSTOM_MEDIA_DOMAIN = (Deno.env.get("CUSTOM_MEDIA_DOMAIN") ?? "https://img.sojorn.net").trim(); const CUSTOM_VIDEO_DOMAIN = (Deno.env.get("CUSTOM_VIDEO_DOMAIN") ?? "https://quips.sojorn.net").trim(); const DEFAULT_BUCKET_NAME = "sojorn-media"; const RESOLVED_BUCKET = (Deno.env.get("R2_BUCKET_NAME") ?? DEFAULT_BUCKET_NAME).trim(); function normalizeKey(key: string): string { let normalized = key.replace(/^\/+/, ""); if (RESOLVED_BUCKET && normalized.startsWith(`${RESOLVED_BUCKET}/`)) { normalized = normalized.slice(RESOLVED_BUCKET.length + 1); } return normalized; } function extractObjectKey(input: string): string { const trimmed = input.trim(); if (!trimmed) { throw new Error("Missing file key"); } try { const url = new URL(trimmed); const key = decodeURIComponent(url.pathname); return normalizeKey(key); } catch { return normalizeKey(trimmed); } } export function transformLegacyMediaUrl(input: string): string | null { const trimmed = input.trim(); if (!trimmed) return null; try { const url = new URL(trimmed); // Handle legacy media.sojorn.net URLs if (url.hostname === 'media.sojorn.net') { const key = decodeURIComponent(url.pathname); return key; } return null; } catch { return null; } } // Deprecated: no-op signer retained for compatibility export async function signR2Url(fileKey: string, expiresIn: number = 3600): Promise { return await trySignR2Url(fileKey, undefined, expiresIn) ?? fileKey; } export async function trySignR2Url(fileKey: string, bucket?: string, expiresIn: number = 3600): Promise { try { const key = normalizeKey(extractObjectKey(fileKey)); // Check if we have credentials to sign. If not, fallback to public URL. const ACCOUNT_ID = Deno.env.get('R2_ACCOUNT_ID'); const ACCESS_KEY = Deno.env.get('R2_ACCESS_KEY'); const SECRET_KEY = Deno.env.get('R2_SECRET_KEY'); const isVideo = key.toLowerCase().endsWith('.mp4') || key.toLowerCase().endsWith('.mov') || key.toLowerCase().endsWith('.webm') || bucket === 'sojorn-videos'; if (!ACCOUNT_ID || !ACCESS_KEY || !SECRET_KEY) { console.warn("Missing R2 credentials for signing. Falling back to public domain."); const domain = isVideo ? CUSTOM_VIDEO_DOMAIN : CUSTOM_MEDIA_DOMAIN; if (domain && domain.startsWith("http")) { return `${domain.replace(/\/+$/, "")}/${key}`; } return fileKey; } const r2 = new AwsClient({ accessKeyId: ACCESS_KEY, secretAccessKey: SECRET_KEY, region: 'auto', service: 's3', }); const targetBucket = bucket || (isVideo ? 'sojorn-videos' : 'sojorn-media'); // We sign against the actual R2 endpoint to ensure auth works, // but the SignedMediaImage can handle redirect/proxying if needed. const url = new URL(`https://${ACCOUNT_ID}.r2.cloudflarestorage.com/${targetBucket}/${key}`); // Add expiration url.searchParams.set('X-Amz-Expires', expiresIn.toString()); const signedRequest = await r2.sign(url, { method: "GET", aws: { signQuery: true, allHeaders: false }, }); return signedRequest.url; } catch (error) { console.error("R2 signing failed", { fileKey, error: error instanceof Error ? error.message : String(error), }); return null; } }