mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
added bknd admin config override to make it easier to include <Admin />
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { withApi } from "bknd/adapter/nextjs";
|
import { withApi } from "bknd/adapter/nextjs";
|
||||||
|
import type { BkndAdminProps } from "bknd/ui";
|
||||||
import type { InferGetServerSidePropsType } from "next";
|
import type { InferGetServerSidePropsType } from "next";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
@@ -10,15 +11,10 @@ export const getServerSideProps = withApi(async (context) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export function adminPage() {
|
export function adminPage(adminProps?: BkndAdminProps) {
|
||||||
const Admin = dynamic(() => import("bknd/ui").then((mod) => mod.Admin), { ssr: false });
|
const Admin = dynamic(() => import("bknd/ui").then((mod) => mod.Admin), { ssr: false });
|
||||||
const ClientProvider = dynamic(() => import("bknd/ui").then((mod) => mod.ClientProvider));
|
|
||||||
return (props: InferGetServerSidePropsType<typeof getServerSideProps>) => {
|
return (props: InferGetServerSidePropsType<typeof getServerSideProps>) => {
|
||||||
if (typeof document === "undefined") return null;
|
if (typeof document === "undefined") return null;
|
||||||
return (
|
return <Admin withProvider={{ user: props.user }} {...adminProps} />;
|
||||||
<ClientProvider user={props.user}>
|
|
||||||
<Admin />
|
|
||||||
</ClientProvider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import type { BkndAdminProps } from "bknd/ui";
|
||||||
import { Suspense, lazy, useEffect, useState } from "react";
|
import { Suspense, lazy, useEffect, useState } from "react";
|
||||||
|
|
||||||
export function adminPage() {
|
export function adminPage(props?: BkndAdminProps) {
|
||||||
const Admin = lazy(() => import("bknd/ui").then((mod) => ({ default: mod.Admin })));
|
const Admin = lazy(() => import("bknd/ui").then((mod) => ({ default: mod.Admin })));
|
||||||
return () => {
|
return () => {
|
||||||
const [loaded, setLoaded] = useState(false);
|
const [loaded, setLoaded] = useState(false);
|
||||||
@@ -12,7 +13,7 @@ export function adminPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<Admin />
|
<Admin {...props} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { MantineProvider } from "@mantine/core";
|
import { MantineProvider } from "@mantine/core";
|
||||||
import { Notifications } from "@mantine/notifications";
|
import { Notifications } from "@mantine/notifications";
|
||||||
|
import type { ModuleConfigs } from "modules";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FlashMessage } from "ui/modules/server/FlashMessage";
|
import { FlashMessage } from "ui/modules/server/FlashMessage";
|
||||||
import { BkndProvider, ClientProvider, type ClientProviderProps, useBknd } from "./client";
|
import { BkndProvider, ClientProvider, type ClientProviderProps, useBknd } from "./client";
|
||||||
@@ -10,12 +11,16 @@ import { Routes } from "./routes";
|
|||||||
export type BkndAdminProps = {
|
export type BkndAdminProps = {
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
withProvider?: boolean | ClientProviderProps;
|
withProvider?: boolean | ClientProviderProps;
|
||||||
// @todo: add admin config override
|
config?: ModuleConfigs["server"]["admin"];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Admin({ baseUrl: baseUrlOverride, withProvider = false }: BkndAdminProps) {
|
export default function Admin({
|
||||||
|
baseUrl: baseUrlOverride,
|
||||||
|
withProvider = false,
|
||||||
|
config
|
||||||
|
}: BkndAdminProps) {
|
||||||
const Component = (
|
const Component = (
|
||||||
<BkndProvider>
|
<BkndProvider adminOverride={config}>
|
||||||
<AdminInternal />
|
<AdminInternal />
|
||||||
</BkndProvider>
|
</BkndProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type BkndContext = {
|
|||||||
requireSecrets: () => Promise<void>;
|
requireSecrets: () => Promise<void>;
|
||||||
actions: ReturnType<typeof getSchemaActions>;
|
actions: ReturnType<typeof getSchemaActions>;
|
||||||
app: AppReduced;
|
app: AppReduced;
|
||||||
|
adminOverride?: ModuleConfigs["server"]["admin"];
|
||||||
};
|
};
|
||||||
|
|
||||||
const BkndContext = createContext<BkndContext>(undefined!);
|
const BkndContext = createContext<BkndContext>(undefined!);
|
||||||
@@ -21,8 +22,9 @@ export type { TSchemaActions };
|
|||||||
|
|
||||||
export function BkndProvider({
|
export function BkndProvider({
|
||||||
includeSecrets = false,
|
includeSecrets = false,
|
||||||
|
adminOverride,
|
||||||
children
|
children
|
||||||
}: { includeSecrets?: boolean; children: any }) {
|
}: { includeSecrets?: boolean; children: any } & Pick<BkndContext, "adminOverride">) {
|
||||||
const [withSecrets, setWithSecrets] = useState<boolean>(includeSecrets);
|
const [withSecrets, setWithSecrets] = useState<boolean>(includeSecrets);
|
||||||
const [schema, setSchema] =
|
const [schema, setSchema] =
|
||||||
useState<Pick<BkndContext, "version" | "schema" | "config" | "permissions">>();
|
useState<Pick<BkndContext, "version" | "schema" | "config" | "permissions">>();
|
||||||
@@ -64,6 +66,13 @@ export function BkndProvider({
|
|||||||
permissions: []
|
permissions: []
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
|
if (adminOverride) {
|
||||||
|
schema.config.server.admin = {
|
||||||
|
...schema.config.server.admin,
|
||||||
|
...adminOverride
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
setSchema(schema);
|
setSchema(schema);
|
||||||
setWithSecrets(_includeSecrets);
|
setWithSecrets(_includeSecrets);
|
||||||
@@ -86,7 +95,7 @@ export function BkndProvider({
|
|||||||
const actions = getSchemaActions({ client, setSchema, reloadSchema });
|
const actions = getSchemaActions({ client, setSchema, reloadSchema });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BkndContext.Provider value={{ ...schema, actions, requireSecrets, app }}>
|
<BkndContext.Provider value={{ ...schema, actions, requireSecrets, app, adminOverride }}>
|
||||||
{children}
|
{children}
|
||||||
</BkndContext.Provider>
|
</BkndContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { ComponentPropsWithoutRef } from "react";
|
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export type AlertProps = ComponentPropsWithoutRef<"div"> & {
|
export type AlertProps = ComponentPropsWithoutRef<"div"> & {
|
||||||
className?: string;
|
className?: string;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
message?: string;
|
message?: ReactNode | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Base: React.FC<AlertProps> = ({ visible = true, title, message, className, ...props }) =>
|
const Base: React.FC<AlertProps> = ({ visible = true, title, message, className, ...props }) =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export { default as Admin } from "./Admin";
|
export { default as Admin, type BkndAdminProps } from "./Admin";
|
||||||
export { Button } from "./components/buttons/Button";
|
export { Button } from "./components/buttons/Button";
|
||||||
export { Context } from "./components/Context";
|
export { Context } from "./components/Context";
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ export function Setting<Schema extends TObject = any>({
|
|||||||
<Breadcrumbs path={path} />
|
<Breadcrumbs path={path} />
|
||||||
</AppShell.SectionHeader>
|
</AppShell.SectionHeader>
|
||||||
<AppShell.Scrollable key={path.join("-")}>
|
<AppShell.Scrollable key={path.join("-")}>
|
||||||
{typeof showAlert === "string" && <Alert.Warning message={showAlert} />}
|
{typeof showAlert !== "undefined" && <Alert.Warning message={showAlert} />}
|
||||||
|
|
||||||
<div className="flex flex-col flex-grow p-3 gap-3">
|
<div className="flex flex-col flex-grow p-3 gap-3">
|
||||||
<JsonSchemaForm
|
<JsonSchemaForm
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Setting } from "./components/Setting";
|
|||||||
import { AuthSettings } from "./routes/auth.settings";
|
import { AuthSettings } from "./routes/auth.settings";
|
||||||
import { DataSettings } from "./routes/data.settings";
|
import { DataSettings } from "./routes/data.settings";
|
||||||
import { FlowsSettings } from "./routes/flows.settings";
|
import { FlowsSettings } from "./routes/flows.settings";
|
||||||
|
import { ServerSettings } from "./routes/server.settings";
|
||||||
|
|
||||||
function SettingsSidebar() {
|
function SettingsSidebar() {
|
||||||
const { version, schema } = useBknd();
|
const { version, schema } = useBknd();
|
||||||
@@ -39,36 +40,6 @@ function SettingsSidebar() {
|
|||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
{/*<button
|
|
||||||
onClick={() =>
|
|
||||||
modals.openContextModal({
|
|
||||||
modal: "test",
|
|
||||||
title: "Test Modal",
|
|
||||||
innerProps: { modalBody: "This is a test modal" }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
modal
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
bkndModals.open(bkndModals.ids.test, { modalBody: "test" }, { title: "what" })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
modal2
|
|
||||||
</button>
|
|
||||||
<button onClick={() => bkndModals.open("test", { modalBody: "test" })}>modal</button>
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
bkndModals.open("debug", {
|
|
||||||
data: {
|
|
||||||
one: { what: 1 }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
debug
|
|
||||||
</button>*/}
|
|
||||||
</AppShell.Scrollable>
|
</AppShell.Scrollable>
|
||||||
</AppShell.Sidebar>
|
</AppShell.Sidebar>
|
||||||
);
|
);
|
||||||
@@ -145,12 +116,7 @@ const SettingRoutesRoutes = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FallbackRoutes
|
<ServerSettings schema={schema.server} config={config.server} />
|
||||||
module="server"
|
|
||||||
schema={schema}
|
|
||||||
config={config}
|
|
||||||
uiSchema={uiSchema.server}
|
|
||||||
/>
|
|
||||||
<DataSettings schema={schema.data} config={config.data} />
|
<DataSettings schema={schema.data} config={config.data} />
|
||||||
<AuthSettings schema={schema.auth} config={config.auth} />
|
<AuthSettings schema={schema.auth} config={config.auth} />
|
||||||
<FallbackRoutes module="media" schema={schema} config={config} uiSchema={uiSchema.media} />
|
<FallbackRoutes module="media" schema={schema} config={config} uiSchema={uiSchema.media} />
|
||||||
|
|||||||
55
app/src/ui/routes/settings/routes/server.settings.tsx
Normal file
55
app/src/ui/routes/settings/routes/server.settings.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
|
import { useBknd } from "ui";
|
||||||
|
import { Setting } from "ui/routes/settings/components/Setting";
|
||||||
|
import { Route } from "wouter";
|
||||||
|
|
||||||
|
const uiSchema = {
|
||||||
|
cors: {
|
||||||
|
allow_methods: {
|
||||||
|
"ui:widget": "checkboxes"
|
||||||
|
},
|
||||||
|
allow_headers: {
|
||||||
|
"ui:options": {
|
||||||
|
orderable: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ServerSettings = ({ schema: _unsafe_copy, config }) => {
|
||||||
|
const { app, adminOverride } = useBknd();
|
||||||
|
const { basepath } = app.getAdminConfig();
|
||||||
|
const _schema = cloneDeep(_unsafe_copy);
|
||||||
|
const prefix = `~/${basepath}/settings`.replace(/\/+/g, "/");
|
||||||
|
|
||||||
|
const schema = _schema;
|
||||||
|
if (adminOverride) {
|
||||||
|
schema.properties.admin.readOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Route path="/server" nest>
|
||||||
|
<Route
|
||||||
|
path="/"
|
||||||
|
component={() => (
|
||||||
|
<Setting
|
||||||
|
options={{
|
||||||
|
showAlert: () => {
|
||||||
|
if (adminOverride) {
|
||||||
|
return "The admin settings are read-only as they are overriden. Remaining server configuration can be edited.";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
schema={schema}
|
||||||
|
uiSchema={uiSchema}
|
||||||
|
config={config}
|
||||||
|
prefix={`${prefix}/server`}
|
||||||
|
path={["server"]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
nest
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -38,7 +38,9 @@ import { adminPage, getServerSideProps } from "bknd/adapter/nextjs";
|
|||||||
import "bknd/dist/styles.css";
|
import "bknd/dist/styles.css";
|
||||||
|
|
||||||
export { getServerSideProps };
|
export { getServerSideProps };
|
||||||
export default adminPage();
|
export default adminPage({
|
||||||
|
config: { basepath: "/admin" }
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example usage of the API in pages dir
|
## Example usage of the API in pages dir
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ Create a new splat route file at `app/routes/admin.$.tsx`:
|
|||||||
import { adminPage } from "bknd/adapter/remix";
|
import { adminPage } from "bknd/adapter/remix";
|
||||||
import "bknd/dist/styles.css";
|
import "bknd/dist/styles.css";
|
||||||
|
|
||||||
export default adminPage();
|
export default adminPage({
|
||||||
|
config: { basepath: "/admin" }
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example usage of the API
|
## Example usage of the API
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ const user = api.getUser();
|
|||||||
|
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<Admin withProvider={{ user }} client:load />
|
<Admin withProvider={{ user }} config={{ basepath: "/admin" }} client:load />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -2,4 +2,8 @@ import { adminPage, getServerSideProps } from "bknd/adapter/nextjs";
|
|||||||
import "bknd/dist/styles.css";
|
import "bknd/dist/styles.css";
|
||||||
|
|
||||||
export { getServerSideProps };
|
export { getServerSideProps };
|
||||||
export default adminPage();
|
export default adminPage({
|
||||||
|
config: {
|
||||||
|
basepath: "/admin"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { adminPage } from "bknd/adapter/remix";
|
import { adminPage } from "bknd/adapter/remix";
|
||||||
import "bknd/dist/styles.css";
|
import "bknd/dist/styles.css";
|
||||||
|
|
||||||
export default adminPage();
|
export default adminPage({
|
||||||
|
config: {
|
||||||
|
basepath: "/admin"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user