moved flash message, removed theme from admin controller

This commit is contained in:
dswbx
2025-03-04 11:21:33 +01:00
parent 9ecfcb3e05
commit e82b72275b
4 changed files with 71 additions and 74 deletions

View File

@@ -5,6 +5,7 @@ import { config, isDebug } from "core";
import { addFlashMessage } from "core/server/flash"; import { addFlashMessage } from "core/server/flash";
import { html } from "hono/html"; import { html } from "hono/html";
import { Fragment } from "hono/jsx"; import { Fragment } from "hono/jsx";
import { css, Style } from "hono/css";
import { Controller } from "modules/Controller"; import { Controller } from "modules/Controller";
import * as SystemPermissions from "modules/permissions"; import * as SystemPermissions from "modules/permissions";
import type { AppTheme } from "modules/server/AppServer"; import type { AppTheme } from "modules/server/AppServer";
@@ -23,7 +24,7 @@ export type AdminControllerOptions = {
export class AdminController extends Controller { export class AdminController extends Controller {
constructor( constructor(
private readonly app: App, private readonly app: App,
private _options: AdminControllerOptions = {} private _options: AdminControllerOptions = {},
) { ) {
super(); super();
} }
@@ -36,7 +37,7 @@ export class AdminController extends Controller {
return { return {
...this._options, ...this._options,
basepath: this._options.basepath ?? "/", basepath: this._options.basepath ?? "/",
assets_path: this._options.assets_path ?? config.server.assets_path assets_path: this._options.assets_path ?? config.server.assets_path,
}; };
} }
@@ -53,7 +54,7 @@ export class AdminController extends Controller {
const hono = this.create().use( const hono = this.create().use(
authMiddleware({ authMiddleware({
//skip: [/favicon\.ico$/] //skip: [/favicon\.ico$/]
}) }),
); );
const auth = this.app.module.auth; const auth = this.app.module.auth;
@@ -66,14 +67,14 @@ export class AdminController extends Controller {
success: configs.auth.cookie.pathSuccess ?? "/", success: configs.auth.cookie.pathSuccess ?? "/",
loggedOut: configs.auth.cookie.pathLoggedOut ?? "/", loggedOut: configs.auth.cookie.pathLoggedOut ?? "/",
login: "/auth/login", login: "/auth/login",
logout: "/auth/logout" logout: "/auth/logout",
}; };
hono.use("*", async (c, next) => { hono.use("*", async (c, next) => {
const obj = { const obj = {
user: c.get("auth")?.user, user: c.get("auth")?.user,
logout_route: this.withBasePath(authRoutes.logout), logout_route: this.withBasePath(authRoutes.logout),
color_scheme: configs.server.admin.color_scheme color_scheme: configs.server.admin.color_scheme,
}; };
const html = await this.getHtml(obj); const html = await this.getHtml(obj);
if (!html) { if (!html) {
@@ -97,11 +98,11 @@ export class AdminController extends Controller {
console.log("redirecting to success"); console.log("redirecting to success");
return c.redirect(authRoutes.success); return c.redirect(authRoutes.success);
} }
} },
}), }),
async (c) => { async (c) => {
return c.html(c.get("html")!); return c.html(c.get("html")!);
} },
); );
hono.get(authRoutes.logout, async (c) => { hono.get(authRoutes.logout, async (c) => {
@@ -119,16 +120,16 @@ export class AdminController extends Controller {
console.log("redirecting"); console.log("redirecting");
return c.redirect(authRoutes.login); return c.redirect(authRoutes.login);
} },
}), }),
permission(SystemPermissions.schemaRead, { permission(SystemPermissions.schemaRead, {
onDenied: async (c) => { onDenied: async (c) => {
addFlashMessage(c, "You not allowed to read the schema", "warning"); addFlashMessage(c, "You not allowed to read the schema", "warning");
} },
}), }),
async (c) => { async (c) => {
return c.html(c.get("html")!); return c.html(c.get("html")!);
} },
); );
return hono; return hono;
@@ -141,12 +142,12 @@ export class AdminController extends Controller {
if (this.options.html.includes(htmlBkndContextReplace)) { if (this.options.html.includes(htmlBkndContextReplace)) {
return this.options.html.replace( return this.options.html.replace(
htmlBkndContextReplace, htmlBkndContextReplace,
"<script>" + bknd_context + "</script>" "<script>" + bknd_context + "</script>",
); );
} }
console.warn( console.warn(
`Custom HTML needs to include '${htmlBkndContextReplace}' to inject BKND context` `Custom HTML needs to include '${htmlBkndContextReplace}' to inject BKND context`,
); );
return this.options.html as string; return this.options.html as string;
} }
@@ -160,27 +161,36 @@ export class AdminController extends Controller {
const assets = { const assets = {
js: "main.js", js: "main.js",
css: "styles.css" css: "styles.css",
}; };
if (isProd) { if (isProd) {
let manifest: any;
if (this.options.assets_path.startsWith("http")) {
manifest = await fetch(this.options.assets_path + "manifest.json", {
headers: {
Accept: "application/json",
},
}).then((res) => res.json());
} else {
// @ts-ignore // @ts-ignore
const manifest = await import("bknd/dist/manifest.json", { manifest = await import("bknd/dist/manifest.json", {
assert: { type: "json" } assert: { type: "json" },
}); }).then((res) => res.default);
// @todo: load all marked as entry (incl. css) }
assets.js = manifest.default["src/ui/main.tsx"].file;
assets.css = manifest.default["src/ui/main.tsx"].css[0] as any; // @todo: load all marked as entry (incl. css)
assets.js = manifest["src/ui/main.tsx"].file;
assets.css = manifest["src/ui/main.tsx"].css[0] as any;
} }
const theme = configs.server.admin.color_scheme ?? "light";
const favicon = isProd ? this.options.assets_path + "favicon.ico" : "/favicon.ico"; const favicon = isProd ? this.options.assets_path + "favicon.ico" : "/favicon.ico";
return ( return (
<Fragment> <Fragment>
{/* dnd complains otherwise */} {/* dnd complains otherwise */}
{html`<!DOCTYPE html>`} {html`<!DOCTYPE html>`}
<html lang="en" class={theme}> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta <meta
@@ -197,16 +207,8 @@ export class AdminController extends Controller {
)} )}
{isProd ? ( {isProd ? (
<Fragment> <Fragment>
<script <script type="module" src={this.options.assets_path + assets?.js} />
type="module" <link rel="stylesheet" href={this.options.assets_path + assets?.css} />
CrossOrigin
src={this.options.assets_path + assets?.js}
/>
<link
rel="stylesheet"
crossOrigin
href={this.options.assets_path + assets?.css}
/>
</Fragment> </Fragment>
) : ( ) : (
<Fragment> <Fragment>
@@ -217,7 +219,7 @@ export class AdminController extends Controller {
RefreshRuntime.injectIntoGlobalHook(window) RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {} window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true` window.__vite_plugin_react_preamble_installed__ = true`,
}} }}
/> />
<script type="module" src={"/@vite/client"} /> <script type="module" src={"/@vite/client"} />
@@ -227,15 +229,14 @@ export class AdminController extends Controller {
</head> </head>
<body> <body>
<div id="root"> <div id="root">
<div id="loading" style={style(theme)}> <Style />
<span style={{ opacity: 0.3, fontSize: 14, fontFamily: "monospace" }}> <div id="loading" className={wrapperStyle}>
Initializing... <span className={loaderStyle}>Initializing...</span>
</span>
</div> </div>
</div> </div>
<script <script
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: bknd_context __html: bknd_context,
}} }}
/> />
{!isProd && <script type="module" src={mainPath} />} {!isProd && <script type="module" src={mainPath} />}
@@ -246,31 +247,27 @@ export class AdminController extends Controller {
} }
} }
const style = (theme: AppTheme) => { const wrapperStyle = css`
const base = { margin: 0;
margin: 0, padding: 0;
padding: 0, height: 100vh;
height: "100vh", width: 100vw;
width: "100vw", display: flex;
display: "flex", justify-content: center;
justifyContent: "center", align-items: center;
alignItems: "center", -webkit-font-smoothing: antialiased;
"-webkit-font-smoothing": "antialiased", -moz-osx-font-smoothing: grayscale;
"-moz-osx-font-smoothing": "grayscale" color: rgb(9,9,11);
}; background-color: rgb(250,250,250);
const styles = {
light: {
color: "rgb(9,9,11)",
backgroundColor: "rgb(250,250,250)"
},
dark: {
color: "rgb(250,250,250)",
backgroundColor: "rgb(30,31,34)"
}
};
return { @media (prefers-color-scheme: dark) {
...base, color: rgb(250,250,250);
...styles[theme === "light" ? "light" : "dark"] background-color: rgb(30,31,34);
}; }
}; `;
const loaderStyle = css`
opacity: 0.3;
font-size: 14px;
font-family: monospace;
`;

View File

@@ -2,11 +2,10 @@ import { MantineProvider } from "@mantine/core";
import { Notifications } from "@mantine/notifications"; import { Notifications } from "@mantine/notifications";
import type { ModuleConfigs } from "modules"; import type { ModuleConfigs } from "modules";
import React from "react"; import React from "react";
import { BkndProvider, useBknd } from "ui/client/bknd"; import { BkndProvider } from "ui/client/bknd";
import { useTheme } from "ui/client/use-theme"; import { useTheme } from "ui/client/use-theme";
import { Logo } from "ui/components/display/Logo"; import { Logo } from "ui/components/display/Logo";
import * as AppShell from "ui/layouts/AppShell/AppShell"; import * as AppShell from "ui/layouts/AppShell/AppShell";
import { FlashMessage } from "ui/modules/server/FlashMessage";
import { ClientProvider, type ClientProviderProps } from "./client"; import { ClientProvider, type ClientProviderProps } from "./client";
import { createMantineTheme } from "./lib/mantine/theme"; import { createMantineTheme } from "./lib/mantine/theme";
import { BkndModalsProvider } from "./modals"; import { BkndModalsProvider } from "./modals";
@@ -21,7 +20,7 @@ export type BkndAdminProps = {
export default function Admin({ export default function Admin({
baseUrl: baseUrlOverride, baseUrl: baseUrlOverride,
withProvider = false, withProvider = false,
config config,
}: BkndAdminProps) { }: BkndAdminProps) {
const Component = ( const Component = (
<BkndProvider adminOverride={config} fallback={<Skeleton theme={config?.color_scheme} />}> <BkndProvider adminOverride={config} fallback={<Skeleton theme={config?.color_scheme} />}>
@@ -45,8 +44,7 @@ function AdminInternal() {
return ( return (
<MantineProvider {...createMantineTheme(theme as any)}> <MantineProvider {...createMantineTheme(theme as any)}>
<Notifications /> <Notifications position="top-right" />
<FlashMessage />
<BkndModalsProvider> <BkndModalsProvider>
<Routes /> <Routes />
</BkndModalsProvider> </BkndModalsProvider>
@@ -54,9 +52,9 @@ function AdminInternal() {
); );
} }
const Skeleton = ({ theme }: { theme?: string }) => { const Skeleton = ({ theme }: { theme?: any }) => {
const actualTheme = const t = useTheme();
(theme ?? document.querySelector("html")?.classList.contains("light")) ? "light" : "dark"; const actualTheme = theme ?? t.theme;
return ( return (
<div id="bknd-admin" className={actualTheme + " antialiased"}> <div id="bknd-admin" className={actualTheme + " antialiased"}>

View File

@@ -7,7 +7,7 @@ import { Alert } from "ui/components/display/Alert";
* @constructor * @constructor
*/ */
export function FlashMessage() { export function FlashMessage() {
const [flash, setFlash] = useState<any>(); const [flash, setFlash] = useState<ReturnType<typeof getFlashMessage>>();
useEffect(() => { useEffect(() => {
if (!flash) { if (!flash) {

View File

@@ -1,4 +1,4 @@
import { Suspense, lazy } from "react"; import React, { Suspense, lazy } from "react";
import { useBknd } from "ui/client/bknd"; import { useBknd } from "ui/client/bknd";
import { useTheme } from "ui/client/use-theme"; import { useTheme } from "ui/client/use-theme";
import { Route, Router, Switch } from "wouter"; import { Route, Router, Switch } from "wouter";
@@ -9,6 +9,7 @@ import FlowRoutes from "./flows";
import MediaRoutes from "./media"; import MediaRoutes from "./media";
import { Root, RootEmpty } from "./root"; import { Root, RootEmpty } from "./root";
import SettingsRoutes from "./settings"; import SettingsRoutes from "./settings";
import { FlashMessage } from "ui/modules/server/FlashMessage";
// @ts-ignore // @ts-ignore
const TestRoutes = lazy(() => import("./test")); const TestRoutes = lazy(() => import("./test"));
@@ -20,6 +21,7 @@ export function Routes() {
return ( return (
<div id="bknd-admin" className={theme + " antialiased"}> <div id="bknd-admin" className={theme + " antialiased"}>
<FlashMessage />
<Router base={basepath}> <Router base={basepath}>
<Switch> <Switch>
<Route path="/auth/login" component={AuthLogin} /> <Route path="/auth/login" component={AuthLogin} />