Skip to content

Commit

Permalink
fix uploader
Browse files Browse the repository at this point in the history
  • Loading branch information
4rthem committed Dec 19, 2023
1 parent 289b984 commit 083d3c8
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 60 deletions.
3 changes: 2 additions & 1 deletion expose/client/src/component/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {DashboardMenu} from '@alchemy/react-ps';
import config from '../config';
import {RouterProvider} from '@alchemy/navigation';
import {MatomoRouteWrapper, RouterProvider} from '@alchemy/navigation';
import {routes} from '../routes.ts';
import RouteProxy from './RouteProxy.tsx';

Expand All @@ -19,6 +19,7 @@ export default function App({}: Props) {
routes={routes}
options={{
RouteProxyComponent: RouteProxy,
WrapperComponent: MatomoRouteWrapper,
}}
/>
</>
Expand Down
21 changes: 14 additions & 7 deletions lib/js/auth/src/client/KeycloakClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ export default class KeycloakClient {
...rest,
});

this.client.registerListener(logoutEventType, this.onLogout.bind(this));
this.client.registerListener(logoutEventType, this.onLogout.bind(this), 255);
}

private async onLogout(options: LogoutEvent): Promise<void>
private async onLogout(event: LogoutEvent): Promise<void>
{
if (!options.quiet) {
this.logout(options);
if (!event.quiet) {
await this.logout({
...event,
}, event);
}
}

Expand Down Expand Up @@ -63,16 +65,21 @@ export default class KeycloakClient {
return `${this.getOpenIdConnectBaseUrl()}/logout?${queryString}`;
}

public logout({
public async logout({
redirectPath = '/',
...options
}: LogoutOptions = {}): void {
this.client.logout({
}: LogoutOptions = {}, event?: LogoutEvent): Promise<void> {
await this.client.logout({
...options,
noEvent: true,
});

if (redirectPath) {
if (event) {
event.stopPropagation = true;
event.preventDefault = true;
}

document.location.href = this.createLogoutUrl({redirectPath});
}
}
Expand Down
40 changes: 26 additions & 14 deletions lib/js/auth/src/client/OAuthClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export type UserInfoResponse = {

export type AuthEvent = {
type: string;
preventDefault?: boolean,
stopPropagation?: boolean,
};

export type LoginEvent = {
Expand Down Expand Up @@ -68,12 +70,17 @@ type Options = {

export type {Options as OAuthClientOptions};

type OrderedListener = {
p: number;
h: AuthEventHandler<any>;
};

export default class OAuthClient {
public tokenPromise: Promise<any> | undefined;
public readonly clientId: string;
public readonly clientSecret: string | undefined;
public readonly baseUrl: string;
private listeners: Record<string, AuthEventHandler<any>[]> = {};
private listeners: Record<string, OrderedListener[]> = {};
private readonly storage: IStorage;
private tokensCache: AuthTokens | undefined;
private sessionTimeout: ReturnType<typeof setTimeout> | undefined;
Expand Down Expand Up @@ -137,31 +144,35 @@ export default class OAuthClient {
return jwtDecode<UserInfoResponse>(accessToken);
}

public logout(options: LogoutOptions = {}): void {
public async logout(options: LogoutOptions = {}): Promise<LogoutEvent | undefined> {
this.clearSessionTimeout();
this.storage.removeItem(this.tokenStorageKey);
this.tokensCache = undefined;

if (!options.noEvent) {
this.triggerEvent<LogoutEvent>(logoutEventType, {
const event = {
...options,
});
} as LogoutEvent;

await this.triggerEvent<LogoutEvent>(logoutEventType, event);

return event;
}
this.storage.removeItem(this.tokenStorageKey);
this.tokensCache = undefined;
}

public registerListener<E extends AuthEvent = AuthEvent>(event: string, callback: AuthEventHandler<E>): void {
public registerListener<E extends AuthEvent = AuthEvent>(event: string, callback: AuthEventHandler<E>, priority: number = 0): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
this.listeners[event].push({p: priority, h: callback});
}

public unregisterListener<E extends AuthEvent = AuthEvent>(event: string, callback: AuthEventHandler<E>): void {
if (!this.listeners[event]) {
return;
}

const index = this.listeners[event].findIndex(c => c === callback);
const index = this.listeners[event].findIndex(({h}) => h === callback);
if (index >= 0) {
delete this.listeners[event][index];
}
Expand Down Expand Up @@ -253,16 +264,17 @@ export default class OAuthClient {
}

private async triggerEvent<E extends AuthEvent = AuthEvent>(type: string, event: Omit<E, "type">): Promise<void> {
const e: E = {
...event,
type,
} as E;
const e = event as E;
e.type = type;

if (!this.listeners[type]) {
return Promise.resolve();
}

await Promise.all(this.listeners[type].map(func => func(e)).filter(f => !!f));
const orderedListeners = this.listeners[type];
orderedListeners.sort((a, b) => a.p - b.p);

await Promise.all(orderedListeners.map(({h}) => !e.stopPropagation && h(e)).filter(f => !!f));
}

private handleSessionTimeout(tokens: AuthTokens): void {
Expand Down
6 changes: 3 additions & 3 deletions lib/js/navigation/src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import {
Routes,
RouteProxyProps,
RouteProxyComponent,
TErrorBoundaryComponent, TErrorFallbackComponent,
TErrorBoundaryComponent, TErrorFallbackComponent, RouteWrapperProps,
} from "./types";
import {getFullPath, getLocationPrefix} from "./utils";
import {Outlet, RouteObject} from "react-router-dom";
import React, {PropsWithChildren} from "react";
import React from "react";
import DefaultRouteProxy from "./proxy/DefaultRouteProxy";
import {NotFoundPage, ErrorPage} from "@alchemy/phrasea-ui";
import {ErrorBoundary} from "@alchemy/core";
Expand Down Expand Up @@ -119,7 +119,7 @@ export type RouterProviderOptions = {
RouteProxyComponent?: RouteProxyComponent,
ErrorComponent?: TErrorFallbackComponent,
ErrorBoundaryComponent?: TErrorBoundaryComponent,
WrapperComponent?: React.FC<PropsWithChildren<{}>>;
WrapperComponent?: React.FC<RouteWrapperProps>;
}

export function createRouterProviderRoutes(
Expand Down
33 changes: 22 additions & 11 deletions lib/js/react-auth/src/components/AuthenticationProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ export default function AuthenticationProvider({
const [tokens, setTokens] = React.useState<AuthTokens | undefined>(oauthClient.getTokens());

React.useEffect(() => {
const listener: AuthEventHandler<LogoutEvent> = async () => {
setTokens(undefined);
const listener: AuthEventHandler<LogoutEvent> = async (event) => {
if (!event.preventDefault) {
setTokens(undefined);
}
};

oauthClient.registerListener(logoutEventType, listener);
Expand Down Expand Up @@ -57,17 +59,26 @@ export default function AuthenticationProvider({
setRedirectPath(undefined);
}, [setRedirectPath]);

const logout = useCallback<LogoutFunction>((redirectPathAfterLogin?: string, quiet = false) => {
if (redirectPathAfterLogin) {
setRedirectPath(redirectPathAfterLogin);
} else {
setTimeout(() => {
setRedirectPath(undefined);
}, 500);
const logout = useCallback<LogoutFunction>(async (redirectPathAfterLogin?: string, quiet = false) => {
const handler = () => {
if (redirectPathAfterLogin) {
setRedirectPath(redirectPathAfterLogin);
} else {
setTimeout(() => {
setRedirectPath(undefined);
}, 500);
}
}

const event = await oauthClient.logout({quiet});
console.log('event', event);
if (event?.preventDefault) {
handler();

return;
}

oauthClient.logout({quiet});
setTokens(undefined);
handler();
}, [setTokens, setRedirectPath]);

const isAuthenticated = (): boolean => {
Expand Down
11 changes: 9 additions & 2 deletions uploader/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {RouterProvider} from '@alchemy/navigation';
import {MatomoRouteWrapper, RouterProvider} from '@alchemy/navigation';
import {routes} from './routes';
import './scss/App.scss';
import Menu from './components/Menu';
import RouteProxy from './components/RouteProxy';
import {PropsWithChildren} from "react";

type Props = {};

Expand All @@ -12,8 +13,14 @@ export default function App({}: Props) {
routes={routes}
options={{
RouteProxyComponent: RouteProxy,
WrapperComponent: Menu,
WrapperComponent: Wrapper,
}}
/>
);
}

function Wrapper({children}: PropsWithChildren<{}>) {
return <MatomoRouteWrapper>
<Menu>{children}</Menu>
</MatomoRouteWrapper>
}
15 changes: 1 addition & 14 deletions uploader/client/src/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
import React from 'react';
import {ModalStack} from '@alchemy/navigation';
import {AuthenticationProvider, MatomoUser} from '@alchemy/react-auth';
import UploaderUserProvider from './context/UploaderUserProvider';
import App from './App';
import {keycloakClient, oauthClient} from './lib/apiClient';
import FullPageLoader from './components/FullPageLoader.jsx';
import {oauthClient} from './lib/apiClient';
import {ToastContainer} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

type Props = {};

export default function Root({}: Props) {
const [redirecting, setRedirecting] = React.useState(false);
const onLogout = React.useCallback((redirectUri: string | false = '/') => {
setRedirecting(true);
keycloakClient.logout(redirectUri);
}, []);

if (redirecting) {
return <FullPageLoader />;
}

return (
<>
<ToastContainer position={'bottom-left'} />
<AuthenticationProvider
oauthClient={oauthClient}
onLogout={onLogout}
>
<MatomoUser />
<UploaderUserProvider>
Expand Down
9 changes: 1 addition & 8 deletions uploader/client/src/components/RouteProxy.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import type {RouteProxyProps} from '@alchemy/navigation';
import {MatomoRouteWrapper} from '@alchemy/navigation';
import {useAuth, useKeycloakUrls} from '@alchemy/react-auth';
import config from '../config.ts';
import {keycloakClient} from '../lib/apiClient.ts';
import Menu from './Menu.tsx';

export default function RouteProxy({
component: Component,
public: isPublic,
...rest
}: RouteProxyProps) {
const {isAuthenticated} = useAuth();
const {getLoginUrl} = useKeycloakUrls({
Expand All @@ -22,9 +19,5 @@ export default function RouteProxy({
return <></>;
}

return (
<Menu>
<MatomoRouteWrapper component={Component} {...rest} />
</Menu>
);
return <Component/>
}

0 comments on commit 083d3c8

Please sign in to comment.