mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
docs: plugins, cloudflare, sdk, elements, database (#240)
* docs: added plugins docs, updated cloudflare docs * updated cli help text * added `systemEntity` and added docs on how to work with system entities * docs: added defaults to cloudflare image plugin * docs: updated sdk and elements
This commit is contained in:
@@ -23,13 +23,34 @@ import type { IEmailDriver, ICacheDriver } from "core/drivers";
|
||||
import { Api, type ApiOptions } from "Api";
|
||||
|
||||
export type AppPluginConfig = {
|
||||
/**
|
||||
* The name of the plugin.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The schema of the plugin.
|
||||
*/
|
||||
schema?: () => MaybePromise<ReturnType<typeof prototypeEm> | void>;
|
||||
/**
|
||||
* Called before the app is built.
|
||||
*/
|
||||
beforeBuild?: () => MaybePromise<void>;
|
||||
/**
|
||||
* Called after the app is built.
|
||||
*/
|
||||
onBuilt?: () => MaybePromise<void>;
|
||||
/**
|
||||
* Called when the server is initialized.
|
||||
*/
|
||||
onServerInit?: (server: Hono<ServerEnv>) => MaybePromise<void>;
|
||||
onFirstBoot?: () => MaybePromise<void>;
|
||||
/**
|
||||
* Called when the app is booted.
|
||||
*/
|
||||
onBoot?: () => MaybePromise<void>;
|
||||
/**
|
||||
* Called when the app is first booted.
|
||||
*/
|
||||
onFirstBoot?: () => MaybePromise<void>;
|
||||
};
|
||||
export type AppPlugin = (app: App) => AppPluginConfig;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ type BunEnv = Bun.Env;
|
||||
export type BunBkndConfig<Env = BunEnv> = RuntimeBkndConfig<Env> & Omit<ServeOptions, "fetch">;
|
||||
|
||||
export async function createApp<Env = BunEnv>(
|
||||
{ distPath, ...config }: BunBkndConfig<Env> = {},
|
||||
{ distPath, serveStatic: _serveStatic, ...config }: BunBkndConfig<Env> = {},
|
||||
args: Env = {} as Env,
|
||||
opts?: RuntimeOptions,
|
||||
) {
|
||||
@@ -20,7 +20,11 @@ export async function createApp<Env = BunEnv>(
|
||||
|
||||
return await createRuntimeApp(
|
||||
{
|
||||
serveStatic: serveStatic({ root }),
|
||||
serveStatic:
|
||||
_serveStatic ??
|
||||
serveStatic({
|
||||
root,
|
||||
}),
|
||||
...config,
|
||||
},
|
||||
args ?? (process.env as Env),
|
||||
|
||||
@@ -6,7 +6,7 @@ import { resolve } from "node:path";
|
||||
* Vite plugin that provides Node.js filesystem access during development
|
||||
* by injecting a polyfill into the SSR environment
|
||||
*/
|
||||
export function devFsPlugin({
|
||||
export function devFsVitePlugin({
|
||||
verbose = false,
|
||||
configFile = "bknd.config.ts",
|
||||
}: {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { usersFields } from "./auth-entities";
|
||||
import { Authenticator } from "./authenticate/Authenticator";
|
||||
import { Role } from "./authorize/Role";
|
||||
|
||||
export type UsersFields = typeof AppAuth.usersFields;
|
||||
export type UserFieldSchema = FieldSchema<typeof AppAuth.usersFields>;
|
||||
declare module "bknd" {
|
||||
interface Users extends AppEntity, UserFieldSchema {}
|
||||
|
||||
@@ -39,6 +39,9 @@ import {
|
||||
type PolymorphicRelationConfig,
|
||||
} from "data/relations";
|
||||
|
||||
import type { MediaFields } from "media/AppMedia";
|
||||
import type { UsersFields } from "auth/AppAuth";
|
||||
|
||||
type Options<Config = any> = {
|
||||
entity: { name: string; fields: Record<string, Field<any, any, any>> };
|
||||
field_name: string;
|
||||
@@ -199,6 +202,18 @@ export function entity<
|
||||
return new Entity(name, _fields, config, type);
|
||||
}
|
||||
|
||||
type SystemEntities = {
|
||||
users: UsersFields;
|
||||
media: MediaFields;
|
||||
};
|
||||
|
||||
export function systemEntity<
|
||||
E extends keyof SystemEntities,
|
||||
Fields extends Record<string, Field<any, any, any>>,
|
||||
>(name: E, fields: Fields) {
|
||||
return entity<E, SystemEntities[E] & Fields>(name, fields as any);
|
||||
}
|
||||
|
||||
export function relation<Local extends Entity>(local: Local) {
|
||||
return {
|
||||
manyToOne: <Foreign extends Entity>(foreign: Foreign, config?: ManyToOneRelationConfig) => {
|
||||
|
||||
@@ -157,6 +157,7 @@ export {
|
||||
medium,
|
||||
make,
|
||||
entity,
|
||||
systemEntity,
|
||||
relation,
|
||||
index,
|
||||
em,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { buildMediaSchema, registry, type TAppMediaConfig } from "./media-schema
|
||||
import { mediaFields } from "./media-entities";
|
||||
import * as MediaPermissions from "media/media-permissions";
|
||||
|
||||
export type MediaFields = typeof AppMedia.mediaFields;
|
||||
export type MediaFieldSchema = FieldSchema<typeof AppMedia.mediaFields>;
|
||||
declare module "bknd" {
|
||||
interface Media extends AppEntity, MediaFieldSchema {}
|
||||
|
||||
@@ -19,22 +19,46 @@ const schema = s.partialObject({
|
||||
type ImageOptimizationSchema = s.Static<typeof schema>;
|
||||
|
||||
export type CloudflareImageOptimizationOptions = {
|
||||
/**
|
||||
* The url to access the image optimization plugin
|
||||
* @default /api/plugin/image/optimize
|
||||
*/
|
||||
accessUrl?: string;
|
||||
/**
|
||||
* The path to resolve the image from
|
||||
* @default /api/media/file
|
||||
*/
|
||||
resolvePath?: string;
|
||||
/**
|
||||
* Whether to explain the image optimization schema
|
||||
* @default false
|
||||
*/
|
||||
explain?: boolean;
|
||||
/**
|
||||
* The default options to use
|
||||
* @default {}
|
||||
*/
|
||||
defaultOptions?: ImageOptimizationSchema;
|
||||
/**
|
||||
* The fixed options to use
|
||||
* @default {}
|
||||
*/
|
||||
fixedOptions?: ImageOptimizationSchema;
|
||||
/**
|
||||
* The cache control to use
|
||||
* @default public, max-age=31536000, immutable
|
||||
*/
|
||||
cacheControl?: string;
|
||||
};
|
||||
|
||||
export function cloudflareImageOptimization({
|
||||
accessUrl = "/_plugin/image/optimize",
|
||||
accessUrl = "/api/plugin/image/optimize",
|
||||
resolvePath = "/api/media/file",
|
||||
explain = false,
|
||||
defaultOptions = {},
|
||||
fixedOptions = {},
|
||||
}: CloudflareImageOptimizationOptions = {}): AppPlugin {
|
||||
const disallowedAccessUrls = ["/api", "/admin", "/_optimize"];
|
||||
const disallowedAccessUrls = ["/api", "/admin", "/api/plugin"];
|
||||
if (disallowedAccessUrls.includes(accessUrl) || accessUrl.length < 2) {
|
||||
throw new Error(`Disallowed accessUrl: ${accessUrl}`);
|
||||
}
|
||||
|
||||
@@ -46,24 +46,73 @@ export type DropzoneRenderProps = {
|
||||
};
|
||||
|
||||
export type DropzoneProps = {
|
||||
/**
|
||||
* Get the upload info for a file
|
||||
*/
|
||||
getUploadInfo: (file: { path: string }) => { url: string; headers?: Headers; method?: string };
|
||||
/**
|
||||
* Handle the deletion of a file
|
||||
*/
|
||||
handleDelete: (file: { path: string }) => Promise<boolean>;
|
||||
/**
|
||||
* The initial items to display
|
||||
*/
|
||||
initialItems?: FileState[];
|
||||
flow?: "start" | "end";
|
||||
/**
|
||||
* Maximum number of media items that can be uploaded
|
||||
*/
|
||||
maxItems?: number;
|
||||
/**
|
||||
* The allowed mime types
|
||||
*/
|
||||
allowedMimeTypes?: string[];
|
||||
/**
|
||||
* If true, the media item will be overwritten on entity media uploads if limit was reached
|
||||
*/
|
||||
overwrite?: boolean;
|
||||
/**
|
||||
* If true, the media items will be uploaded automatically
|
||||
*/
|
||||
autoUpload?: boolean;
|
||||
/**
|
||||
* Whether to add new items to the start or end of the list
|
||||
* @default "start"
|
||||
*/
|
||||
flow?: "start" | "end";
|
||||
/**
|
||||
* The on rejected callback
|
||||
*/
|
||||
onRejected?: (files: FileWithPath[]) => void;
|
||||
/**
|
||||
* The on deleted callback
|
||||
*/
|
||||
onDeleted?: (file: { path: string }) => void;
|
||||
/**
|
||||
* The on uploaded all callback
|
||||
*/
|
||||
onUploadedAll?: (files: FileStateWithData[]) => void;
|
||||
/**
|
||||
* The on uploaded callback
|
||||
*/
|
||||
onUploaded?: (file: FileStateWithData) => void;
|
||||
/**
|
||||
* The on clicked callback
|
||||
*/
|
||||
onClick?: (file: FileState) => void;
|
||||
/**
|
||||
* The placeholder to use
|
||||
*/
|
||||
placeholder?: {
|
||||
show?: boolean;
|
||||
text?: string;
|
||||
};
|
||||
/**
|
||||
* The footer to render
|
||||
*/
|
||||
footer?: ReactNode;
|
||||
/**
|
||||
* The children to render
|
||||
*/
|
||||
children?: ReactNode | ((props: DropzoneRenderProps) => ReactNode);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,15 +10,38 @@ import { mediaItemsToFileStates } from "./helper";
|
||||
import { useInViewport } from "@mantine/hooks";
|
||||
|
||||
export type DropzoneContainerProps = {
|
||||
/**
|
||||
* The initial items to display
|
||||
* @default []
|
||||
*/
|
||||
initialItems?: MediaFieldSchema[] | false;
|
||||
/**
|
||||
* Whether to use infinite scrolling
|
||||
* @default false
|
||||
*/
|
||||
infinite?: boolean;
|
||||
/**
|
||||
* If given, the initial media items fetched will be from this entity
|
||||
* @default undefined
|
||||
*/
|
||||
entity?: {
|
||||
name: string;
|
||||
id: PrimaryFieldType;
|
||||
field: string;
|
||||
};
|
||||
/**
|
||||
* The media config
|
||||
* @default undefined
|
||||
*/
|
||||
media?: Pick<TAppMediaConfig, "entity_name" | "storage">;
|
||||
/**
|
||||
* Query to filter the media items
|
||||
*/
|
||||
query?: RepoQueryIn;
|
||||
/**
|
||||
* Whether to use a random filename
|
||||
* @default false
|
||||
*/
|
||||
randomFilename?: boolean;
|
||||
} & Omit<Partial<DropzoneProps>, "initialItems">;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user