mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 12:56:05 +00:00
added format command and added trailing commas to reduce conflicts
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
entity,
|
||||
json,
|
||||
number,
|
||||
text
|
||||
text,
|
||||
} from "../data/prototype";
|
||||
import { MediaController } from "./api/MediaController";
|
||||
import { ADAPTERS, buildMediaSchema, type mediaConfigSchema, registry } from "./media-schema";
|
||||
@@ -52,12 +52,12 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
this.ensureSchema(
|
||||
em({ [media.name as "media"]: media }, ({ index }, { media }) => {
|
||||
index(media).on(["path"], true).on(["reference"]).on(["entity_id"]);
|
||||
})
|
||||
}),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error(
|
||||
`Could not build adapter with config ${JSON.stringify(this.config.adapter)}`
|
||||
`Could not build adapter with config ${JSON.stringify(this.config.adapter)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
mime_type: info.meta.type,
|
||||
size: info.meta.size,
|
||||
etag: info.etag,
|
||||
modified_at: new Date()
|
||||
modified_at: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
modified_at: datetime(),
|
||||
reference: text(),
|
||||
entity_id: number(),
|
||||
metadata: json()
|
||||
metadata: json(),
|
||||
};
|
||||
|
||||
getMediaEntity(forceCreate?: boolean): Entity<"media", typeof AppMedia.mediaFields> {
|
||||
@@ -128,7 +128,7 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
mutator.__unstable_toggleSystemEntityCreation(true);
|
||||
return { data };
|
||||
},
|
||||
{ mode: "sync", id: "add-data-media" }
|
||||
{ mode: "sync", id: "add-data-media" },
|
||||
);
|
||||
|
||||
// when file is deleted, sync with media entity
|
||||
@@ -144,7 +144,7 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
|
||||
console.log("App:storage:file deleted", e);
|
||||
},
|
||||
{ mode: "sync", id: "delete-data-media" }
|
||||
{ mode: "sync", id: "delete-data-media" },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
|
||||
return {
|
||||
...this.config,
|
||||
adapter: this.storage.getAdapter().toJSON(secrets)
|
||||
adapter: this.storage.getAdapter().toJSON(secrets),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ export const mediaFieldConfigSchema = Type.Composite([
|
||||
entity: Type.String(), // @todo: is this really required?
|
||||
min_items: Type.Optional(Type.Number()),
|
||||
max_items: Type.Optional(Type.Number()),
|
||||
mime_types: Type.Optional(Type.Array(Type.String()))
|
||||
mime_types: Type.Optional(Type.Array(Type.String())),
|
||||
}),
|
||||
baseFieldConfigSchema
|
||||
baseFieldConfigSchema,
|
||||
]);
|
||||
|
||||
export type MediaFieldConfig = Static<typeof mediaFieldConfigSchema>;
|
||||
@@ -26,7 +26,7 @@ export type MediaItem = {
|
||||
|
||||
export class MediaField<
|
||||
Required extends true | false = false,
|
||||
TypeOverride = MediaItem[]
|
||||
TypeOverride = MediaItem[],
|
||||
> extends Field<MediaFieldConfig, TypeOverride, Required> {
|
||||
override readonly type = "media";
|
||||
|
||||
@@ -64,7 +64,7 @@ export class MediaField<
|
||||
type: "array",
|
||||
items: { $ref },
|
||||
minItems,
|
||||
maxItems
|
||||
maxItems,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
type BaseModuleApiOptions,
|
||||
ModuleApi,
|
||||
type PrimaryFieldType,
|
||||
type TInput
|
||||
type TInput,
|
||||
} from "modules/ModuleApi";
|
||||
import type { FileWithPath } from "ui/elements/media/file-selector";
|
||||
|
||||
@@ -12,7 +12,7 @@ export type MediaApiOptions = BaseModuleApiOptions & {};
|
||||
export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
protected override getDefaultOptions(): Partial<MediaApiOptions> {
|
||||
return {
|
||||
basepath: "/api/media"
|
||||
basepath: "/api/media",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
getFile(filename: string) {
|
||||
return this.get<ReadableStream<Uint8Array>>(["file", filename], undefined, {
|
||||
headers: {
|
||||
Accept: "*/*"
|
||||
}
|
||||
Accept: "*/*",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
|
||||
getUploadHeaders(): Headers {
|
||||
return new Headers({
|
||||
Authorization: `Bearer ${this.options.token}`
|
||||
Authorization: `Bearer ${this.options.token}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,11 +65,11 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
filename?: string;
|
||||
path?: TInput;
|
||||
_init?: Omit<RequestInit, "body">;
|
||||
}
|
||||
},
|
||||
) {
|
||||
const headers = {
|
||||
"Content-Type": "application/octet-stream",
|
||||
...(opts?._init?.headers || {})
|
||||
...(opts?._init?.headers || {}),
|
||||
};
|
||||
let name: string = opts?.filename || "";
|
||||
try {
|
||||
@@ -87,7 +87,7 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
|
||||
const init = {
|
||||
...(opts?._init || {}),
|
||||
headers
|
||||
headers,
|
||||
};
|
||||
if (opts?.path) {
|
||||
return this.post(opts.path, body, init);
|
||||
@@ -106,7 +106,7 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
filename?: string;
|
||||
_init?: Omit<RequestInit, "body">;
|
||||
path?: TInput;
|
||||
} = {}
|
||||
} = {},
|
||||
) {
|
||||
if (item instanceof Request || typeof item === "string") {
|
||||
const res = await this.fetcher(item);
|
||||
@@ -124,9 +124,9 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
...(opts._init ?? {}),
|
||||
headers: {
|
||||
...(opts._init?.headers ?? {}),
|
||||
"Content-Type": item.headers.get("Content-Type") || "application/octet-stream"
|
||||
}
|
||||
}
|
||||
"Content-Type": item.headers.get("Content-Type") || "application/octet-stream",
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -140,11 +140,11 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||
item: Request | Response | string | File | ReadableStream,
|
||||
opts?: {
|
||||
_init?: Omit<RequestInit, "body">;
|
||||
}
|
||||
},
|
||||
) {
|
||||
return this.upload(item, {
|
||||
...opts,
|
||||
path: ["entity", entity, id, field]
|
||||
path: ["entity", entity, id, field],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export class MediaController extends Controller {
|
||||
return c.json({
|
||||
type: file?.type,
|
||||
name: file?.name,
|
||||
size: file?.size
|
||||
size: file?.size,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export class MediaController extends Controller {
|
||||
if (body.size > maxSize) {
|
||||
return c.json(
|
||||
{ error: `Max size (${maxSize} bytes) exceeded` },
|
||||
HttpStatus.PAYLOAD_TOO_LARGE
|
||||
HttpStatus.PAYLOAD_TOO_LARGE,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ export class MediaController extends Controller {
|
||||
tb(
|
||||
"query",
|
||||
Type.Object({
|
||||
overwrite: Type.Optional(booleanLike)
|
||||
})
|
||||
overwrite: Type.Optional(booleanLike),
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
const entity_name = c.req.param("entity");
|
||||
@@ -124,7 +124,7 @@ export class MediaController extends Controller {
|
||||
const mediaRef = {
|
||||
scope: field_name,
|
||||
reference,
|
||||
entity_id: entity_id
|
||||
entity_id: entity_id,
|
||||
};
|
||||
|
||||
// check max items
|
||||
@@ -140,7 +140,7 @@ export class MediaController extends Controller {
|
||||
if (!overwrite) {
|
||||
return c.json(
|
||||
{ error: `Max items (${max_items}) reached` },
|
||||
HttpStatus.BAD_REQUEST
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export class MediaController extends Controller {
|
||||
if (count > max_items) {
|
||||
return c.json(
|
||||
{ error: `Max items (${max_items}) exceeded already with ${count} items.` },
|
||||
HttpStatus.UNPROCESSABLE_ENTITY
|
||||
HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,9 +159,9 @@ export class MediaController extends Controller {
|
||||
where: mediaRef,
|
||||
sort: {
|
||||
by: "id",
|
||||
dir: "asc"
|
||||
dir: "asc",
|
||||
},
|
||||
limit: count - max_items + 1
|
||||
limit: count - max_items + 1,
|
||||
});
|
||||
|
||||
if (deleteRes.data && deleteRes.data.length > 0) {
|
||||
@@ -175,7 +175,7 @@ export class MediaController extends Controller {
|
||||
if (!exists) {
|
||||
return c.json(
|
||||
{ error: `Entity "${entity_name}" with ID "${entity_id}" doesn't exist found` },
|
||||
HttpStatus.NOT_FOUND
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ export class MediaController extends Controller {
|
||||
if (file.size > maxSize) {
|
||||
return c.json(
|
||||
{ error: `Max size (${maxSize} bytes) exceeded` },
|
||||
HttpStatus.PAYLOAD_TOO_LARGE
|
||||
HttpStatus.PAYLOAD_TOO_LARGE,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ export class MediaController extends Controller {
|
||||
mutator.__unstable_toggleSystemEntityCreation(false);
|
||||
const result = await mutator.insertOne({
|
||||
...this.media.uploadedEventDataToMediaPayload(info),
|
||||
...mediaRef
|
||||
...mediaRef,
|
||||
} as any);
|
||||
mutator.__unstable_toggleSystemEntityCreation(true);
|
||||
|
||||
@@ -210,7 +210,7 @@ export class MediaController extends Controller {
|
||||
}
|
||||
|
||||
return c.json({ ok: true, result: result.data, ...info }, HttpStatus.CREATED);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return hono.all("*", (c) => c.notFound());
|
||||
|
||||
@@ -8,12 +8,12 @@ export {
|
||||
type StorageAdapter,
|
||||
type FileMeta,
|
||||
type FileListObject,
|
||||
type StorageConfig
|
||||
type StorageConfig,
|
||||
} from "./storage/Storage";
|
||||
import type { StorageAdapter } from "./storage/Storage";
|
||||
import {
|
||||
type CloudinaryConfig,
|
||||
StorageCloudinaryAdapter
|
||||
StorageCloudinaryAdapter,
|
||||
} from "./storage/adapters/StorageCloudinaryAdapter";
|
||||
import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/StorageS3Adapter";
|
||||
|
||||
@@ -30,7 +30,7 @@ export const MediaAdapterRegistry = new Registry<{
|
||||
schema: TObject;
|
||||
}>((cls: ClassThatImplements<StorageAdapter>) => ({
|
||||
cls,
|
||||
schema: cls.prototype.getSchema() as TObject
|
||||
schema: cls.prototype.getSchema() as TObject,
|
||||
}))
|
||||
.register("s3", StorageS3Adapter)
|
||||
.register("cloudinary", StorageCloudinaryAdapter);
|
||||
@@ -38,10 +38,10 @@ export const MediaAdapterRegistry = new Registry<{
|
||||
export const Adapters = {
|
||||
s3: {
|
||||
cls: StorageS3Adapter,
|
||||
schema: StorageS3Adapter.prototype.getSchema()
|
||||
schema: StorageS3Adapter.prototype.getSchema(),
|
||||
},
|
||||
cloudinary: {
|
||||
cls: StorageCloudinaryAdapter,
|
||||
schema: StorageCloudinaryAdapter.prototype.getSchema()
|
||||
}
|
||||
schema: StorageCloudinaryAdapter.prototype.getSchema(),
|
||||
},
|
||||
} as const;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Adapters } from "media";
|
||||
import { registries } from "modules/registries";
|
||||
|
||||
export const ADAPTERS = {
|
||||
...Adapters
|
||||
...Adapters,
|
||||
} as const;
|
||||
|
||||
export const registry = registries.media;
|
||||
@@ -13,13 +13,13 @@ export function buildMediaSchema() {
|
||||
return Type.Object(
|
||||
{
|
||||
type: Const(name),
|
||||
config: adapter.schema
|
||||
config: adapter.schema,
|
||||
},
|
||||
{
|
||||
title: adapter.schema?.title ?? name,
|
||||
description: adapter.schema?.description,
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
const adapterSchema = Type.Union(Object.values(adapterSchemaObject));
|
||||
@@ -33,17 +33,17 @@ export function buildMediaSchema() {
|
||||
{
|
||||
body_max_size: Type.Optional(
|
||||
Type.Number({
|
||||
description: "Max size of the body in bytes. Leave blank for unlimited."
|
||||
})
|
||||
)
|
||||
description: "Max size of the body in bytes. Leave blank for unlimited.",
|
||||
}),
|
||||
),
|
||||
},
|
||||
{ default: {} }
|
||||
{ default: {} },
|
||||
),
|
||||
adapter: Type.Optional(adapterSchema)
|
||||
adapter: Type.Optional(adapterSchema),
|
||||
},
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ export class Storage implements EmitsEvents {
|
||||
constructor(
|
||||
adapter: StorageAdapter,
|
||||
config: Partial<StorageConfig> = {},
|
||||
emgr?: EventManager<any>
|
||||
emgr?: EventManager<any>,
|
||||
) {
|
||||
this.#adapter = adapter;
|
||||
this.config = {
|
||||
...config,
|
||||
body_max_size: config.body_max_size
|
||||
body_max_size: config.body_max_size,
|
||||
};
|
||||
|
||||
this.emgr = emgr ?? new EventManager();
|
||||
@@ -78,7 +78,7 @@ export class Storage implements EmitsEvents {
|
||||
async uploadFile(
|
||||
file: FileBody,
|
||||
name: string,
|
||||
noEmit?: boolean
|
||||
noEmit?: boolean,
|
||||
): Promise<FileUploadedEventData> {
|
||||
const result = await this.#adapter.putObject(name, file);
|
||||
if (typeof result === "undefined") {
|
||||
@@ -89,9 +89,9 @@ export class Storage implements EmitsEvents {
|
||||
name,
|
||||
meta: {
|
||||
size: 0,
|
||||
type: "application/octet-stream"
|
||||
type: "application/octet-stream",
|
||||
},
|
||||
etag: typeof result === "string" ? result : ""
|
||||
etag: typeof result === "string" ? result : "",
|
||||
};
|
||||
|
||||
if (typeof result === "object") {
|
||||
@@ -115,8 +115,8 @@ export class Storage implements EmitsEvents {
|
||||
...info,
|
||||
state: {
|
||||
name: info.name,
|
||||
path: info.name
|
||||
}
|
||||
path: info.name,
|
||||
},
|
||||
};
|
||||
if (!noEmit) {
|
||||
const result = await this.emgr.emit(new StorageEvents.FileUploadedEvent(eventData));
|
||||
|
||||
@@ -7,9 +7,9 @@ export const cloudinaryAdapterConfig = Type.Object(
|
||||
cloud_name: Type.String(),
|
||||
api_key: Type.String(),
|
||||
api_secret: Type.String(),
|
||||
upload_preset: Type.Optional(Type.String())
|
||||
upload_preset: Type.Optional(Type.String()),
|
||||
},
|
||||
{ title: "Cloudinary", description: "Cloudinary media storage" }
|
||||
{ title: "Cloudinary", description: "Cloudinary media storage" },
|
||||
);
|
||||
|
||||
export type CloudinaryConfig = Static<typeof cloudinaryAdapterConfig>;
|
||||
@@ -80,7 +80,7 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
private getAuthorizationHeader() {
|
||||
const credentials = btoa(`${this.config.api_key}:${this.config.api_secret}`);
|
||||
return {
|
||||
Authorization: `Basic ${credentials}`
|
||||
Authorization: `Basic ${credentials}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json"
|
||||
Accept: "application/json",
|
||||
// content type must be undefined to use correct boundaries
|
||||
//"Content-Type": "multipart/form-data",
|
||||
},
|
||||
body: formData
|
||||
}
|
||||
body: formData,
|
||||
},
|
||||
);
|
||||
|
||||
if (!result.ok) {
|
||||
@@ -121,8 +121,8 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
etag: data.etag,
|
||||
meta: {
|
||||
type: this.getMimeType(data),
|
||||
size: data.bytes
|
||||
}
|
||||
size: data.bytes,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -133,9 +133,9 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
...this.getAuthorizationHeader()
|
||||
}
|
||||
}
|
||||
...this.getAuthorizationHeader(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!result.ok) {
|
||||
@@ -146,7 +146,7 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
return data.resources.map((item) => ({
|
||||
key: item.public_id,
|
||||
last_modified: new Date(item.uploaded_at),
|
||||
size: item.bytes
|
||||
size: item.bytes,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
return await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Range: "bytes=0-1"
|
||||
}
|
||||
Range: "bytes=0-1",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
const size = Number(result.headers.get("content-range")?.split("/")[1]);
|
||||
return {
|
||||
type: type as string,
|
||||
size: size
|
||||
size: size,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
private guessType(key: string): string | undefined {
|
||||
const extensions = {
|
||||
image: ["jpg", "jpeg", "png", "gif", "webp", "svg"],
|
||||
video: ["mp4", "webm", "ogg"]
|
||||
video: ["mp4", "webm", "ogg"],
|
||||
};
|
||||
|
||||
const ext = key.split(".").pop();
|
||||
@@ -199,13 +199,13 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
async getObject(key: string, headers: Headers): Promise<Response> {
|
||||
const res = await fetch(this.getObjectUrl(key), {
|
||||
method: "GET",
|
||||
headers: pickHeaders(headers, ["range"])
|
||||
headers: pickHeaders(headers, ["range"]),
|
||||
});
|
||||
|
||||
return new Response(res.body, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers: res.headers
|
||||
headers: res.headers,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -216,14 +216,14 @@ export class StorageCloudinaryAdapter implements StorageAdapter {
|
||||
|
||||
await fetch(`https://res.cloudinary.com/${this.config.cloud_name}/${type}/upload/`, {
|
||||
method: "DELETE",
|
||||
body: formData
|
||||
body: formData,
|
||||
});
|
||||
}
|
||||
|
||||
toJSON(secrets?: boolean) {
|
||||
return {
|
||||
type: "cloudinary",
|
||||
config: secrets ? this.config : { cloud_name: this.config.cloud_name }
|
||||
config: secrets ? this.config : { cloud_name: this.config.cloud_name },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@ import type {
|
||||
FileListObject,
|
||||
FileMeta,
|
||||
FileUploadPayload,
|
||||
StorageAdapter
|
||||
StorageAdapter,
|
||||
} from "../../Storage";
|
||||
import { guess } from "../../mime-types-tiny";
|
||||
|
||||
export const localAdapterConfig = Type.Object(
|
||||
{
|
||||
path: Type.String({ default: "./" })
|
||||
path: Type.String({ default: "./" }),
|
||||
},
|
||||
{ title: "Local", description: "Local file system storage" }
|
||||
{ title: "Local", description: "Local file system storage" },
|
||||
);
|
||||
export type LocalAdapterConfig = Static<typeof localAdapterConfig>;
|
||||
|
||||
@@ -42,9 +42,9 @@ export class StorageLocalAdapter implements StorageAdapter {
|
||||
return {
|
||||
key: file,
|
||||
last_modified: stats.mtime,
|
||||
size: stats.size
|
||||
size: stats.size,
|
||||
};
|
||||
})
|
||||
}),
|
||||
);
|
||||
return fileStats;
|
||||
}
|
||||
@@ -95,8 +95,8 @@ export class StorageLocalAdapter implements StorageAdapter {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": mimeType || "application/octet-stream",
|
||||
"Content-Length": content.length.toString()
|
||||
}
|
||||
"Content-Length": content.length.toString(),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
// Handle file reading errors
|
||||
@@ -112,14 +112,14 @@ export class StorageLocalAdapter implements StorageAdapter {
|
||||
const stats = await stat(`${this.config.path}/${key}`);
|
||||
return {
|
||||
type: guess(key) || "application/octet-stream",
|
||||
size: stats.size
|
||||
size: stats.size,
|
||||
};
|
||||
}
|
||||
|
||||
toJSON(secrets?: boolean) {
|
||||
return {
|
||||
type: this.getName(),
|
||||
config: this.config
|
||||
config: this.config,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export {
|
||||
StorageLocalAdapter,
|
||||
type LocalAdapterConfig,
|
||||
localAdapterConfig
|
||||
localAdapterConfig,
|
||||
} from "./StorageLocalAdapter";
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
HeadObjectRequest,
|
||||
ListObjectsV2Output,
|
||||
ListObjectsV2Request,
|
||||
PutObjectRequest
|
||||
PutObjectRequest,
|
||||
} from "@aws-sdk/client-s3";
|
||||
import { AwsClient, isDebug } from "core";
|
||||
import { type Static, Type, isFile, parse, pickHeaders } from "core/utils";
|
||||
@@ -20,14 +20,14 @@ export const s3AdapterConfig = Type.Object(
|
||||
description: "URL to S3 compatible endpoint without trailing slash",
|
||||
examples: [
|
||||
"https://{account_id}.r2.cloudflarestorage.com/{bucket}",
|
||||
"https://{bucket}.s3.{region}.amazonaws.com"
|
||||
]
|
||||
})
|
||||
"https://{bucket}.s3.{region}.amazonaws.com",
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: "AWS S3",
|
||||
description: "AWS S3 or compatible storage"
|
||||
}
|
||||
description: "AWS S3 or compatible storage",
|
||||
},
|
||||
);
|
||||
|
||||
export type S3AdapterConfig = Static<typeof s3AdapterConfig>;
|
||||
@@ -40,12 +40,12 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
{
|
||||
accessKeyId: config.access_key,
|
||||
secretAccessKey: config.secret_access_key,
|
||||
retries: isDebug() ? 0 : 10
|
||||
retries: isDebug() ? 0 : 10,
|
||||
},
|
||||
{
|
||||
convertParams: "pascalToKebab",
|
||||
responseType: "xml"
|
||||
}
|
||||
responseType: "xml",
|
||||
},
|
||||
);
|
||||
this.#config = parse(s3AdapterConfig, config);
|
||||
}
|
||||
@@ -78,12 +78,12 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
async listObjects(key: string = ""): Promise<FileListObject[]> {
|
||||
const params: Omit<ListObjectsV2Request, "Bucket"> & { ListType: number } = {
|
||||
ListType: 2,
|
||||
Prefix: key
|
||||
Prefix: key,
|
||||
};
|
||||
|
||||
const url = this.getUrl("", params);
|
||||
const res = await this.fetchJson<{ ListBucketResult: ListObjectsV2Output }>(url, {
|
||||
method: "GET"
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
// absolutely weird, but if only one object is there, it's an object, not an array
|
||||
@@ -98,11 +98,11 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
acc.push({
|
||||
key: obj.Key,
|
||||
last_modified: obj.LastModified,
|
||||
size: obj.Size
|
||||
size: obj.Size,
|
||||
});
|
||||
}
|
||||
},
|
||||
[] as FileListObject[]
|
||||
[] as FileListObject[],
|
||||
);
|
||||
|
||||
return transformed;
|
||||
@@ -112,12 +112,12 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
key: string,
|
||||
body: FileBody,
|
||||
// @todo: params must be added as headers, skipping for now
|
||||
params: Omit<PutObjectRequest, "Bucket" | "Key"> = {}
|
||||
params: Omit<PutObjectRequest, "Bucket" | "Key"> = {},
|
||||
) {
|
||||
const url = this.getUrl(key, {});
|
||||
const res = await this.fetch(url, {
|
||||
method: "PUT",
|
||||
body
|
||||
body,
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
@@ -130,14 +130,14 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
|
||||
private async headObject(
|
||||
key: string,
|
||||
params: Pick<HeadObjectRequest, "PartNumber" | "VersionId"> = {}
|
||||
params: Pick<HeadObjectRequest, "PartNumber" | "VersionId"> = {},
|
||||
) {
|
||||
const url = this.getUrl(key, {});
|
||||
return await this.fetch(url, {
|
||||
method: "HEAD",
|
||||
headers: {
|
||||
Range: "bytes=0-1"
|
||||
}
|
||||
Range: "bytes=0-1",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
|
||||
return {
|
||||
type,
|
||||
size
|
||||
size,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
*/
|
||||
async objectExists(
|
||||
key: string,
|
||||
params: Pick<HeadObjectRequest, "PartNumber" | "VersionId"> = {}
|
||||
params: Pick<HeadObjectRequest, "PartNumber" | "VersionId"> = {},
|
||||
) {
|
||||
return (await this.headObject(key)).ok;
|
||||
}
|
||||
@@ -171,14 +171,14 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
const url = this.getUrl(key);
|
||||
const res = await this.fetch(url, {
|
||||
method: "GET",
|
||||
headers: pickHeaders(headers, ["range"])
|
||||
headers: pickHeaders(headers, ["range"]),
|
||||
});
|
||||
|
||||
// Response has to be copied, because of middlewares that might set headers
|
||||
return new Response(res.body, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers: res.headers
|
||||
headers: res.headers,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -187,18 +187,18 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
|
||||
*/
|
||||
async deleteObject(
|
||||
key: string,
|
||||
params: Omit<DeleteObjectRequest, "Bucket" | "Key"> = {}
|
||||
params: Omit<DeleteObjectRequest, "Bucket" | "Key"> = {},
|
||||
): Promise<void> {
|
||||
const url = this.getUrl(key, params);
|
||||
const res = await this.fetch(url, {
|
||||
method: "DELETE"
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
toJSON(secrets?: boolean) {
|
||||
return {
|
||||
type: this.getName(),
|
||||
config: secrets ? this.#config : undefined
|
||||
config: secrets ? this.#config : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class FileUploadedEvent extends Event<FileUploadedEventData, object> {
|
||||
return this.clone({
|
||||
// prepending result, so original is always kept
|
||||
...data,
|
||||
...this.params
|
||||
...this.params,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export const Q = {
|
||||
image: ["jpeg", "png", "gif", "webp", "bmp", "tiff", "avif", "heic", "heif"],
|
||||
text: ["html", "css", "mdx", "yaml", "vcard", "csv", "vtt"],
|
||||
application: ["zip", "xml", "toml", "json", "json5"],
|
||||
font: ["woff", "woff2", "ttf", "otf"]
|
||||
font: ["woff", "woff2", "ttf", "otf"],
|
||||
} as const;
|
||||
|
||||
// reduced
|
||||
@@ -14,7 +14,7 @@ const c = {
|
||||
t: (w = "plain") => `text/${w}`,
|
||||
a: (w = "octet-stream") => `application/${w}`,
|
||||
i: (w) => `image/${w}`,
|
||||
v: (w) => `video/${w}`
|
||||
v: (w) => `video/${w}`,
|
||||
} as const;
|
||||
export const M = new Map<string, string>([
|
||||
["7z", c.z],
|
||||
@@ -52,7 +52,7 @@ export const M = new Map<string, string>([
|
||||
["webmanifest", c.a("manifest+json")],
|
||||
["xls", c.a("vnd.ms-excel")],
|
||||
["xlsx", `${c.vnd}.spreadsheetml.sheet`],
|
||||
["yml", c.t("yaml")]
|
||||
["yml", c.t("yaml")],
|
||||
]);
|
||||
|
||||
export function guess(f: string): string {
|
||||
|
||||
Reference in New Issue
Block a user