mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-03 16:46:00 +00:00
initial refactor
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
text,
|
||||
} from "../data/prototype";
|
||||
import { MediaController } from "./api/MediaController";
|
||||
import { buildMediaSchema, type mediaConfigSchema, registry } from "./media-schema";
|
||||
import { buildMediaSchema, registry, type TAppMediaConfig } from "./media-schema";
|
||||
|
||||
export type MediaFieldSchema = FieldSchema<typeof AppMedia.mediaFields>;
|
||||
declare module "core" {
|
||||
@@ -23,7 +23,7 @@ declare module "core" {
|
||||
}
|
||||
}
|
||||
|
||||
export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
export class AppMedia extends Module<TAppMediaConfig> {
|
||||
private _storage?: Storage;
|
||||
|
||||
override async build() {
|
||||
|
||||
+11
-13
@@ -1,19 +1,17 @@
|
||||
import type { Static } from "core/utils";
|
||||
import { Field, baseFieldConfigSchema } from "data/fields";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { s } from "core/object/schema";
|
||||
|
||||
export const mediaFieldConfigSchema = Type.Composite([
|
||||
Type.Object({
|
||||
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())),
|
||||
}),
|
||||
baseFieldConfigSchema,
|
||||
]);
|
||||
export const mediaFieldConfigSchema = s
|
||||
.strictObject({
|
||||
entity: s.string(), // @todo: is this really required?
|
||||
min_items: s.number(),
|
||||
max_items: s.number(),
|
||||
mime_types: s.array(s.string()),
|
||||
...baseFieldConfigSchema.properties,
|
||||
})
|
||||
.partial();
|
||||
|
||||
export type MediaFieldConfig = Static<typeof mediaFieldConfigSchema>;
|
||||
export type MediaFieldConfig = s.Static<typeof mediaFieldConfigSchema>;
|
||||
|
||||
export type MediaItem = {
|
||||
id: number;
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
StorageCloudinaryAdapter,
|
||||
} from "./storage/adapters/cloudinary/StorageCloudinaryAdapter";
|
||||
import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/s3/StorageS3Adapter";
|
||||
import type { s } from "core/object/schema";
|
||||
|
||||
export { StorageAdapter };
|
||||
export { StorageS3Adapter, type S3AdapterConfig, StorageCloudinaryAdapter, type CloudinaryConfig };
|
||||
@@ -29,10 +30,10 @@ type ClassThatImplements<T> = Constructor<T> & { prototype: T };
|
||||
|
||||
export const MediaAdapterRegistry = new Registry<{
|
||||
cls: ClassThatImplements<StorageAdapter>;
|
||||
schema: TObject;
|
||||
schema: s.Schema;
|
||||
}>((cls: ClassThatImplements<StorageAdapter>) => ({
|
||||
cls,
|
||||
schema: cls.prototype.getSchema() as TObject,
|
||||
schema: cls.prototype.getSchema() as s.Schema,
|
||||
}))
|
||||
.register("s3", StorageS3Adapter)
|
||||
.register("cloudinary", StorageCloudinaryAdapter);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Const, type Static, objectTransform } from "core/utils";
|
||||
import { objectTransform } from "core/utils";
|
||||
import { Adapters } from "media";
|
||||
import { registries } from "modules/registries";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { s } from "core/object/schema";
|
||||
|
||||
export const ADAPTERS = {
|
||||
...Adapters,
|
||||
@@ -12,42 +11,33 @@ export const registry = registries.media;
|
||||
|
||||
export function buildMediaSchema() {
|
||||
const adapterSchemaObject = objectTransform(registry.all(), (adapter, name) => {
|
||||
return Type.Object(
|
||||
return s.strictObject(
|
||||
{
|
||||
type: Const(name),
|
||||
type: s.literal(name),
|
||||
config: adapter.schema,
|
||||
},
|
||||
{
|
||||
title: adapter.schema?.title ?? name,
|
||||
description: adapter.schema?.description,
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
const adapterSchema = Type.Union(Object.values(adapterSchemaObject));
|
||||
|
||||
return Type.Object(
|
||||
{
|
||||
enabled: Type.Boolean({ default: false }),
|
||||
basepath: Type.String({ default: "/api/media" }),
|
||||
entity_name: Type.String({ default: "media" }),
|
||||
storage: Type.Object(
|
||||
{
|
||||
body_max_size: Type.Optional(
|
||||
Type.Number({
|
||||
description: "Max size of the body in bytes. Leave blank for unlimited.",
|
||||
}),
|
||||
),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
adapter: Type.Optional(adapterSchema),
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
return s.strictObject({
|
||||
enabled: s.boolean({ default: false }),
|
||||
basepath: s.string({ default: "/api/media" }),
|
||||
entity_name: s.string({ default: "media" }),
|
||||
storage: s.strictObject(
|
||||
{
|
||||
body_max_size: s.number({
|
||||
description: "Max size of the body in bytes. Leave blank for unlimited.",
|
||||
}),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
adapter: s.anyOf(Object.values(adapterSchemaObject)).optional(),
|
||||
});
|
||||
}
|
||||
|
||||
export const mediaConfigSchema = buildMediaSchema();
|
||||
export type TAppMediaConfig = Static<typeof mediaConfigSchema>;
|
||||
export type TAppMediaConfig = s.Static<typeof mediaConfigSchema>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { FileListObject, FileMeta } from "media";
|
||||
import type { FileBody, FileUploadPayload } from "media/storage/Storage";
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
import type { s } from "core/object/schema";
|
||||
|
||||
const SYMBOL = Symbol.for("bknd:storage");
|
||||
|
||||
@@ -32,6 +32,6 @@ export abstract class StorageAdapter {
|
||||
abstract getObject(key: string, headers: Headers): Promise<Response>;
|
||||
abstract getObjectUrl(key: string): string;
|
||||
abstract getObjectMeta(key: string): Promise<FileMeta>;
|
||||
abstract getSchema(): TSchema | undefined;
|
||||
abstract getSchema(): s.Schema | undefined;
|
||||
abstract toJSON(secrets?: boolean): any;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { hash, pickHeaders } from "core/utils";
|
||||
import { type Static, parse } from "core/utils";
|
||||
import type { FileBody, FileListObject, FileMeta } from "../../Storage";
|
||||
import { StorageAdapter } from "../../StorageAdapter";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { s, parse } from "core/object/schema";
|
||||
|
||||
export const cloudinaryAdapterConfig = Type.Object(
|
||||
export const cloudinaryAdapterConfig = s.object(
|
||||
{
|
||||
cloud_name: Type.String(),
|
||||
api_key: Type.String(),
|
||||
api_secret: Type.String(),
|
||||
upload_preset: Type.Optional(Type.String()),
|
||||
cloud_name: s.string(),
|
||||
api_key: s.string(),
|
||||
api_secret: s.string(),
|
||||
upload_preset: s.string().optional(),
|
||||
},
|
||||
{ title: "Cloudinary", description: "Cloudinary media storage" },
|
||||
);
|
||||
|
||||
export type CloudinaryConfig = Static<typeof cloudinaryAdapterConfig>;
|
||||
export type CloudinaryConfig = s.Static<typeof cloudinaryAdapterConfig>;
|
||||
|
||||
type CloudinaryObject = {
|
||||
asset_id: string;
|
||||
|
||||
@@ -7,18 +7,17 @@ import type {
|
||||
PutObjectRequest,
|
||||
} from "@aws-sdk/client-s3";
|
||||
import { AwsClient, isDebug } from "core";
|
||||
import { type Static, isFile, parse, pickHeaders2 } from "core/utils";
|
||||
import { isFile, pickHeaders2 } from "core/utils";
|
||||
import { transform } from "lodash-es";
|
||||
import type { FileBody, FileListObject } from "../../Storage";
|
||||
import { StorageAdapter } from "../../StorageAdapter";
|
||||
import * as tbbox from "@sinclair/typebox";
|
||||
const { Type } = tbbox;
|
||||
import { parse, s } from "core/object/schema";
|
||||
|
||||
export const s3AdapterConfig = Type.Object(
|
||||
export const s3AdapterConfig = s.object(
|
||||
{
|
||||
access_key: Type.String(),
|
||||
secret_access_key: Type.String(),
|
||||
url: Type.String({
|
||||
access_key: s.string(),
|
||||
secret_access_key: s.string(),
|
||||
url: s.string({
|
||||
pattern: "^https?://(?:.*)?[^/.]+$",
|
||||
description: "URL to S3 compatible endpoint without trailing slash",
|
||||
examples: [
|
||||
@@ -33,7 +32,7 @@ export const s3AdapterConfig = Type.Object(
|
||||
},
|
||||
);
|
||||
|
||||
export type S3AdapterConfig = Static<typeof s3AdapterConfig>;
|
||||
export type S3AdapterConfig = s.Static<typeof s3AdapterConfig>;
|
||||
|
||||
export class StorageS3Adapter extends StorageAdapter {
|
||||
readonly #config: S3AdapterConfig;
|
||||
|
||||
Reference in New Issue
Block a user