mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
added better error messages if config secret permission is missing
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"version": "0.6.0-rc.12",
|
"version": "0.6.0-rc.13",
|
||||||
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, Remix, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, Remix, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
||||||
"homepage": "https://bknd.io",
|
"homepage": "https://bknd.io",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -118,8 +118,6 @@ export class App {
|
|||||||
this.trigger_first_boot = false;
|
this.trigger_first_boot = false;
|
||||||
await this.emgr.emit(new AppFirstBoot({ app: this }));
|
await this.emgr.emit(new AppFirstBoot({ app: this }));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[APP] built");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutateConfig<Module extends keyof Modules>(module: Module) {
|
mutateConfig<Module extends keyof Modules>(module: Module) {
|
||||||
|
|||||||
@@ -111,15 +111,18 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
|||||||
// caused issues because this.entity() was using a reference (for when initial config was given)
|
// caused issues because this.entity() was using a reference (for when initial config was given)
|
||||||
}
|
}
|
||||||
|
|
||||||
entity(e: Entity | keyof TBD | string, silent?: boolean): Entity {
|
entity<Silent extends true | false = false>(
|
||||||
|
e: Entity | keyof TBD | string,
|
||||||
|
silent?: Silent
|
||||||
|
): Silent extends true ? Entity | undefined : Entity {
|
||||||
// make sure to always retrieve by name
|
// make sure to always retrieve by name
|
||||||
const entity = this.entities.find((entity) =>
|
const entity = this.entities.find((entity) =>
|
||||||
e instanceof Entity ? entity.name === e.name : entity.name === e
|
e instanceof Entity ? entity.name === e.name : entity.name === e
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!entity && !silent) {
|
if (!entity) {
|
||||||
// @ts-ignore
|
if (silent === true) return undefined as any;
|
||||||
throw new EntityNotDefinedException(e instanceof Entity ? e.name : e);
|
throw new EntityNotDefinedException(e instanceof Entity ? e.name : (e as string));
|
||||||
}
|
}
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
import { IconAlertHexagon } from "@tabler/icons-react";
|
||||||
import type { ModuleConfigs, ModuleSchemas } from "modules";
|
import type { ModuleConfigs, ModuleSchemas } from "modules";
|
||||||
import { getDefaultConfig, getDefaultSchema } from "modules/ModuleManager";
|
import { getDefaultConfig, getDefaultSchema } from "modules/ModuleManager";
|
||||||
import { createContext, startTransition, useContext, useEffect, useRef, useState } from "react";
|
import { createContext, startTransition, useContext, useEffect, useRef, useState } from "react";
|
||||||
import { useApi } from "ui/client";
|
import { useApi } from "ui/client";
|
||||||
|
import { Button } from "ui/components/buttons/Button";
|
||||||
|
import { Alert } from "ui/components/display/Alert";
|
||||||
import { type TSchemaActions, getSchemaActions } from "./schema/actions";
|
import { type TSchemaActions, getSchemaActions } from "./schema/actions";
|
||||||
import { AppReduced } from "./utils/AppReduced";
|
import { AppReduced } from "./utils/AppReduced";
|
||||||
|
|
||||||
@@ -10,6 +13,7 @@ type BkndContext = {
|
|||||||
schema: ModuleSchemas;
|
schema: ModuleSchemas;
|
||||||
config: ModuleConfigs;
|
config: ModuleConfigs;
|
||||||
permissions: string[];
|
permissions: string[];
|
||||||
|
hasSecrets: boolean;
|
||||||
requireSecrets: () => Promise<void>;
|
requireSecrets: () => Promise<void>;
|
||||||
actions: ReturnType<typeof getSchemaActions>;
|
actions: ReturnType<typeof getSchemaActions>;
|
||||||
app: AppReduced;
|
app: AppReduced;
|
||||||
@@ -32,6 +36,7 @@ export function BkndProvider({
|
|||||||
const [schema, setSchema] =
|
const [schema, setSchema] =
|
||||||
useState<Pick<BkndContext, "version" | "schema" | "config" | "permissions">>();
|
useState<Pick<BkndContext, "version" | "schema" | "config" | "permissions">>();
|
||||||
const [fetched, setFetched] = useState(false);
|
const [fetched, setFetched] = useState(false);
|
||||||
|
const [error, setError] = useState<boolean>();
|
||||||
const errorShown = useRef<boolean>();
|
const errorShown = useRef<boolean>();
|
||||||
const [local_version, set_local_version] = useState(0);
|
const [local_version, set_local_version] = useState(0);
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
@@ -50,15 +55,11 @@ export function BkndProvider({
|
|||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
if (errorShown.current) return;
|
if (errorShown.current) return;
|
||||||
errorShown.current = true;
|
errorShown.current = true;
|
||||||
/*notifications.show({
|
|
||||||
title: "Failed to fetch schema",
|
setError(true);
|
||||||
// @ts-ignore
|
return;
|
||||||
message: body.error,
|
} else if (error) {
|
||||||
color: "red",
|
setError(false);
|
||||||
position: "top-right",
|
|
||||||
autoClose: false,
|
|
||||||
withCloseButton: true
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = res.ok
|
const schema = res.ok
|
||||||
@@ -98,12 +99,24 @@ export function BkndProvider({
|
|||||||
if (!fetched || !schema) return fallback;
|
if (!fetched || !schema) return fallback;
|
||||||
const app = new AppReduced(schema?.config as any);
|
const app = new AppReduced(schema?.config as any);
|
||||||
const actions = getSchemaActions({ api, setSchema, reloadSchema });
|
const actions = getSchemaActions({ api, setSchema, reloadSchema });
|
||||||
|
const hasSecrets = withSecrets && !error;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BkndContext.Provider
|
<BkndContext.Provider
|
||||||
value={{ ...schema, actions, requireSecrets, app, adminOverride }}
|
value={{ ...schema, actions, requireSecrets, app, adminOverride, hasSecrets }}
|
||||||
key={local_version}
|
key={local_version}
|
||||||
>
|
>
|
||||||
|
{error && (
|
||||||
|
<Alert.Exception className="gap-2">
|
||||||
|
<IconAlertHexagon />
|
||||||
|
You attempted to load system configuration with secrets without having proper
|
||||||
|
permission.
|
||||||
|
<a href={schema.config.server.admin.basepath || "/"}>
|
||||||
|
<Button variant="red">Reload</Button>
|
||||||
|
</a>
|
||||||
|
</Alert.Exception>
|
||||||
|
)}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</BkndContext.Provider>
|
</BkndContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,16 +6,27 @@ export type AlertProps = ComponentPropsWithoutRef<"div"> & {
|
|||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
message?: ReactNode | string;
|
message?: ReactNode | string;
|
||||||
|
children?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Base: React.FC<AlertProps> = ({ visible = true, title, message, className, ...props }) =>
|
const Base: React.FC<AlertProps> = ({
|
||||||
|
visible = true,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) =>
|
||||||
visible ? (
|
visible ? (
|
||||||
<div
|
<div
|
||||||
{...props}
|
{...props}
|
||||||
className={twMerge("flex flex-row dark:bg-amber-300/20 bg-amber-200 p-4", className)}
|
className={twMerge(
|
||||||
|
"flex flex-row items-center dark:bg-amber-300/20 bg-amber-200 p-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{title && <b className="mr-2">{title}:</b>}
|
{title && <b className="mr-2">{title}:</b>}
|
||||||
{message}
|
{message || children}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
|
import { IconLockAccessOff } from "@tabler/icons-react";
|
||||||
import { Empty, type EmptyProps } from "./Empty";
|
import { Empty, type EmptyProps } from "./Empty";
|
||||||
|
|
||||||
const NotFound = (props: Partial<EmptyProps>) => <Empty title="Not Found" {...props} />;
|
const NotFound = (props: Partial<EmptyProps>) => <Empty title="Not Found" {...props} />;
|
||||||
const NotAllowed = (props: Partial<EmptyProps>) => <Empty title="Not Allowed" {...props} />;
|
const NotAllowed = (props: Partial<EmptyProps>) => <Empty title="Not Allowed" {...props} />;
|
||||||
|
const MissingPermission = ({
|
||||||
|
what,
|
||||||
|
...props
|
||||||
|
}: Partial<EmptyProps> & {
|
||||||
|
what?: string;
|
||||||
|
}) => (
|
||||||
|
<Empty
|
||||||
|
Icon={IconLockAccessOff}
|
||||||
|
title="Missing Permission"
|
||||||
|
description={`You're not allowed to access ${what ?? "this"}.`}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
export const Message = {
|
export const Message = {
|
||||||
NotFound,
|
NotFound,
|
||||||
NotAllowed
|
NotAllowed,
|
||||||
|
MissingPermission
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { notifications } from "@mantine/notifications";
|
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { TbDots } from "react-icons/tb";
|
import { TbDots } from "react-icons/tb";
|
||||||
import { useBknd } from "ui/client/bknd";
|
import { useBknd } from "ui/client/bknd";
|
||||||
import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth";
|
import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth";
|
||||||
import { Button } from "ui/components/buttons/Button";
|
import { Button } from "ui/components/buttons/Button";
|
||||||
import { IconButton } from "ui/components/buttons/IconButton";
|
import { IconButton } from "ui/components/buttons/IconButton";
|
||||||
|
import { Message } from "ui/components/display/Message";
|
||||||
import { Dropdown } from "ui/components/overlay/Dropdown";
|
import { Dropdown } from "ui/components/overlay/Dropdown";
|
||||||
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
||||||
import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2";
|
import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2";
|
||||||
@@ -12,7 +12,11 @@ import { routes, useNavigate } from "ui/lib/routes";
|
|||||||
import { AuthRoleForm, type AuthRoleFormRef } from "ui/routes/auth/forms/role.form";
|
import { AuthRoleForm, type AuthRoleFormRef } from "ui/routes/auth/forms/role.form";
|
||||||
|
|
||||||
export function AuthRolesEdit(props) {
|
export function AuthRolesEdit(props) {
|
||||||
useBknd({ withSecrets: true });
|
const { hasSecrets } = useBknd({ withSecrets: true });
|
||||||
|
if (!hasSecrets) {
|
||||||
|
return <Message.MissingPermission what="Roles & Permissions" />;
|
||||||
|
}
|
||||||
|
|
||||||
return <AuthRolesEditInternal {...props} />;
|
return <AuthRolesEditInternal {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ const EntityContextMenu = ({
|
|||||||
items={[
|
items={[
|
||||||
href && {
|
href && {
|
||||||
icon: IconExternalLink,
|
icon: IconExternalLink,
|
||||||
label: "Open in new tab",
|
label: "Open in tab",
|
||||||
onClick: () => navigate(href, { target: "_blank" })
|
onClick: () => navigate(href, { target: "_blank" })
|
||||||
},
|
},
|
||||||
separator,
|
separator,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { IconSettings } from "@tabler/icons-react";
|
|||||||
import { ucFirst } from "core/utils";
|
import { ucFirst } from "core/utils";
|
||||||
import { useBknd } from "ui/client/bknd";
|
import { useBknd } from "ui/client/bknd";
|
||||||
import { Empty } from "ui/components/display/Empty";
|
import { Empty } from "ui/components/display/Empty";
|
||||||
|
import { Message } from "ui/components/display/Message";
|
||||||
import { Link } from "ui/components/wouter/Link";
|
import { Link } from "ui/components/wouter/Link";
|
||||||
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
||||||
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
||||||
@@ -44,7 +45,9 @@ function SettingsSidebar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SettingsRoutes() {
|
export default function SettingsRoutes() {
|
||||||
useBknd({ withSecrets: true });
|
const b = useBknd({ withSecrets: true });
|
||||||
|
if (!b.hasSecrets) return <Message.MissingPermission what="the settings" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsSidebar />
|
<SettingsSidebar />
|
||||||
|
|||||||
Reference in New Issue
Block a user