mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 08:06:00 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4758f41a02 |
@@ -1,66 +0,0 @@
|
||||
import { AuthStrategy, registries, type Authenticator } from "bknd";
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import { createApp } from "core/test/utils";
|
||||
import { s } from "bknd/utils";
|
||||
import { Hono } from "hono";
|
||||
|
||||
const customStrategySchema = s.object({
|
||||
name: s.string(),
|
||||
});
|
||||
type CustomStrategyOptions = s.Static<typeof customStrategySchema>;
|
||||
|
||||
class CustomStrategy extends AuthStrategy<typeof customStrategySchema> {
|
||||
constructor(config: Partial<CustomStrategyOptions> = {}) {
|
||||
super(config as any, "custom", "custom", "form");
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return customStrategySchema;
|
||||
}
|
||||
|
||||
getController(authenticator: Authenticator) {
|
||||
return new Hono()
|
||||
.post("/login", async (c) => {
|
||||
try {
|
||||
// do magic, and it succeeds, resolve login
|
||||
|
||||
// @ts-ignore
|
||||
return await authenticator.resolveLogin();
|
||||
} catch (e) {
|
||||
// in case of error, respond with the error
|
||||
return authenticator.respondWithError(c, e as any);
|
||||
}
|
||||
})
|
||||
.post("/register", async (c) => {
|
||||
try {
|
||||
// do magic, and it succeeds, resolve register
|
||||
// @ts-ignore
|
||||
return await authenticator.resolveRegister();
|
||||
} catch (e) {
|
||||
// in case of error, respond with the error
|
||||
return authenticator.respondWithError(c, e as any);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe("custom auth strategy", () => {
|
||||
it("should be registered", async () => {
|
||||
registries.auth.register("custom", CustomStrategy);
|
||||
|
||||
const app = createApp({
|
||||
initialConfig: {
|
||||
auth: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
await app.build();
|
||||
|
||||
const strategies = app
|
||||
.getSchema()
|
||||
.auth.properties.strategies.additionalProperties.anyOf.map((s) => s.properties.type.const);
|
||||
|
||||
expect(strategies).toContain("custom");
|
||||
});
|
||||
});
|
||||
@@ -68,7 +68,7 @@ export async function createAdapterApp<Config extends BkndConfig = BkndConfig, A
|
||||
} else {
|
||||
const sqlite = (await import("bknd/adapter/sqlite")).sqlite;
|
||||
const conf = appConfig.connection ?? { url: ":memory:" };
|
||||
connection = sqlite(conf);
|
||||
connection = sqlite(conf) as unknown as Connection;
|
||||
$console.info(`Using ${connection!.name} connection`, conf.url);
|
||||
}
|
||||
appConfig.connection = connection;
|
||||
@@ -168,7 +168,7 @@ export function serveStaticViaImport(opts?: { manifest?: Manifest }) {
|
||||
const path = c.req.path.substring(1);
|
||||
if (files.includes(path)) {
|
||||
try {
|
||||
const content = await import(`bknd/static/${path}?raw`, {
|
||||
const content = await import(/* @vite-ignore */ `bknd/static/${path}?raw`, {
|
||||
assert: { type: "text" },
|
||||
}).then((m) => m.default);
|
||||
|
||||
|
||||
@@ -7,12 +7,7 @@ import type { Entity, EntityManager } from "data/entities";
|
||||
import { em, entity, enumm, type FieldSchema } from "data/prototype";
|
||||
import { Module } from "modules/Module";
|
||||
import { AuthController } from "./api/AuthController";
|
||||
import {
|
||||
type AppAuthSchema,
|
||||
authConfigSchema,
|
||||
AuthStrategyRegistry,
|
||||
buildAuthSchema,
|
||||
} from "./auth-schema";
|
||||
import { type AppAuthSchema, authConfigSchema, STRATEGIES } from "./auth-schema";
|
||||
import { AppUserPool } from "auth/AppUserPool";
|
||||
import type { AppEntity } from "core/config";
|
||||
import { usersFields } from "./auth-entities";
|
||||
@@ -73,8 +68,7 @@ export class AppAuth extends Module<AppAuthSchema> {
|
||||
// build strategies
|
||||
const strategies = transformObject(this.config.strategies ?? {}, (strategy, name) => {
|
||||
try {
|
||||
const cls = AuthStrategyRegistry.get(strategy.type as any).cls;
|
||||
return new cls(strategy.config as any);
|
||||
return new STRATEGIES[strategy.type].cls(strategy.config as any);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Could not build strategy ${String(
|
||||
@@ -114,7 +108,7 @@ export class AppAuth extends Module<AppAuthSchema> {
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return buildAuthSchema();
|
||||
return authConfigSchema;
|
||||
}
|
||||
|
||||
get authenticator(): Authenticator {
|
||||
|
||||
+57
-60
@@ -1,26 +1,40 @@
|
||||
import { Registry, type Constructor } from "core/registry/Registry";
|
||||
import { cookieConfig, jwtConfig } from "auth/authenticate/Authenticator";
|
||||
import { CustomOAuthStrategy, OAuthStrategy, PasswordStrategy } from "auth/authenticate/strategies";
|
||||
import { objectTransform, s } from "bknd/utils";
|
||||
import type { ProvidedOAuthConfig } from "./authenticate/strategies/oauth/OAuthStrategy";
|
||||
import type { OAuthConfigCustom } from "./authenticate/strategies/oauth/CustomOAuthStrategy";
|
||||
import { PasswordStrategy } from "./authenticate/strategies/PasswordStrategy";
|
||||
import { OAuthStrategy } from "./authenticate/strategies/oauth/OAuthStrategy";
|
||||
import { CustomOAuthStrategy } from "./authenticate/strategies/oauth/CustomOAuthStrategy";
|
||||
import type { AuthStrategy } from "auth/authenticate/strategies/Strategy";
|
||||
|
||||
export const AuthStrategyRegistry = new Registry<{
|
||||
cls: Constructor<AuthStrategy>;
|
||||
schema: s.Schema;
|
||||
}>((cls: Constructor<AuthStrategy>) => ({
|
||||
cls,
|
||||
schema: cls.prototype.getSchema() as s.Schema,
|
||||
}))
|
||||
.register("password", PasswordStrategy)
|
||||
.register("oauth", OAuthStrategy)
|
||||
.register("custom_oauth", CustomOAuthStrategy);
|
||||
export const Strategies = {
|
||||
password: {
|
||||
cls: PasswordStrategy,
|
||||
schema: PasswordStrategy.prototype.getSchema(),
|
||||
},
|
||||
oauth: {
|
||||
cls: OAuthStrategy,
|
||||
schema: OAuthStrategy.prototype.getSchema(),
|
||||
},
|
||||
custom_oauth: {
|
||||
cls: CustomOAuthStrategy,
|
||||
schema: CustomOAuthStrategy.prototype.getSchema(),
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type AppAuthOAuthStrategy = ProvidedOAuthConfig;
|
||||
export type AppAuthCustomOAuthStrategy = OAuthConfigCustom;
|
||||
export const STRATEGIES = Strategies;
|
||||
const strategiesSchemaObject = objectTransform(STRATEGIES, (strategy, name) => {
|
||||
return s.strictObject(
|
||||
{
|
||||
enabled: s.boolean({ default: true }).optional(),
|
||||
type: s.literal(name),
|
||||
config: strategy.schema,
|
||||
},
|
||||
{
|
||||
title: name,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const strategiesSchema = s.anyOf(Object.values(strategiesSchemaObject));
|
||||
export type AppAuthStrategies = s.Static<typeof strategiesSchema>;
|
||||
export type AppAuthOAuthStrategy = s.Static<typeof STRATEGIES.oauth.schema>;
|
||||
export type AppAuthCustomOAuthStrategy = s.Static<typeof STRATEGIES.custom_oauth.schema>;
|
||||
|
||||
const guardConfigSchema = s.object({
|
||||
enabled: s.boolean({ default: false }).optional(),
|
||||
@@ -31,49 +45,32 @@ export const guardRoleSchema = s.strictObject({
|
||||
implicit_allow: s.boolean().optional(),
|
||||
});
|
||||
|
||||
export function buildAuthSchema() {
|
||||
const strategiesSchemaObject = objectTransform(AuthStrategyRegistry.all(), (strategy, name) => {
|
||||
return s.strictObject(
|
||||
{
|
||||
enabled: s.boolean({ default: true }).optional(),
|
||||
type: s.literal(name),
|
||||
config: strategy.schema,
|
||||
},
|
||||
{
|
||||
title: name,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const strategiesSchema = s.anyOf(Object.values(strategiesSchemaObject));
|
||||
return s.strictObject(
|
||||
{
|
||||
enabled: s.boolean({ default: false }),
|
||||
basepath: s.string({ default: "/api/auth" }),
|
||||
entity_name: s.string({ default: "users" }),
|
||||
allow_register: s.boolean({ default: true }).optional(),
|
||||
jwt: jwtConfig,
|
||||
cookie: cookieConfig,
|
||||
strategies: s.record(strategiesSchema, {
|
||||
title: "Strategies",
|
||||
default: {
|
||||
password: {
|
||||
type: "password",
|
||||
enabled: true,
|
||||
config: {
|
||||
hashing: "sha256",
|
||||
},
|
||||
export const authConfigSchema = s.strictObject(
|
||||
{
|
||||
enabled: s.boolean({ default: false }),
|
||||
basepath: s.string({ default: "/api/auth" }),
|
||||
entity_name: s.string({ default: "users" }),
|
||||
allow_register: s.boolean({ default: true }).optional(),
|
||||
jwt: jwtConfig,
|
||||
cookie: cookieConfig,
|
||||
strategies: s.record(strategiesSchema, {
|
||||
title: "Strategies",
|
||||
default: {
|
||||
password: {
|
||||
type: "password",
|
||||
enabled: true,
|
||||
config: {
|
||||
hashing: "sha256",
|
||||
},
|
||||
},
|
||||
}),
|
||||
guard: guardConfigSchema.optional(),
|
||||
roles: s.record(guardRoleSchema, { default: {} }).optional(),
|
||||
},
|
||||
{ title: "Authentication" },
|
||||
);
|
||||
}
|
||||
export const authConfigSchema = buildAuthSchema();
|
||||
},
|
||||
}),
|
||||
guard: guardConfigSchema.optional(),
|
||||
roles: s.record(guardRoleSchema, { default: {} }).optional(),
|
||||
},
|
||||
{ title: "Authentication" },
|
||||
);
|
||||
|
||||
export type AppAuthStrategies = s.Static<(typeof authConfigSchema)["properties"]["strategies"]>;
|
||||
export type AppAuthJWTConfig = s.Static<typeof jwtConfig>;
|
||||
|
||||
export type AppAuthSchema = s.Static<typeof authConfigSchema>;
|
||||
|
||||
@@ -30,7 +30,7 @@ const oauthSchemaCustom = s.strictObject(
|
||||
{ title: "Custom OAuth" },
|
||||
);
|
||||
|
||||
export type OAuthConfigCustom = s.Static<typeof oauthSchemaCustom>;
|
||||
type OAuthConfigCustom = s.Static<typeof oauthSchemaCustom>;
|
||||
|
||||
export type UserProfile = {
|
||||
sub: string;
|
||||
|
||||
@@ -26,7 +26,7 @@ const schemaProvided = s.object(
|
||||
},
|
||||
{ title: "OAuth" },
|
||||
);
|
||||
export type ProvidedOAuthConfig = s.Static<typeof schemaProvided>;
|
||||
type ProvidedOAuthConfig = s.Static<typeof schemaProvided>;
|
||||
|
||||
export type CustomOAuthConfig = {
|
||||
type: SupportedTypes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* These are package global defaults.
|
||||
*/
|
||||
import type { Generated } from "kysely";
|
||||
import type { Generated } from "./types";
|
||||
|
||||
export type PrimaryFieldType<IdType = number | string> = IdType | Generated<IdType>;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type Constructor<T> = new (...args: any[]) => T;
|
||||
export type ClassThatImplements<T> = Constructor<T> & { prototype: T };
|
||||
|
||||
export type RegisterFn<Item> = (unknown: any) => Item;
|
||||
|
||||
export class Registry<
|
||||
|
||||
@@ -4,3 +4,194 @@ export interface Serializable<Class, Json extends object = object> {
|
||||
}
|
||||
|
||||
export type MaybePromise<T> = T | Promise<T>;
|
||||
|
||||
/**
|
||||
* Types below are copied from kysely.
|
||||
*/
|
||||
|
||||
export type DrainOuterGeneric<T> = [T] extends [unknown] ? T : never;
|
||||
/**
|
||||
* This type can be used to specify a different type for
|
||||
* select, insert and update operations.
|
||||
*
|
||||
* Also see the {@link Generated} type.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* The next example defines a number column that is optional
|
||||
* in inserts and updates. All columns are always optional
|
||||
* in updates so therefore we don't need to specify `undefined`
|
||||
* for the update type. The type below is useful for all kinds of
|
||||
* database generated columns like identifiers. The `Generated`
|
||||
* type is actually just a shortcut for the type in this example:
|
||||
*
|
||||
* ```ts
|
||||
* type GeneratedNumber = ColumnType<number, number | undefined, number>
|
||||
* ```
|
||||
*
|
||||
* The above example makes the column optional in inserts
|
||||
* and updates, but you can still choose to provide the
|
||||
* column. If you want to prevent insertion/update you
|
||||
* can se the type as `never`:
|
||||
*
|
||||
* ```ts
|
||||
* type ReadonlyNumber = ColumnType<number, never, never>
|
||||
* ```
|
||||
*
|
||||
* Here's one more example where the type is different
|
||||
* for each different operation:
|
||||
*
|
||||
* ```ts
|
||||
* type UnupdateableDate = ColumnType<Date, string, never>
|
||||
* ```
|
||||
*/
|
||||
export type ColumnType<SelectType, InsertType = SelectType, UpdateType = SelectType> = {
|
||||
readonly __select__: SelectType;
|
||||
readonly __insert__: InsertType;
|
||||
readonly __update__: UpdateType;
|
||||
};
|
||||
/**
|
||||
* A shortcut for defining database-generated columns. The type
|
||||
* is the same for all selects, inserts and updates but the
|
||||
* column is optional for inserts and updates.
|
||||
*
|
||||
* The update type is `S` instead of `S | undefined` because updates are always
|
||||
* optional --> no need to specify optionality.
|
||||
* ```
|
||||
*/
|
||||
export type Generated<S> = ColumnType<S, S | undefined, S>;
|
||||
/**
|
||||
* A shortcut for defining columns that are only database-generated
|
||||
* (like postgres GENERATED ALWAYS AS IDENTITY). No insert/update
|
||||
* is allowed.
|
||||
*/
|
||||
export type GeneratedAlways<S> = ColumnType<S, never, never>;
|
||||
/**
|
||||
* A shortcut for defining JSON columns, which are by default inserted/updated
|
||||
* as stringified JSON strings.
|
||||
*/
|
||||
export type JSONColumnType<
|
||||
SelectType extends object | null,
|
||||
InsertType = string,
|
||||
UpdateType = string,
|
||||
> = ColumnType<SelectType, InsertType, UpdateType>;
|
||||
/**
|
||||
* Evaluates to `K` if `T` can be `null` or `undefined`.
|
||||
*/
|
||||
type IfNullable<T, K> = undefined extends T ? K : null extends T ? K : never;
|
||||
/**
|
||||
* Evaluates to `K` if `T` can't be `null` or `undefined`.
|
||||
*/
|
||||
type IfNotNullable<T, K> = undefined extends T
|
||||
? never
|
||||
: null extends T
|
||||
? never
|
||||
: T extends never
|
||||
? never
|
||||
: K;
|
||||
/**
|
||||
* Evaluates to `K` if `T` isn't `never`.
|
||||
*/
|
||||
type IfNotNever<T, K> = T extends never ? never : K;
|
||||
export type SelectType<T> = T extends ColumnType<infer S, any, any> ? S : T;
|
||||
export type InsertType<T> = T extends ColumnType<any, infer I, any> ? I : T;
|
||||
export type UpdateType<T> = T extends ColumnType<any, any, infer U> ? U : T;
|
||||
/**
|
||||
* Keys of `R` whose `InsertType` values can be `null` or `undefined`.
|
||||
*/
|
||||
export type NullableInsertKeys<R> = {
|
||||
[K in keyof R]: IfNullable<InsertType<R[K]>, K>;
|
||||
}[keyof R];
|
||||
/**
|
||||
* Keys of `R` whose `InsertType` values can't be `null` or `undefined`.
|
||||
*/
|
||||
export type NonNullableInsertKeys<R> = {
|
||||
[K in keyof R]: IfNotNullable<InsertType<R[K]>, K>;
|
||||
}[keyof R];
|
||||
/**
|
||||
* Keys of `R` whose `SelectType` values are not `never`
|
||||
*/
|
||||
type NonNeverSelectKeys<R> = {
|
||||
[K in keyof R]: IfNotNever<SelectType<R[K]>, K>;
|
||||
}[keyof R];
|
||||
/**
|
||||
* Keys of `R` whose `UpdateType` values are not `never`
|
||||
*/
|
||||
export type UpdateKeys<R> = {
|
||||
[K in keyof R]: IfNotNever<UpdateType<R[K]>, K>;
|
||||
}[keyof R];
|
||||
/**
|
||||
* Given a table interface, extracts the select type from all
|
||||
* {@link ColumnType} types.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* ```ts
|
||||
* interface PersonTable {
|
||||
* id: Generated<number>
|
||||
* first_name: string
|
||||
* modified_at: ColumnType<Date, string, never>
|
||||
* }
|
||||
*
|
||||
* type Person = Selectable<PersonTable>
|
||||
* // {
|
||||
* // id: number,
|
||||
* // first_name: string
|
||||
* // modified_at: Date
|
||||
* // }
|
||||
* ```
|
||||
*/
|
||||
export type Selectable<R> = DrainOuterGeneric<{
|
||||
[K in NonNeverSelectKeys<R>]: SelectType<R[K]>;
|
||||
}>;
|
||||
/**
|
||||
* Given a table interface, extracts the insert type from all
|
||||
* {@link ColumnType} types.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* ```ts
|
||||
* interface PersonTable {
|
||||
* id: Generated<number>
|
||||
* first_name: string
|
||||
* modified_at: ColumnType<Date, string, never>
|
||||
* }
|
||||
*
|
||||
* type InsertablePerson = Insertable<PersonTable>
|
||||
* // {
|
||||
* // id?: number,
|
||||
* // first_name: string
|
||||
* // modified_at: string
|
||||
* // }
|
||||
* ```
|
||||
*/
|
||||
export type Insertable<R> = DrainOuterGeneric<
|
||||
{
|
||||
[K in NonNullableInsertKeys<R>]: InsertType<R[K]>;
|
||||
} & {
|
||||
[K in NullableInsertKeys<R>]?: InsertType<R[K]>;
|
||||
}
|
||||
>;
|
||||
/**
|
||||
* Given a table interface, extracts the update type from all
|
||||
* {@link ColumnType} types.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* ```ts
|
||||
* interface PersonTable {
|
||||
* id: Generated<number>
|
||||
* first_name: string
|
||||
* modified_at: ColumnType<Date, string, never>
|
||||
* }
|
||||
*
|
||||
* type UpdateablePerson = Updateable<PersonTable>
|
||||
* // {
|
||||
* // id?: number,
|
||||
* // first_name?: string
|
||||
* // }
|
||||
* ```
|
||||
*/
|
||||
export type Updateable<R> = DrainOuterGeneric<{
|
||||
[K in UpdateKeys<R>]?: UpdateType<R[K]>;
|
||||
}>;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { DB, EntityData, RepoQueryIn } from "bknd";
|
||||
import type { DB, EntityData, RepoQueryIn, Insertable, Selectable, Updateable } from "bknd";
|
||||
|
||||
import type { Insertable, Selectable, Updateable } from "kysely";
|
||||
import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules";
|
||||
import type { FetchPromise, ResponseObject } from "modules/ModuleApi";
|
||||
import type { RepositoryResultJSON } from "data/entities/query/RepositoryResult";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { autoFormatString } from "bknd/utils";
|
||||
import type { Entity, EntityManager, TEntityType } from "data/entities";
|
||||
import type { EntityRelation } from "data/relations";
|
||||
import { autoFormatString } from "core/utils";
|
||||
import { usersFields } from "auth/auth-entities";
|
||||
import { mediaFields } from "media/media-entities";
|
||||
|
||||
@@ -170,8 +170,7 @@ export class EntityTypescript {
|
||||
const strings: string[] = [];
|
||||
const tables: Record<string, string> = {};
|
||||
const imports: Record<string, string[]> = {
|
||||
"bknd/core": ["DB"],
|
||||
kysely: ["Insertable", "Selectable", "Updateable", "Generated"],
|
||||
bknd: ["DB", "Insertable", "Selectable", "Updateable", "Generated"],
|
||||
};
|
||||
|
||||
// add global types
|
||||
@@ -207,7 +206,7 @@ export class EntityTypescript {
|
||||
strings.push(tables_string);
|
||||
|
||||
// merge
|
||||
let merge = `declare module "bknd/core" {\n`;
|
||||
let merge = `declare module "bknd" {\n`;
|
||||
for (const systemEntity of system_entities) {
|
||||
const system_fields = Object.keys(systemEntities[systemEntity.name]);
|
||||
const additional_fields = systemEntity.fields
|
||||
|
||||
+1
-1
@@ -51,6 +51,7 @@ export type {
|
||||
ListenerHandler,
|
||||
} from "core/events/EventListener";
|
||||
export { EventManager, type EmitsEvents, type EventClass } from "core/events/EventManager";
|
||||
export type * from "core/types";
|
||||
|
||||
/**
|
||||
* Auth
|
||||
@@ -69,7 +70,6 @@ export type {
|
||||
UserPool,
|
||||
AuthAction,
|
||||
AuthUserResolver,
|
||||
Authenticator,
|
||||
} from "auth/authenticate/Authenticator";
|
||||
export { AuthStrategy } from "auth/authenticate/strategies/Strategy";
|
||||
export * as AuthPermissions from "auth/auth-permissions";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Storage } from "media/storage/Storage";
|
||||
import { Module } from "modules/Module";
|
||||
import { type FieldSchema, em, entity } from "../data/prototype";
|
||||
import { MediaController } from "./api/MediaController";
|
||||
import { buildMediaSchema, MediaAdapterRegistry, type TAppMediaConfig } from "./media-schema";
|
||||
import { buildMediaSchema, registry, type TAppMediaConfig } from "./media-schema";
|
||||
import { mediaFields } from "./media-entities";
|
||||
import * as MediaPermissions from "media/media-permissions";
|
||||
|
||||
@@ -36,7 +36,7 @@ export class AppMedia extends Module<Required<TAppMediaConfig>> {
|
||||
let adapter: StorageAdapter;
|
||||
try {
|
||||
const { type, config } = this.config.adapter;
|
||||
const cls = MediaAdapterRegistry.get(type as any).cls;
|
||||
const cls = registry.get(type as any).cls;
|
||||
adapter = new cls(config as any);
|
||||
|
||||
this._storage = new Storage(adapter, this.config.storage, this.ctx.emgr);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { type Constructor, Registry } from "core/registry/Registry";
|
||||
import type { StorageAdapter } from "./storage/StorageAdapter";
|
||||
import type { s } from "bknd/utils";
|
||||
import { StorageS3Adapter } from "./storage/adapters/s3/StorageS3Adapter";
|
||||
import { StorageCloudinaryAdapter } from "./storage/adapters/cloudinary/StorageCloudinaryAdapter";
|
||||
|
||||
type ClassThatImplements<T> = Constructor<T> & { prototype: T };
|
||||
|
||||
export const MediaAdapterRegistry = new Registry<{
|
||||
cls: ClassThatImplements<StorageAdapter>;
|
||||
schema: s.Schema;
|
||||
}>((cls: ClassThatImplements<StorageAdapter>) => ({
|
||||
cls,
|
||||
schema: cls.prototype.getSchema() as s.Schema,
|
||||
}))
|
||||
.register("s3", StorageS3Adapter)
|
||||
.register("cloudinary", StorageCloudinaryAdapter);
|
||||
|
||||
export const MediaAdapters = {
|
||||
s3: {
|
||||
cls: StorageS3Adapter,
|
||||
schema: StorageS3Adapter.prototype.getSchema(),
|
||||
},
|
||||
cloudinary: {
|
||||
cls: StorageCloudinaryAdapter,
|
||||
schema: StorageCloudinaryAdapter.prototype.getSchema(),
|
||||
},
|
||||
} as const;
|
||||
@@ -1,21 +1,15 @@
|
||||
import { MediaAdapters } from "media/media-registry";
|
||||
import { registries } from "modules/registries";
|
||||
import { s, objectTransform } from "bknd/utils";
|
||||
import { Registry, type ClassThatImplements } from "core/registry/Registry";
|
||||
import type { StorageAdapter } from "./storage/StorageAdapter";
|
||||
import { StorageS3Adapter } from "./storage/adapters/s3/StorageS3Adapter";
|
||||
import { StorageCloudinaryAdapter } from "./storage/adapters/cloudinary/StorageCloudinaryAdapter";
|
||||
|
||||
export const MediaAdapterRegistry = new Registry<{
|
||||
cls: ClassThatImplements<StorageAdapter>;
|
||||
schema: s.Schema;
|
||||
}>((cls: ClassThatImplements<StorageAdapter>) => ({
|
||||
cls,
|
||||
schema: cls.prototype.getSchema() as s.Schema,
|
||||
}))
|
||||
.register("s3", StorageS3Adapter)
|
||||
.register("cloudinary", StorageCloudinaryAdapter);
|
||||
export const ADAPTERS = {
|
||||
...MediaAdapters,
|
||||
} as const;
|
||||
|
||||
export const registry = registries.media;
|
||||
|
||||
export function buildMediaSchema() {
|
||||
const adapterSchemaObject = objectTransform(MediaAdapterRegistry.all(), (adapter, name) => {
|
||||
const adapterSchemaObject = objectTransform(registry.all(), (adapter, name) => {
|
||||
return s.strictObject(
|
||||
{
|
||||
type: s.literal(name),
|
||||
|
||||
@@ -696,13 +696,13 @@ export class ModuleManager {
|
||||
return transformObject(this.modules, (module) => module.toJSON(true)) as any;
|
||||
}
|
||||
|
||||
getSchema(): { version: number } & ModuleSchemas {
|
||||
getSchema() {
|
||||
const schemas = transformObject(this.modules, (module) => module.getSchema());
|
||||
|
||||
return {
|
||||
version: this.version(),
|
||||
...schemas,
|
||||
} as any;
|
||||
};
|
||||
}
|
||||
|
||||
toJSON(secrets?: boolean): { version: number } & ModuleConfigs {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { AuthStrategyRegistry } from "auth/auth-schema";
|
||||
import { MediaAdapterRegistry } from "media/media-schema";
|
||||
import { MediaAdapterRegistry } from "media/media-registry";
|
||||
|
||||
const registries = {
|
||||
media: MediaAdapterRegistry,
|
||||
auth: AuthStrategyRegistry,
|
||||
} as const;
|
||||
|
||||
export { registries };
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
TbBrandX,
|
||||
TbSettings,
|
||||
} from "react-icons/tb";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { useBknd } from "ui/client/bknd";
|
||||
import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
@@ -75,7 +76,7 @@ function AuthStrategiesListInternal() {
|
||||
|
||||
return (
|
||||
<Form
|
||||
schema={JSON.parse(JSON.stringify(schema))}
|
||||
schema={schema as any}
|
||||
initialValues={config}
|
||||
onSubmit={handleSubmit}
|
||||
options={formOptions}
|
||||
|
||||
+1
-5
@@ -32,12 +32,8 @@
|
||||
"*": ["./src/*"],
|
||||
"bknd": ["./src/index.ts"],
|
||||
"bknd/utils": ["./src/core/utils/index.ts"],
|
||||
"bknd/core": ["./src/core/index.ts"],
|
||||
"bknd/adapter": ["./src/adapter/index.ts"],
|
||||
"bknd/client": ["./src/ui/client/index.ts"],
|
||||
"bknd/data": ["./src/data/index.ts"],
|
||||
"bknd/media": ["./src/media/index.ts"],
|
||||
"bknd/auth": ["./src/auth/index.ts"]
|
||||
"bknd/client": ["./src/ui/client/index.ts"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
|
||||
Reference in New Issue
Block a user