diff --git a/app/package.json b/app/package.json index 94b6912..c618d9c 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "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.", "homepage": "https://bknd.io", "repository": { diff --git a/app/src/App.ts b/app/src/App.ts index 322554d..b98fc67 100644 --- a/app/src/App.ts +++ b/app/src/App.ts @@ -118,8 +118,6 @@ export class App { this.trigger_first_boot = false; await this.emgr.emit(new AppFirstBoot({ app: this })); } - - console.log("[APP] built"); } mutateConfig(module: Module) { diff --git a/app/src/data/entities/EntityManager.ts b/app/src/data/entities/EntityManager.ts index 31401b3..6018029 100644 --- a/app/src/data/entities/EntityManager.ts +++ b/app/src/data/entities/EntityManager.ts @@ -111,15 +111,18 @@ export class EntityManager { // 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( + e: Entity | keyof TBD | string, + silent?: Silent + ): Silent extends true ? Entity | undefined : Entity { // make sure to always retrieve by name const entity = this.entities.find((entity) => e instanceof Entity ? entity.name === e.name : entity.name === e ); - if (!entity && !silent) { - // @ts-ignore - throw new EntityNotDefinedException(e instanceof Entity ? e.name : e); + if (!entity) { + if (silent === true) return undefined as any; + throw new EntityNotDefinedException(e instanceof Entity ? e.name : (e as string)); } return entity; diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index cc32221..1dc51e6 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -1,7 +1,10 @@ +import { IconAlertHexagon } from "@tabler/icons-react"; import type { ModuleConfigs, ModuleSchemas } from "modules"; import { getDefaultConfig, getDefaultSchema } from "modules/ModuleManager"; import { createContext, startTransition, useContext, useEffect, useRef, useState } from "react"; 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 { AppReduced } from "./utils/AppReduced"; @@ -10,6 +13,7 @@ type BkndContext = { schema: ModuleSchemas; config: ModuleConfigs; permissions: string[]; + hasSecrets: boolean; requireSecrets: () => Promise; actions: ReturnType; app: AppReduced; @@ -32,6 +36,7 @@ export function BkndProvider({ const [schema, setSchema] = useState>(); const [fetched, setFetched] = useState(false); + const [error, setError] = useState(); const errorShown = useRef(); const [local_version, set_local_version] = useState(0); const api = useApi(); @@ -50,15 +55,11 @@ export function BkndProvider({ if (!res.ok) { if (errorShown.current) return; errorShown.current = true; - /*notifications.show({ - title: "Failed to fetch schema", - // @ts-ignore - message: body.error, - color: "red", - position: "top-right", - autoClose: false, - withCloseButton: true - });*/ + + setError(true); + return; + } else if (error) { + setError(false); } const schema = res.ok @@ -98,12 +99,24 @@ export function BkndProvider({ if (!fetched || !schema) return fallback; const app = new AppReduced(schema?.config as any); const actions = getSchemaActions({ api, setSchema, reloadSchema }); + const hasSecrets = withSecrets && !error; return ( + {error && ( + + + You attempted to load system configuration with secrets without having proper + permission. + + + + + )} + {children} ); diff --git a/app/src/ui/components/display/Alert.tsx b/app/src/ui/components/display/Alert.tsx index ba3c4cd..ee5aad1 100644 --- a/app/src/ui/components/display/Alert.tsx +++ b/app/src/ui/components/display/Alert.tsx @@ -6,16 +6,27 @@ export type AlertProps = ComponentPropsWithoutRef<"div"> & { visible?: boolean; title?: string; message?: ReactNode | string; + children?: ReactNode; }; -const Base: React.FC = ({ visible = true, title, message, className, ...props }) => +const Base: React.FC = ({ + visible = true, + title, + message, + className, + children, + ...props +}) => visible ? (
{title && {title}:} - {message} + {message || children}
) : null; diff --git a/app/src/ui/components/display/Message.tsx b/app/src/ui/components/display/Message.tsx index da44346..bc262d2 100644 --- a/app/src/ui/components/display/Message.tsx +++ b/app/src/ui/components/display/Message.tsx @@ -1,9 +1,24 @@ +import { IconLockAccessOff } from "@tabler/icons-react"; import { Empty, type EmptyProps } from "./Empty"; const NotFound = (props: Partial) => ; const NotAllowed = (props: Partial) => ; +const MissingPermission = ({ + what, + ...props +}: Partial & { + what?: string; +}) => ( + +); export const Message = { NotFound, - NotAllowed + NotAllowed, + MissingPermission }; diff --git a/app/src/ui/routes/auth/auth.roles.edit.$role.tsx b/app/src/ui/routes/auth/auth.roles.edit.$role.tsx index cb7de81..738e568 100644 --- a/app/src/ui/routes/auth/auth.roles.edit.$role.tsx +++ b/app/src/ui/routes/auth/auth.roles.edit.$role.tsx @@ -1,10 +1,10 @@ -import { notifications } from "@mantine/notifications"; import { useRef } from "react"; import { TbDots } from "react-icons/tb"; import { useBknd } from "ui/client/bknd"; import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; +import { Message } from "ui/components/display/Message"; import { Dropdown } from "ui/components/overlay/Dropdown"; import * as AppShell from "ui/layouts/AppShell/AppShell"; 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"; export function AuthRolesEdit(props) { - useBknd({ withSecrets: true }); + const { hasSecrets } = useBknd({ withSecrets: true }); + if (!hasSecrets) { + return ; + } + return ; } diff --git a/app/src/ui/routes/data/_data.root.tsx b/app/src/ui/routes/data/_data.root.tsx index ad02097..543816c 100644 --- a/app/src/ui/routes/data/_data.root.tsx +++ b/app/src/ui/routes/data/_data.root.tsx @@ -168,7 +168,7 @@ const EntityContextMenu = ({ items={[ href && { icon: IconExternalLink, - label: "Open in new tab", + label: "Open in tab", onClick: () => navigate(href, { target: "_blank" }) }, separator, diff --git a/app/src/ui/routes/settings/index.tsx b/app/src/ui/routes/settings/index.tsx index 71c5754..026005b 100644 --- a/app/src/ui/routes/settings/index.tsx +++ b/app/src/ui/routes/settings/index.tsx @@ -2,6 +2,7 @@ import { IconSettings } from "@tabler/icons-react"; import { ucFirst } from "core/utils"; import { useBknd } from "ui/client/bknd"; import { Empty } from "ui/components/display/Empty"; +import { Message } from "ui/components/display/Message"; import { Link } from "ui/components/wouter/Link"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import * as AppShell from "ui/layouts/AppShell/AppShell"; @@ -44,7 +45,9 @@ function SettingsSidebar() { } export default function SettingsRoutes() { - useBknd({ withSecrets: true }); + const b = useBknd({ withSecrets: true }); + if (!b.hasSecrets) return ; + return ( <>