deps: update webui deps
This commit is contained in:
parent
eb8f0be004
commit
b20f7cd1ad
@ -9,7 +9,8 @@
|
||||
"preview": "rsbuild preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@abraham/reflection": "^0.12.0",
|
||||
"@abraham/reflection": "^0.13.0",
|
||||
"@apollo/client": "^3.13.8",
|
||||
"@codemirror/language": "6.0.0",
|
||||
"@corvu/drawer": "^0.2.3",
|
||||
"@corvu/otp-field": "^0.1.4",
|
||||
@ -53,18 +54,19 @@
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"graphiql": "^3.8.3",
|
||||
"graphiql": "^4.0.2",
|
||||
"graphql": "^16.11.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"jotai": "^2.12.3",
|
||||
"jotai-signal": "^0.9.0",
|
||||
"lucide-react": "^0.503.0",
|
||||
"lucide-react": "^0.508.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"oidc-client-rx": "0.1.0-alpha.9",
|
||||
"react": "^19.1.0",
|
||||
"react-day-picker": "8.10.1",
|
||||
"react-day-picker": "9.6.7",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.56.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-resizable-panels": "^3.0.0",
|
||||
"recharts": "^2.15.3",
|
||||
"rxjs": "^7.8.2",
|
||||
"sonner": "^2.0.3",
|
||||
@ -72,8 +74,7 @@
|
||||
"tailwindcss": "^4.0.6",
|
||||
"tw-animate-css": "^1.2.7",
|
||||
"type-fest": "^4.40.0",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.24.3"
|
||||
"vaul": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rsbuild/core": "^1.2.15",
|
||||
|
@ -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 { AnyRouter } from '@tanstack/react-router';
|
||||
import {
|
||||
CHECK_AUTH_RESULT_EVENT,
|
||||
type CheckAuthResultEventType,
|
||||
OidcSecurityService,
|
||||
provideAuth as provideOidcAuth,
|
||||
withCheckAuthResultEvent,
|
||||
withDefaultFeatures,
|
||||
} from 'oidc-client-rx';
|
||||
import { withTanstackRouter } from 'oidc-client-rx/adapters/@tanstack/react-router';
|
||||
import { NEVER, type Observable, map, of } from 'rxjs';
|
||||
import { AppAuthMethod, AuthMethodEnum, buildOidcConfig } from './config';
|
||||
import type { Observable } from 'rxjs';
|
||||
import {
|
||||
AppAuthMethod,
|
||||
AuthMethodEnum,
|
||||
type AuthMethodType,
|
||||
buildOidcConfig,
|
||||
} from './config';
|
||||
|
||||
export function provideAuth(router: AnyRouter): Provider[] {
|
||||
const providers: Provider[] = [AuthService];
|
||||
if (AppAuthMethod === AuthMethodEnum.OIDC) {
|
||||
return provideOidcAuth(
|
||||
{
|
||||
config: buildOidcConfig(),
|
||||
},
|
||||
withDefaultFeatures({
|
||||
router: { enabled: false },
|
||||
securityStorage: { type: 'local-storage' },
|
||||
}),
|
||||
withTanstackRouter(router),
|
||||
withCheckAuthResultEvent()
|
||||
providers.push(
|
||||
...provideOidcAuth(
|
||||
{
|
||||
config: buildOidcConfig(),
|
||||
},
|
||||
withDefaultFeatures({
|
||||
router: { enabled: false },
|
||||
securityStorage: { type: 'local-storage' },
|
||||
}),
|
||||
withTanstackRouter(router),
|
||||
withCheckAuthResultEvent()
|
||||
)
|
||||
);
|
||||
}
|
||||
return [];
|
||||
return providers;
|
||||
}
|
||||
|
||||
export function setupAuthContext(injector: Injector) {
|
||||
if (AppAuthMethod === AuthMethodEnum.OIDC) {
|
||||
const oidcSecurityService = injector.get(OidcSecurityService);
|
||||
oidcSecurityService.checkAuth().subscribe();
|
||||
}
|
||||
const { authService } = authContextFromInjector(injector);
|
||||
authService.setup();
|
||||
}
|
||||
export interface OidcAuthContext {
|
||||
type: typeof AuthMethodEnum.OIDC;
|
||||
oidcSecurityService: OidcSecurityService;
|
||||
|
||||
export interface AuthContext {
|
||||
type: AuthMethodType;
|
||||
authService: AuthService;
|
||||
isAuthenticated$: Observable<boolean>;
|
||||
userData$: Observable<{}>;
|
||||
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 {
|
||||
if (AppAuthMethod === AuthMethodEnum.OIDC) {
|
||||
const oidcSecurityService = injector.get(OidcSecurityService)!;
|
||||
|
||||
return {
|
||||
type: AuthMethodEnum.OIDC,
|
||||
oidcSecurityService: injector.get(OidcSecurityService),
|
||||
isAuthenticated$: oidcSecurityService.isAuthenticated$.pipe(
|
||||
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');
|
||||
const authService = injector.get(AuthService);
|
||||
return {
|
||||
type: AppAuthMethod,
|
||||
authService,
|
||||
isAuthenticated$: authService.isAuthenticated$,
|
||||
userData$: authService.userData$,
|
||||
checkAuthResultEvent$: authService.checkAuthResultEvent$,
|
||||
};
|
||||
}
|
||||
|
53
apps/webui/src/domains/auth/auth.service.ts
Normal file
53
apps/webui/src/domains/auth/auth.service.ts
Normal 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);
|
||||
}
|
||||
}
|
5
apps/webui/src/infra/di/inject.ts
Normal file
5
apps/webui/src/infra/di/inject.ts
Normal 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>);
|
||||
}
|
@ -1,19 +1,21 @@
|
||||
import * as React from "react"
|
||||
import React from 'react';
|
||||
|
||||
const MOBILE_BREAKPOINT = 768
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
}
|
||||
mql.addEventListener("change", onChange)
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
return () => mql.removeEventListener("change", onChange)
|
||||
}, [])
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
};
|
||||
mql.addEventListener('change', onChange);
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
return () => mql.removeEventListener('change', onChange);
|
||||
}, []);
|
||||
|
||||
return !!isMobile
|
||||
return !!isMobile;
|
||||
}
|
||||
|
7
apps/webui/src/infra/http/http.service.ts
Normal file
7
apps/webui/src/infra/http/http.service.ts
Normal 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);
|
||||
}
|
@ -48,16 +48,16 @@ const rootElement = document.getElementById('root');
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<Suspense>
|
||||
<InjectorProvider injector={injector}>
|
||||
<InjectorProvider injector={injector}>
|
||||
<Suspense>
|
||||
<RouterProvider
|
||||
router={router}
|
||||
context={{
|
||||
injector,
|
||||
}}
|
||||
/>
|
||||
</InjectorProvider>
|
||||
</Suspense>
|
||||
</Suspense>
|
||||
</InjectorProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { useAuth } from '@/app/auth/hooks';
|
||||
import { type Fetcher, createGraphiQLFetcher } from '@graphiql/toolkit';
|
||||
import { createLazyFileRoute } from '@tanstack/react-router';
|
||||
import GraphiQL from 'graphiql';
|
||||
import { GraphiQL } from 'graphiql';
|
||||
import { useCallback } from 'react';
|
||||
import 'graphiql/graphiql.css';
|
||||
import { AuthMethodEnum } from '@/app/auth/config';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
export const Route = createLazyFileRoute('/_app/playground/graphql-api')({
|
||||
@ -16,12 +15,9 @@ function PlaygroundGraphQLApiRouteComponent() {
|
||||
|
||||
const fetcher: Fetcher = useCallback(
|
||||
async (props) => {
|
||||
const accessToken =
|
||||
authContext.type === AuthMethodEnum.OIDC
|
||||
? await firstValueFrom(
|
||||
authContext.oidcSecurityService.getAccessToken()
|
||||
)
|
||||
: undefined;
|
||||
const accessToken = await firstValueFrom(
|
||||
authContext.authService.getAccessToken()
|
||||
);
|
||||
return createGraphiQLFetcher({
|
||||
url: '/api/graphql',
|
||||
headers: accessToken
|
||||
@ -31,11 +27,7 @@ function PlaygroundGraphQLApiRouteComponent() {
|
||||
: undefined,
|
||||
})(props);
|
||||
},
|
||||
[
|
||||
authContext.type,
|
||||
// @ts-ignore
|
||||
authContext?.oidcSecurityService?.getAccessToken,
|
||||
]
|
||||
[authContext.authService.getAccessToken]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -7,6 +7,7 @@
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"noParameterProperties": "off",
|
||||
"noNonNullAssertion": "off"
|
||||
},
|
||||
"security": {
|
||||
|
24
package.json
24
package.json
@ -14,7 +14,7 @@
|
||||
"bump-deps": "npx --yes npm-check-updates --deep -u -x react-day-picker && pnpm install",
|
||||
"clean": "git clean -xdf node_modules"
|
||||
},
|
||||
"packageManager": "pnpm@10.6.1",
|
||||
"packageManager": "pnpm@10.10.0",
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
},
|
||||
@ -22,23 +22,9 @@
|
||||
"@auto-it/all-contributors": "^11.3.0",
|
||||
"@auto-it/first-time-contributor": "^11.3.0",
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/node": "^22.13.8",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.8.2",
|
||||
"ultracite": "^4.1.15"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"@types/node": "^22.15.16",
|
||||
"tsx": "^4.19.4",
|
||||
"typescript": "^5.8.3",
|
||||
"ultracite": "^4.2.4"
|
||||
}
|
||||
}
|
||||
|
4658
pnpm-lock.yaml
generated
4658
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user