feat: add fallback redirect for cloudflare image optimization plugin

Introduced `fallbackRedirect` option to enable a redirect to the original image if image optimization fails. Added logging for platform proxy usage and adjusted `beforeBuild` to handle undefined `app`. Minor improvements and process termination for clear command flow.
This commit is contained in:
dswbx
2025-09-19 11:28:04 +02:00
parent 99a812cc75
commit 91120091a3
4 changed files with 20 additions and 1 deletions

View File

@@ -7,6 +7,7 @@ import {
} from "bknd/adapter/cloudflare"; } from "bknd/adapter/cloudflare";
import type { GetPlatformProxyOptions, PlatformProxy } from "wrangler"; import type { GetPlatformProxyOptions, PlatformProxy } from "wrangler";
import process from "node:process"; import process from "node:process";
import { $console } from "bknd/utils";
export type WithPlatformProxyOptions = { export type WithPlatformProxyOptions = {
/** /**
@@ -25,6 +26,8 @@ export function withPlatformProxy<Env extends CloudflareEnv>(
typeof opts?.useProxy === "boolean" ? opts.useProxy : process.env.PROXY === "1"; typeof opts?.useProxy === "boolean" ? opts.useProxy : process.env.PROXY === "1";
let proxy: PlatformProxy | undefined; let proxy: PlatformProxy | undefined;
$console.log("Using cloudflare platform proxy");
async function getEnv(env?: Env): Promise<Env> { async function getEnv(env?: Env): Promise<Env> {
if (use_proxy) { if (use_proxy) {
if (!proxy) { if (!proxy) {

View File

@@ -15,7 +15,7 @@ import type { Manifest } from "vite";
export type BkndConfig<Args = any> = CreateAppConfig & { export type BkndConfig<Args = any> = CreateAppConfig & {
app?: Omit<BkndConfig, "app"> | ((args: Args) => MaybePromise<Omit<BkndConfig<Args>, "app">>); app?: Omit<BkndConfig, "app"> | ((args: Args) => MaybePromise<Omit<BkndConfig<Args>, "app">>);
onBuilt?: (app: App) => MaybePromise<void>; onBuilt?: (app: App) => MaybePromise<void>;
beforeBuild?: (app: App, registries?: typeof $registries) => MaybePromise<void>; beforeBuild?: (app?: App, registries?: typeof $registries) => MaybePromise<void>;
buildConfig?: Parameters<App["build"]>[0]; buildConfig?: Parameters<App["build"]>[0];
}; };
@@ -57,6 +57,8 @@ export async function createAdapterApp<Config extends BkndConfig = BkndConfig, A
config: Config = {} as Config, config: Config = {} as Config,
args?: Args, args?: Args,
): Promise<App> { ): Promise<App> {
await config.beforeBuild?.(undefined, $registries);
const appConfig = await makeConfig(config, args); const appConfig = await makeConfig(config, args);
if (!appConfig.connection || !Connection.isConnection(appConfig.connection)) { if (!appConfig.connection || !Connection.isConnection(appConfig.connection)) {
let connection: Connection | undefined; let connection: Connection | undefined;

View File

@@ -35,4 +35,6 @@ async function action({
await writeFile(outfile, et.toString()); await writeFile(outfile, et.toString());
console.info(`\nTypes written to ${c.cyan(outfile)}`); console.info(`\nTypes written to ${c.cyan(outfile)}`);
} }
process.exit(0);
} }

View File

@@ -49,6 +49,11 @@ export type CloudflareImageOptimizationOptions = {
* @default public, max-age=31536000, immutable * @default public, max-age=31536000, immutable
*/ */
cacheControl?: string; cacheControl?: string;
/**
* Whether to fallback to the original image if the image optimization fails
* @default false
*/
fallbackRedirect?: boolean;
}; };
export function cloudflareImageOptimization({ export function cloudflareImageOptimization({
@@ -57,6 +62,7 @@ export function cloudflareImageOptimization({
explain = false, explain = false,
defaultOptions = {}, defaultOptions = {},
fixedOptions = {}, fixedOptions = {},
fallbackRedirect = false,
}: CloudflareImageOptimizationOptions = {}): AppPlugin { }: CloudflareImageOptimizationOptions = {}): AppPlugin {
const disallowedAccessUrls = ["/api", "/admin", "/api/plugin"]; const disallowedAccessUrls = ["/api", "/admin", "/api/plugin"];
if (disallowedAccessUrls.includes(accessUrl) || accessUrl.length < 2) { if (disallowedAccessUrls.includes(accessUrl) || accessUrl.length < 2) {
@@ -117,6 +123,12 @@ export function cloudflareImageOptimization({
// Returning fetch() with resizing options will pass through response with the resized image. // Returning fetch() with resizing options will pass through response with the resized image.
const res = await fetch(imageRequest, { cf: { image: options as any } }); const res = await fetch(imageRequest, { cf: { image: options as any } });
if (!res.ok && fallbackRedirect) {
// add redirect response to the original image
return new Response(null, { status: 307, headers: { Location: imageURL } });
}
const headers = pickHeaders2(res.headers, [ const headers = pickHeaders2(res.headers, [
"Content-Type", "Content-Type",
"Content-Length", "Content-Length",