deps: update webui deps

This commit is contained in:
master 2025-04-25 02:21:20 +08:00
parent eb8f0be004
commit b20f7cd1ad
11 changed files with 2644 additions and 2263 deletions

View File

@ -9,7 +9,8 @@
"preview": "rsbuild preview" "preview": "rsbuild preview"
}, },
"dependencies": { "dependencies": {
"@abraham/reflection": "^0.12.0", "@abraham/reflection": "^0.13.0",
"@apollo/client": "^3.13.8",
"@codemirror/language": "6.0.0", "@codemirror/language": "6.0.0",
"@corvu/drawer": "^0.2.3", "@corvu/drawer": "^0.2.3",
"@corvu/otp-field": "^0.1.4", "@corvu/otp-field": "^0.1.4",
@ -53,18 +54,19 @@
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.6.0",
"graphiql": "^3.8.3", "graphiql": "^4.0.2",
"graphql": "^16.11.0",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"jotai": "^2.12.3", "jotai": "^2.12.3",
"jotai-signal": "^0.9.0", "jotai-signal": "^0.9.0",
"lucide-react": "^0.503.0", "lucide-react": "^0.508.0",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"oidc-client-rx": "0.1.0-alpha.9", "oidc-client-rx": "0.1.0-alpha.9",
"react": "^19.1.0", "react": "^19.1.0",
"react-day-picker": "8.10.1", "react-day-picker": "9.6.7",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-hook-form": "^7.56.0", "react-hook-form": "^7.56.0",
"react-resizable-panels": "^2.1.7", "react-resizable-panels": "^3.0.0",
"recharts": "^2.15.3", "recharts": "^2.15.3",
"rxjs": "^7.8.2", "rxjs": "^7.8.2",
"sonner": "^2.0.3", "sonner": "^2.0.3",
@ -72,8 +74,7 @@
"tailwindcss": "^4.0.6", "tailwindcss": "^4.0.6",
"tw-animate-css": "^1.2.7", "tw-animate-css": "^1.2.7",
"type-fest": "^4.40.0", "type-fest": "^4.40.0",
"vaul": "^1.1.2", "vaul": "^1.1.2"
"zod": "^3.24.3"
}, },
"devDependencies": { "devDependencies": {
"@rsbuild/core": "^1.2.15", "@rsbuild/core": "^1.2.15",

View File

@ -1,83 +1,61 @@
import { UnreachableError } from '@/infra/errors/common'; import { AuthService } from '@/domains/auth/auth.service';
import type { Injector, Provider } from '@outposts/injection-js'; import type { Injector, Provider } from '@outposts/injection-js';
import type { AnyRouter } from '@tanstack/react-router'; import type { AnyRouter } from '@tanstack/react-router';
import { import {
CHECK_AUTH_RESULT_EVENT,
type CheckAuthResultEventType, type CheckAuthResultEventType,
OidcSecurityService,
provideAuth as provideOidcAuth, provideAuth as provideOidcAuth,
withCheckAuthResultEvent, withCheckAuthResultEvent,
withDefaultFeatures, withDefaultFeatures,
} from 'oidc-client-rx'; } from 'oidc-client-rx';
import { withTanstackRouter } from 'oidc-client-rx/adapters/@tanstack/react-router'; import { withTanstackRouter } from 'oidc-client-rx/adapters/@tanstack/react-router';
import { NEVER, type Observable, map, of } from 'rxjs'; import type { Observable } from 'rxjs';
import { AppAuthMethod, AuthMethodEnum, buildOidcConfig } from './config'; import {
AppAuthMethod,
AuthMethodEnum,
type AuthMethodType,
buildOidcConfig,
} from './config';
export function provideAuth(router: AnyRouter): Provider[] { export function provideAuth(router: AnyRouter): Provider[] {
const providers: Provider[] = [AuthService];
if (AppAuthMethod === AuthMethodEnum.OIDC) { if (AppAuthMethod === AuthMethodEnum.OIDC) {
return provideOidcAuth( providers.push(
{ ...provideOidcAuth(
config: buildOidcConfig(), {
}, config: buildOidcConfig(),
withDefaultFeatures({ },
router: { enabled: false }, withDefaultFeatures({
securityStorage: { type: 'local-storage' }, router: { enabled: false },
}), securityStorage: { type: 'local-storage' },
withTanstackRouter(router), }),
withCheckAuthResultEvent() withTanstackRouter(router),
withCheckAuthResultEvent()
)
); );
} }
return []; return providers;
} }
export function setupAuthContext(injector: Injector) { export function setupAuthContext(injector: Injector) {
if (AppAuthMethod === AuthMethodEnum.OIDC) { const { authService } = authContextFromInjector(injector);
const oidcSecurityService = injector.get(OidcSecurityService); authService.setup();
oidcSecurityService.checkAuth().subscribe();
}
} }
export interface OidcAuthContext {
type: typeof AuthMethodEnum.OIDC; export interface AuthContext {
oidcSecurityService: OidcSecurityService; type: AuthMethodType;
authService: AuthService;
isAuthenticated$: Observable<boolean>; isAuthenticated$: Observable<boolean>;
userData$: Observable<{}>; userData$: Observable<{}>;
checkAuthResultEvent$: Observable<CheckAuthResultEventType>; checkAuthResultEvent$: Observable<CheckAuthResultEventType>;
} }
export interface BasicAuthContext {
type: typeof AuthMethodEnum.BASIC;
isAuthenticated$: Observable<true>;
userData$: Observable<{}>;
checkAuthResultEvent$: Observable<CheckAuthResultEventType>;
}
export type AuthContext = OidcAuthContext | BasicAuthContext;
const BASIC_AUTH_IS_AUTHENTICATED$ = of(true) as Observable<true>;
const BASIC_AUTH_USER_DATA$ = of({});
export function authContextFromInjector(injector: Injector): AuthContext { export function authContextFromInjector(injector: Injector): AuthContext {
if (AppAuthMethod === AuthMethodEnum.OIDC) { const authService = injector.get(AuthService);
const oidcSecurityService = injector.get(OidcSecurityService)!; return {
type: AppAuthMethod,
return { authService,
type: AuthMethodEnum.OIDC, isAuthenticated$: authService.isAuthenticated$,
oidcSecurityService: injector.get(OidcSecurityService), userData$: authService.userData$,
isAuthenticated$: oidcSecurityService.isAuthenticated$.pipe( checkAuthResultEvent$: authService.checkAuthResultEvent$,
map((s) => s.isAuthenticated) };
),
userData$: oidcSecurityService.userData$.pipe(map((s) => s.userData)),
checkAuthResultEvent$: injector.get(CHECK_AUTH_RESULT_EVENT),
};
}
if (AppAuthMethod === AuthMethodEnum.BASIC) {
return {
type: AuthMethodEnum.BASIC,
isAuthenticated$: BASIC_AUTH_IS_AUTHENTICATED$,
userData$: BASIC_AUTH_USER_DATA$,
checkAuthResultEvent$: NEVER,
};
}
throw new UnreachableError('Invalid auth method');
} }

View File

@ -0,0 +1,53 @@
import { AppAuthMethod, AuthMethodEnum } from '@/app/auth/config';
import { injectInjector } from '@/infra/di/inject';
import { Injectable, type Injector } from '@outposts/injection-js';
import {
CHECK_AUTH_RESULT_EVENT,
type CheckAuthResultEventType,
OidcSecurityService,
} from 'oidc-client-rx';
import { NEVER, type Observable, map, of } from 'rxjs';
const BASIC_AUTH_IS_AUTHENTICATED$ = of(true) as Observable<true>;
const BASIC_AUTH_USER_DATA$ = of({});
@Injectable()
export class AuthService {
private injector: Injector = injectInjector();
oidcSecurityService: OidcSecurityService | undefined;
checkAuthResultEvent$: Observable<CheckAuthResultEventType>;
constructor() {
if (AppAuthMethod === 'oidc') {
this.oidcSecurityService = this.injector.get(OidcSecurityService);
this.checkAuthResultEvent$ = this.injector.get(CHECK_AUTH_RESULT_EVENT);
} else {
this.checkAuthResultEvent$ = NEVER;
}
}
setup() {
if (AppAuthMethod === AuthMethodEnum.OIDC) {
this.oidcSecurityService!.checkAuth().subscribe();
}
}
get isAuthenticated$() {
return (
this.oidcSecurityService?.isAuthenticated$.pipe(
map((s) => s.isAuthenticated)
) ?? BASIC_AUTH_IS_AUTHENTICATED$
);
}
get userData$() {
return (
this.oidcSecurityService?.userData$?.pipe(map((s) => s.userData)) ??
BASIC_AUTH_USER_DATA$
);
}
getAccessToken(): Observable<string | undefined> {
return this.oidcSecurityService?.getAccessToken() ?? of(undefined);
}
}

View File

@ -0,0 +1,5 @@
import { type InjectionToken, Injector, inject } from '@outposts/injection-js';
export function injectInjector(): Injector {
return inject(Injector as any as InjectionToken<Injector>);
}

View File

@ -1,19 +1,21 @@
import * as React from "react" import React from 'react';
const MOBILE_BREAKPOINT = 768 const MOBILE_BREAKPOINT = 768;
export function useIsMobile() { export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined) const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined
);
React.useEffect(() => { React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => { const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
} };
mql.addEventListener("change", onChange) mql.addEventListener('change', onChange);
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
return () => mql.removeEventListener("change", onChange) return () => mql.removeEventListener('change', onChange);
}, []) }, []);
return !!isMobile return !!isMobile;
} }

