From 17c9885ccce4fcd800cda8f51a2d357f729c0f83 Mon Sep 17 00:00:00 2001 From: john Date: Mon, 19 May 2025 09:23:15 +0200 Subject: [PATCH] refresh user --- scripts/generate-schema.mjs | 4 +- src/App.tsx | 31 ++++++++------- src/app/api/schema.ts | 52 +++++++++++++++++++++++-- src/app/auth/authService.ts | 31 +++++++++++++++ src/app/auth/components/RefreshUser.tsx | 27 +++++++++++++ src/app/messageBus/messageTypes.ts | 2 + 6 files changed, 127 insertions(+), 20 deletions(-) create mode 100644 src/app/auth/components/RefreshUser.tsx diff --git a/scripts/generate-schema.mjs b/scripts/generate-schema.mjs index 4381bbf..edc8972 100644 --- a/scripts/generate-schema.mjs +++ b/scripts/generate-schema.mjs @@ -14,9 +14,7 @@ export async function generateApiSchema(openapiUrl, outputFilePath, pathToPretti const request = new Request(openapiUrl) const response = await fetch(request) const json = await response.text() - const ast = await openapiTS(json, { - pathParamsAsTypes: true, - }) + const ast = await openapiTS(json, {}) const prettierConfig = await resolveConfig(pathToPrettierRc, { useCache: true, }) diff --git a/src/App.tsx b/src/App.tsx index 74cc867..6f04dca 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import LogoutPage from './app/auth/pages/LogoutPage.tsx' import UnauthorizedHandler from './app/auth/components/UnauthorizedHandler.tsx' import AdminPage from './app/admin/pages/AdminPage.tsx' import SignupCodesManagementPage from './app/admin/pages/subpages/SignupCodesManagementPage.tsx' +import RefreshUser from './app/auth/components/RefreshUser.tsx' function App() { const postService = new PostsService() @@ -19,22 +20,24 @@ function App() { return ( - - } - /> - } /> - } /> - } /> - } /> - }> + + } + path={'/'} + element={} /> - - + } /> + } /> + } /> + } /> + }> + } + /> + + + ) diff --git a/src/app/api/schema.ts b/src/app/api/schema.ts index 32d4ad8..f476eb0 100644 --- a/src/app/api/schema.ts +++ b/src/app/api/schema.ts @@ -87,8 +87,7 @@ export interface paths { requestBody: { content: { 'multipart/form-data': { - /** Format: binary */ - file?: string + file?: components['schemas']['IFormFile'] } } } @@ -112,7 +111,7 @@ export interface paths { patch?: never trace?: never } - [path: `/media/${string}`]: { + '/media/{id}': { parameters: { query?: never header?: never @@ -266,6 +265,45 @@ export interface paths { patch?: never trace?: never } + '/auth/user/{userId}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get: { + parameters: { + query?: never + header?: never + path: { + userId: string + } + cookie?: never + } + requestBody?: never + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'text/plain': components['schemas']['RefreshUserResult'] + 'application/json': components['schemas']['RefreshUserResult'] + 'text/json': components['schemas']['RefreshUserResult'] + } + } + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/auth/signup-codes': { parameters: { query?: never @@ -362,6 +400,8 @@ export interface components { /** Format: uuid */ next: string | null } + /** Format: binary */ + IFormFile: string ListSignupCodesResult: { signupCodes: components['schemas']['SignupCodeDto'][] } @@ -397,6 +437,12 @@ export interface components { /** Format: int32 */ height: number | null } + RefreshUserResult: { + /** Format: uuid */ + userId: string + username: string + isSuperUser: boolean + } RegisterRequest: { username: string password: string diff --git a/src/app/auth/authService.ts b/src/app/auth/authService.ts index a5733d6..293c242 100644 --- a/src/app/auth/authService.ts +++ b/src/app/auth/authService.ts @@ -63,4 +63,35 @@ export class AuthService { return res.data.signupCodes.map(SignupCode.fromDto) } + + async refreshUser(userId: string) { + if (this.getCookie('hasSession') !== 'true') { + return + } + + const res = await client.GET(`/auth/user/{userId}`, { + params: { + path: { userId }, + }, + credentials: 'include', + }) + + if (!res.data) { + dispatchMessage('auth:user-refresh-failed', null) + } else { + dispatchMessage('auth:user-refreshed', { ...res.data }) + } + } + + private getCookie(cookieName: string): string | undefined { + const cookie = document.cookie + .split('; ') + .map((c) => { + const [name, value] = c.split('=') + return { name, value } + }) + .find((c) => c.name === cookieName) + + return cookie?.value + } } diff --git a/src/app/auth/components/RefreshUser.tsx b/src/app/auth/components/RefreshUser.tsx new file mode 100644 index 0000000..34dd268 --- /dev/null +++ b/src/app/auth/components/RefreshUser.tsx @@ -0,0 +1,27 @@ +import { PropsWithChildren, useEffect, useRef } from 'react' +import { AuthService } from '../authService.ts' +import { useUser } from '../../user/userStore.ts' + +interface RefreshUserProps { + authService: AuthService +} + +export default function RefreshUser({ + authService, + children, +}: PropsWithChildren) { + const { user } = useUser() + const didRefresh = useRef(false) + + useEffect(() => { + const timeoutId = setTimeout(async () => { + if (didRefresh.current) return + if (user == null) return + didRefresh.current = true + await authService.refreshUser(user.userId) + }) + return () => clearTimeout(timeoutId) + }, [authService, user]) + + return <>{children} +} diff --git a/src/app/messageBus/messageTypes.ts b/src/app/messageBus/messageTypes.ts index 8d15f6c..080964e 100644 --- a/src/app/messageBus/messageTypes.ts +++ b/src/app/messageBus/messageTypes.ts @@ -5,4 +5,6 @@ export interface MessageTypes { 'auth:registered': User 'auth:logged-out': null 'auth:unauthorized': null + 'auth:user-refreshed': User + 'auth:user-refresh-failed': null }