initial refactor

This commit is contained in:
dswbx
2025-06-21 13:35:58 +02:00
parent b2086c4da7
commit 42edce904f
93 changed files with 1021 additions and 1216 deletions
+2 -2
View File
@@ -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
View File
@@ -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;
+3 -2
View File
@@ -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);
+19 -29
View File
@@ -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>;
+2 -2
View File
@@ -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;