mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
fix cloudflare r2 adapter range requests
This commit is contained in:
@@ -33,6 +33,7 @@ export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> &
|
|||||||
keepAliveSeconds?: number;
|
keepAliveSeconds?: number;
|
||||||
forceHttps?: boolean;
|
forceHttps?: boolean;
|
||||||
manifest?: string;
|
manifest?: string;
|
||||||
|
registerMedia?: boolean | ((env: Env) => void);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Context<Env = CloudflareEnv> = {
|
export type Context<Env = CloudflareEnv> = {
|
||||||
|
|||||||
@@ -93,8 +93,12 @@ export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
config: CloudflareBkndConfig<Env>,
|
config: CloudflareBkndConfig<Env>,
|
||||||
args?: CfMakeConfigArgs<Env>,
|
args?: CfMakeConfigArgs<Env>,
|
||||||
) {
|
) {
|
||||||
if (!media_registered) {
|
if (!media_registered && config.registerMedia !== false) {
|
||||||
|
if (typeof config.registerMedia === "function") {
|
||||||
|
config.registerMedia(args?.env as any);
|
||||||
|
} else {
|
||||||
registerMedia(args?.env as any);
|
registerMedia(args?.env as any);
|
||||||
|
}
|
||||||
media_registered = true;
|
media_registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ export {
|
|||||||
type BindingMap,
|
type BindingMap,
|
||||||
} from "./bindings";
|
} from "./bindings";
|
||||||
export { constants } from "./config";
|
export { constants } from "./config";
|
||||||
|
export { StorageR2Adapter } from "./storage/StorageR2Adapter";
|
||||||
|
export { registries } from "bknd";
|
||||||
|
|
||||||
// for compatibility with old code
|
// for compatibility with old code
|
||||||
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { registries } from "bknd";
|
import { registries } from "bknd";
|
||||||
import { isDebug } from "bknd/core";
|
import { isDebug } from "bknd/core";
|
||||||
|
// @ts-ignore
|
||||||
import { StringEnum } from "bknd/utils";
|
import { StringEnum } from "bknd/utils";
|
||||||
import { guessMimeType as guess, StorageAdapter, type FileBody } from "bknd/media";
|
import { guessMimeType as guess, StorageAdapter, type FileBody } from "bknd/media";
|
||||||
import { getBindings } from "../bindings";
|
import { getBindings } from "../bindings";
|
||||||
@@ -63,46 +64,49 @@ export class StorageR2Adapter extends StorageAdapter {
|
|||||||
|
|
||||||
async putObject(key: string, body: FileBody) {
|
async putObject(key: string, body: FileBody) {
|
||||||
try {
|
try {
|
||||||
const res = await this.bucket.put(key, body);
|
const res = await this.bucket.put(this.getKey(key), body);
|
||||||
return res?.etag;
|
return res?.etag;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async listObjects(
|
async listObjects(prefix = ""): Promise<{ key: string; last_modified: Date; size: number }[]> {
|
||||||
prefix?: string,
|
const list = await this.bucket.list({ limit: 50, prefix: this.getKey(prefix) });
|
||||||
): Promise<{ key: string; last_modified: Date; size: number }[]> {
|
|
||||||
const list = await this.bucket.list({ limit: 50 });
|
|
||||||
return list.objects.map((item) => ({
|
return list.objects.map((item) => ({
|
||||||
key: item.key,
|
key: item.key.replace(this.getKey(""), ""),
|
||||||
size: item.size,
|
size: item.size,
|
||||||
last_modified: item.uploaded,
|
last_modified: item.uploaded,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async headObject(key: string): Promise<R2Object | null> {
|
private async headObject(key: string): Promise<R2Object | null> {
|
||||||
return await this.bucket.head(key);
|
return await this.bucket.head(this.getKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
async objectExists(key: string): Promise<boolean> {
|
async objectExists(key: string): Promise<boolean> {
|
||||||
return (await this.headObject(key)) !== null;
|
return (await this.headObject(key)) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getObject(key: string, headers: Headers): Promise<Response> {
|
async getObject(_key: string, headers: Headers): Promise<Response> {
|
||||||
let object: R2ObjectBody | null;
|
let object: R2ObjectBody | null;
|
||||||
|
const key = this.getKey(_key);
|
||||||
|
|
||||||
const responseHeaders = new Headers({
|
const responseHeaders = new Headers({
|
||||||
"Accept-Ranges": "bytes",
|
"Accept-Ranges": "bytes",
|
||||||
"Content-Type": guess(key),
|
"Content-Type": guess(key),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const range = headers.has("range");
|
||||||
|
|
||||||
//console.log("getObject:headers", headersToObject(headers));
|
//console.log("getObject:headers", headersToObject(headers));
|
||||||
if (headers.has("range")) {
|
if (range) {
|
||||||
const options = isDebug()
|
const options = isDebug()
|
||||||
? {} // miniflare doesn't support range requests
|
? {} // miniflare doesn't support range requests
|
||||||
: {
|
: {
|
||||||
range: headers,
|
range: headers,
|
||||||
onlyIf: headers,
|
onlyIf: headers,
|
||||||
};
|
};
|
||||||
|
|
||||||
object = (await this.bucket.get(key, options)) as R2ObjectBody;
|
object = (await this.bucket.get(key, options)) as R2ObjectBody;
|
||||||
|
|
||||||
if (!object) {
|
if (!object) {
|
||||||
@@ -130,13 +134,14 @@ export class StorageR2Adapter extends StorageAdapter {
|
|||||||
responseHeaders.set("Last-Modified", object.uploaded.toUTCString());
|
responseHeaders.set("Last-Modified", object.uploaded.toUTCString());
|
||||||
|
|
||||||
return new Response(object.body, {
|
return new Response(object.body, {
|
||||||
status: object.range ? 206 : 200,
|
status: range ? 206 : 200,
|
||||||
headers: responseHeaders,
|
headers: responseHeaders,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private writeHttpMetadata(headers: Headers, object: R2Object | R2ObjectBody): void {
|
private writeHttpMetadata(headers: Headers, object: R2Object | R2ObjectBody): void {
|
||||||
let metadata = object.httpMetadata;
|
let metadata = object.httpMetadata;
|
||||||
|
|
||||||
if (!metadata || Object.keys(metadata).length === 0) {
|
if (!metadata || Object.keys(metadata).length === 0) {
|
||||||
// guessing is especially required for dev environment (miniflare)
|
// guessing is especially required for dev environment (miniflare)
|
||||||
metadata = {
|
metadata = {
|
||||||
@@ -163,13 +168,17 @@ export class StorageR2Adapter extends StorageAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteObject(key: string): Promise<void> {
|
async deleteObject(key: string): Promise<void> {
|
||||||
await this.bucket.delete(key);
|
await this.bucket.delete(this.getKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
getObjectUrl(key: string): string {
|
getObjectUrl(key: string): string {
|
||||||
throw new Error("Method getObjectUrl not implemented.");
|
throw new Error("Method getObjectUrl not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getKey(key: string) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
toJSON(secrets?: boolean) {
|
toJSON(secrets?: boolean) {
|
||||||
return {
|
return {
|
||||||
type: this.getName(),
|
type: this.getName(),
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ const Icons = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AdapterIcon = ({ type }: { type: string }) => {
|
const AdapterIcon = ({ type }: { type: string }) => {
|
||||||
const Icon = Icons[type];
|
// find icon whose name starts with type
|
||||||
|
const Icon = Object.entries(Icons).find(([key]) => type.startsWith(key))?.[1];
|
||||||
if (!Icon) return null;
|
if (!Icon) return null;
|
||||||
return <Icon />;
|
return <Icon />;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user