View File

@ -0,0 +1,7 @@
import { Injectable, inject } from '@outposts/injection-js';
import { OidcSecurityService } from 'oidc-client-rx';
@Injectable()
export class HttpService {
authService = inject(OidcSecurityService);
}

View File

@ -48,16 +48,16 @@ const rootElement = document.getElementById('root');
const App = () => { const App = () => {
return ( return (
<Suspense> <InjectorProvider injector={injector}>
<InjectorProvider injector={injector}> <Suspense>
<RouterProvider <RouterProvider
router={router} router={router}
context={{ context={{
injector, injector,
}} }}
/> />
</InjectorProvider> </Suspense>
</Suspense> </InjectorProvider>
); );
}; };

View File

@ -1,10 +1,9 @@
import { useAuth } from '@/app/auth/hooks'; import { useAuth } from '@/app/auth/hooks';
import { type Fetcher, createGraphiQLFetcher } from '@graphiql/toolkit'; import { type Fetcher, createGraphiQLFetcher } from '@graphiql/toolkit';
import { createLazyFileRoute } from '@tanstack/react-router'; import { createLazyFileRoute } from '@tanstack/react-router';
import GraphiQL from 'graphiql'; import { GraphiQL } from 'graphiql';
import { useCallback } from 'react'; import { useCallback } from 'react';
import 'graphiql/graphiql.css'; import 'graphiql/graphiql.css';
import { AuthMethodEnum } from '@/app/auth/config';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
export const Route = createLazyFileRoute('/_app/playground/graphql-api')({ export const Route = createLazyFileRoute('/_app/playground/graphql-api')({
@ -16,12 +15,9 @@ function PlaygroundGraphQLApiRouteComponent() {
const fetcher: Fetcher = useCallback( const fetcher: Fetcher = useCallback(
async (props) => { async (props) => {
const accessToken = const accessToken = await firstValueFrom(
authContext.type === AuthMethodEnum.OIDC authContext.authService.getAccessToken()
? await firstValueFrom( );
authContext.oidcSecurityService.getAccessToken()
)
: undefined;
return createGraphiQLFetcher({ return createGraphiQLFetcher({
url: '/api/graphql', url: '/api/graphql',
headers: accessToken headers: accessToken
@ -31,11 +27,7 @@ function PlaygroundGraphQLApiRouteComponent() {
: undefined, : undefined,
})(props); })(props);
}, },
[ [authContext.authService.getAccessToken]
authContext.type,
// @ts-ignore
authContext?.oidcSecurityService?.getAccessToken,
]
); );
return ( return (

View File

@ -7,6 +7,7 @@
"linter": { "linter": {
"rules": { "rules": {
"style": { "style": {
"noParameterProperties": "off",
"noNonNullAssertion": "off" "noNonNullAssertion": "off"
}, },
"security": { "security": {

View File

@ -14,7 +14,7 @@
"bump-deps": "npx --yes npm-check-updates --deep -u -x react-day-picker && pnpm install", "bump-deps": "npx --yes npm-check-updates --deep -u -x react-day-picker && pnpm install",
"clean": "git clean -xdf node_modules" "clean": "git clean -xdf node_modules"
}, },
"packageManager": "pnpm@10.6.1", "packageManager": "pnpm@10.10.0",
"engines": { "engines": {
"node": ">=22" "node": ">=22"
}, },
@ -22,23 +22,9 @@
"@auto-it/all-contributors": "^11.3.0", "@auto-it/all-contributors": "^11.3.0",
"@auto-it/first-time-contributor": "^11.3.0", "@auto-it/first-time-contributor": "^11.3.0",
"@biomejs/biome": "1.9.4", "@biomejs/biome": "1.9.4",
"@types/node": "^22.13.8", "@types/node": "^22.15.16",
"tsx": "^4.19.2", "tsx": "^4.19.4",
"typescript": "^5.8.2", "typescript": "^5.8.3",
"ultracite": "^4.1.15" "ultracite": "^4.2.4"
},
"overrides": {
"@headlessui/react": "^2.2.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"date-fns": "^4.1.0"
},
"pnpm": {
"overrides": {
"@headlessui/react": "^2.2.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"date-fns": "^4.1.0"
}
} }
} }

4658
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff