/// /** * onFileDownloadRequest hook is triggered before each API File download request. Could be used to validate or modify the file response before returning it to the client. * @see https://pocketbase.io/docs/js-event-hooks/#onfiledownloadrequest * * We use this to return a 302 to the CDN asset instead of having the asset proxied via Pocketbase * @see https://github.com/pocketbase/pocketbase/discussions/5995 * */ onFileDownloadRequest((event) => { console.log('>>> We got a file download request event', JSON.stringify(event)) console.log('the below the following is event.request'); console.log(JSON.stringify(event.request)); console.log('the above the previous line is event.request'); // e.app // e.collection // e.record // e.fileField // e.servedPath // e.servedName // and all RequestEvent fields... const securityKey = process.env?.BUNNY_TOKEN_KEY; const baseUrl = process.env?.BUNNY_ZONE_URL; console.log(`securityKey=${securityKey}, baseUrl=${baseUrl}`) if (!securityKey) { console.error('BUNNY_TOKEN_KEY was missing from env'); return event.next(); } if (!baseUrl) { console.error('BUNNY_ZONE_URL was missing from env'); return event.next(); } /** * Generate a signed BunnyCDN URL. * * @see https://support.bunny.net/hc/en-us/articles/360016055099-How-to-sign-URLs-for-BunnyCDN-Token-Authentication * * @param {string} securityKey - Your BunnyCDN security token * @param {string} inputUrl - The base URL (protocol + host + path + (optional) qs) * @param {number} expires - Unix timestamp for expiration * @param {string} tokenPath - (optional) create a token that has access to any file within that path */ function signUrlCool(securityKey, inputUrl, expires, tokenPath = '') { if (inputUrl.endsWith('/')) throw new Error(`url must not end with a slash. got inputUrl=${inputUrl}`); // reading query parameters // let search = e.request.url.query().get("search") const URL = event.request.url let parsedURL = URL.parse(inputUrl); // this is pre-parsed. no need to parse again let params = (URL.parse(parsedURL)).searchParams; // these are pre-parsed as event.request.url.rawQuery. no need to parse again let signaturePath = ''; let parameterData = ''; let parameterDataUrl = ''; console.log(`eyy we got parsedURL=${parsedURL}`); throw new Error('@todo'); // Build parameter string (sort keys alphabetically) // let parameterData = ""; // if (rawQuery) { // const params = rawQuery // .split("&") // .map(p => p.split("=")) // .filter(([key]) => key && key !== "token" && key !== "expires") // .sort(([a], [b]) => a.localeCompare(b)); // if (params.length) { // parameterData = params.map(([k, v]) => `${k}=${v}`).join("&"); // } // } // Build hashable base const hashableBase = securityKey + path + expires + parameterData; console.log(`hashableBase`, hashableBase) // Compute token using your $security.sha256 workflow const tokenH = $security.sha256(hashableBase); const token = Buffer.from(tokenH, "hex") .toString("base64") .replace(/\n/g, "") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); // Build final signed URL let tokenUrl = baseUrl + path + "?token=" + token; if (parameterData) tokenUrl += "&" + parameterData; tokenUrl += "&expires=" + expires; if (tokenPath) tokenUrl += "&token_path=" + tokenPath; return tokenUrl; } const rawQuery = event.requestEvent.request.url.rawQuery; console.log(`record: ${JSON.stringify(event.record)}`) // console.log(`collection: ${JSON.stringify(event.collection)}`) console.log(`app: ${JSON.stringify(event.app)}`) console.log(`fileField: ${JSON.stringify(event.fileField)}`) console.log(`servedPath: ${JSON.stringify(event.servedPath)}`) console.log(`servedName: ${JSON.stringify(event.servedName)}`) // Our job here is to take the servedPath, and sign it using bunnycdn method // Then serve a 302 redirect instead of serving the file proxied thru PB const path = event.servedPath; const expires = Math.round(Date.now() / 1000) + 7 * 24 * 3600; // 7 days const signedUrl = signUrlCool(securityKey, baseUrl, path, rawQuery, expires); console.log(`rawQuery`, rawQuery, 'path', path); console.log(`signedUrl=${signedUrl}`); // This redirect is a tricky thing. We do this to avoid proxying file requests via our pocketbase origin server. // The idea is to reduce load. // HOWEVER, this redirect slows down image loading because it now takes 2 requests per image. event.redirect(302, signedUrl); event.next() })