api: added custom storage option (#174)

This commit is contained in:
dswbx
2025-05-27 13:09:24 +02:00
committed by GitHub
parent db795ec050
commit 17ab35e245
10 changed files with 159 additions and 90 deletions

View File

@@ -1,52 +1,64 @@
import { Api, type ApiOptions, type TApiUser } from "Api";
import { Api, type ApiOptions, type AuthState } from "Api";
import { isDebug } from "core";
import { createContext, type ReactNode, useContext } from "react";
import { createContext, type ReactNode, useContext, useMemo, useState } from "react";
import type { AdminBkndWindowContext } from "modules/server/AdminController";
const ClientContext = createContext<{ baseUrl: string; api: Api }>({
baseUrl: undefined,
} as any);
export type BkndClientContext = {
baseUrl: string;
api: Api;
authState?: Partial<AuthState>;
};
const ClientContext = createContext<BkndClientContext>(undefined!);
export type ClientProviderProps = {
children?: ReactNode;
} & (
| { baseUrl?: string; user?: TApiUser | null | undefined }
| {
api: Api;
}
);
baseUrl?: string;
} & ApiOptions;
export const ClientProvider = ({ children, ...props }: ClientProviderProps) => {
let api: Api;
export const ClientProvider = ({
children,
host,
baseUrl: _baseUrl = host,
...props
}: ClientProviderProps) => {
const winCtx = useBkndWindowContext();
const _ctx = useClientContext();
let actualBaseUrl = _baseUrl ?? _ctx?.baseUrl ?? "";
let user: any = undefined;
if (props && "api" in props) {
api = props.api;
} else {
const winCtx = useBkndWindowContext();
const _ctx_baseUrl = useBaseUrl();
const { baseUrl, user } = props;
let actualBaseUrl = baseUrl ?? _ctx_baseUrl ?? "";
try {
if (!baseUrl) {
if (_ctx_baseUrl) {
actualBaseUrl = _ctx_baseUrl;
console.warn("wrapped many times, take from context", actualBaseUrl);
} else if (typeof window !== "undefined") {
actualBaseUrl = window.location.origin;
//console.log("setting from window", actualBaseUrl);
}
}
} catch (e) {
console.error("Error in ClientProvider", e);
}
//console.log("api init", { host: actualBaseUrl, user: user ?? winCtx.user });
api = new Api({ host: actualBaseUrl, user: user ?? winCtx.user, verbose: isDebug() });
if (winCtx) {
user = winCtx.user;
}
if (!actualBaseUrl) {
try {
actualBaseUrl = window.location.origin;
} catch (e) {}
}
const apiProps = { user, ...props, host: actualBaseUrl };
const api = useMemo(
() =>
new Api({
...apiProps,
verbose: isDebug(),
onAuthStateChange: (state) => {
props.onAuthStateChange?.(state);
if (!authState?.token || state.token !== authState?.token) {
setAuthState(state);
}
},
}),
[JSON.stringify(apiProps)],
);
const [authState, setAuthState] = useState<Partial<AuthState> | undefined>(
apiProps.user ? api.getAuthState() : undefined,
);
return (
<ClientContext.Provider value={{ baseUrl: api.baseUrl, api }}>
<ClientContext.Provider value={{ baseUrl: api.baseUrl, api, authState }}>
{children}
</ClientContext.Provider>
);
@@ -61,12 +73,16 @@ export const useApi = (host?: ApiOptions["host"]): Api => {
return context.api;
};
export const useClientContext = () => {
return useContext(ClientContext);
};
/**
* @deprecated use useApi().baseUrl instead
*/
export const useBaseUrl = () => {
const context = useContext(ClientContext);
return context.baseUrl;
const context = useClientContext();
return context?.baseUrl;
};
export function useBkndWindowContext(): AdminBkndWindowContext {

View File

@@ -1,7 +1,7 @@
import type { AuthState } from "Api";
import type { AuthResponse } from "auth";
import { useState } from "react";
import { useApi, useInvalidate } from "ui/client";
import { useClientContext } from "ui/client/ClientProvider";
type LoginData = {
email: string;
@@ -10,7 +10,7 @@ type LoginData = {
};
type UseAuth = {
data: AuthState | undefined;
data: Partial<AuthState> | undefined;
user: AuthState["user"] | undefined;
token: AuthState["token"] | undefined;
verified: boolean;
@@ -24,46 +24,36 @@ type UseAuth = {
export const useAuth = (options?: { baseUrl?: string }): UseAuth => {
const api = useApi(options?.baseUrl);
const invalidate = useInvalidate();
const authState = api.getAuthState();
const [authData, setAuthData] = useState<UseAuth["data"]>(authState);
const { authState } = useClientContext();
const verified = authState?.verified ?? false;
function updateAuthState() {
setAuthData(api.getAuthState());
}
async function login(input: LoginData) {
const res = await api.auth.loginWithPassword(input);
updateAuthState();
const res = await api.auth.login("password", input);
return res.data;
}
async function register(input: LoginData) {
const res = await api.auth.registerWithPassword(input);
updateAuthState();
const res = await api.auth.register("password", input);
return res.data;
}
function setToken(token: string) {
api.updateToken(token);
updateAuthState();
}
async function logout() {
await api.updateToken(undefined);
setAuthData(undefined);
api.updateToken(undefined);
invalidate();
}
async function verify() {
await api.verifyAuth();
updateAuthState();
}
return {
data: authData,
user: authData?.user,
token: authData?.token,
data: authState,
user: authState?.user,
token: authState?.token,
verified,
login,
register,

View File

@@ -1,4 +1,8 @@
import { default as CodeMirror, type ReactCodeMirrorProps } from "@uiw/react-codemirror";
import {
default as CodeMirror,
type ReactCodeMirrorProps,
EditorView,
} from "@uiw/react-codemirror";
import { json } from "@codemirror/lang-json";
import { html } from "@codemirror/lang-html";
import { useTheme } from "ui/client/use-theme";
@@ -43,7 +47,7 @@ export default function CodeEditor({
theme={theme === "dark" ? "dark" : "light"}
editable={editable}
basicSetup={_basicSetup}
extensions={extensions}
extensions={[...extensions, EditorView.lineWrapping]}
{...props}
/>
);

View File

@@ -19,6 +19,7 @@ export type DropdownItem =
onClick?: () => void;
destructive?: boolean;
disabled?: boolean;
title?: string;
[key: string]: any;
};
@@ -142,6 +143,7 @@ export function Dropdown({
item.destructive && "text-red-500 hover:bg-red-600 hover:text-white",
)}
onClick={onClick}
title={item.title}
>
{space_for_icon && (
<div className="size-[16px] text-left mr-1.5 opacity-80">

View File

@@ -171,7 +171,8 @@ function UserMenu() {
items.push({ label: "Login", onClick: handleLogin, icon: IconUser });
} else {
items.push({
label: `Logout ${auth.user.email}`,
label: "Logout",
title: `Logout ${auth.user.email}`,
onClick: handleLogout,
icon: IconKeyOff,
});

View File

@@ -49,6 +49,7 @@ input[type="date"]::-webkit-calendar-picker-indicator {
.cm-editor {
display: flex;
flex: 1;
max-width: 100%;
}
.animate-fade-in {