// Updated Cloudflare Worker for Backblaze B2 with NestJS Backend Resizing export default { async fetch(request, env, ctx) { try { const url = new URL(request.url); // 1. Restrict to CDN domain only if (url.hostname !== 'cdn.aksharbhesaniya.dev') { return new Response('Forbidden', { status: 403 }); } // 2. Serve from cache if available const cache = caches.default; const cacheKey = new Request(url.toString(), request); const cached = await cache.match(cacheKey); if (cached) { console.log('Cache HIT:', url.toString()); return cached; } console.log('Cache MISS:', url.toString()); // 3. Read resize parameters const width = url.searchParams.get('w'); const height = url.searchParams.get('h'); const quality = url.searchParams.get('q'); // 4. Check if resizing is requested const needsResize = width || height || quality; if (needsResize) { // ===== RESIZE PATH: Proxy to NestJS Backend ===== const backendUrl = env.BACKEND_URL || 'https://api.streamflix.com'; const filePath = url.pathname.substring(1); // Remove leading slash // Build backend resize URL const resizeUrl = new URL(`${backendUrl}/backblaze/resize`); resizeUrl.searchParams.set('path', filePath); if (width) resizeUrl.searchParams.set('w', width); if (height) resizeUrl.searchParams.set('h', height); if (quality) resizeUrl.searchParams.set('q', quality); console.log('Fetching resized image from backend:', resizeUrl.toString()); // Fetch resized image from NestJS backend const backendResponse = await fetch(resizeUrl.toString()); if (!backendResponse.ok) { return new Response('Resize failed', { status: backendResponse.status }); } // Cache and return resized image const response = new Response(backendResponse.body, { headers: { 'Content-Type': backendResponse.headers.get('Content-Type') || 'image/jpeg', 'Cache-Control': 'public, max-age=31536000', // Cache for 1 year 'X-Resize-Backend': 'NestJS', }, }); ctx.waitUntil(cache.put(cacheKey, response.clone())); return response; } else { // ===== ORIGINAL PATH: Serve from B2 directly ===== // 5. Load B2 secrets const KEY_ID = env.B2_KEY_ID; const APP_KEY = env.B2_APP_KEY; const BUCKET = env.B2_BUCKET; if (!KEY_ID || !APP_KEY || !BUCKET) { return new Response('Missing B2 config', { status: 500 }); } // 6. Authorize Backblaze B2 const authRes = await fetch( 'https://api.backblazeb2.com/b2api/v2/b2_authorize_account', { headers: { Authorization: 'Basic ' + btoa(`${KEY_ID}:${APP_KEY}`) }, }, ); if (!authRes.ok) { return new Response('B2 auth failed', { status: 502 }); } const auth = await authRes.json(); // 7. Build file URL const cleanPath = url.pathname; const fileUrl = `${auth.downloadUrl}/file/${BUCKET}${cleanPath}`; console.log('Fetching original from B2:', fileUrl); // 8. Fetch file from B2 const origin = await fetch(fileUrl, { headers: { Authorization: auth.authorizationToken }, }); if (!origin.ok) { return new Response('File not found', { status: origin.status }); } // 9. Cache and return original const response = new Response(origin.body, { headers: { 'Content-Type': origin.headers.get('Content-Type') || 'image/jpeg', 'Cache-Control': 'public, max-age=31536000', // Cache for 1 year 'X-Source': 'B2-Direct', }, }); ctx.waitUntil(cache.put(cacheKey, response.clone())); return response; } } catch (err) { console.error('Worker error:', err); return new Response('Internal Error: ' + err.message, { status: 500 }); } }, };