Refactor auto-generated schemas and types formatting
Some checks failed
Build and Push Docker Images / changes (push) Successful in 5s
Build and Push Docker Images / build-backend (push) Has been skipped
Build and Push Docker Images / build-frontend (push) Failing after 42s

Updated the formatting for auto-generated schemas, types, and related files to adhere to consistent coding standards (e.g., double quotes, indentation). No functional changes were made, ensuring behavior remains identical.
This commit is contained in:
2025-03-05 10:13:33 +01:00
parent ffa3f2ffd3
commit c61ad52331
12 changed files with 1193 additions and 914 deletions

View File

@@ -1,13 +1,13 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: 'standalone', output: "standalone",
// Ensure we can connect to the backend in Docker // Ensure we can connect to the backend in Docker
async rewrites() { async rewrites() {
return [ return [
{ {
source: '/api/:path*', source: "/api/:path*",
destination: 'http://backend:8000/:path*', destination: "http://backend:8000/:path*",
}, },
]; ];
}, },

View File

@@ -1,28 +1,27 @@
import {defineConfig} from '@hey-api/openapi-ts'; import { defineConfig } from "@hey-api/openapi-ts";
import path from "path"; import path from "path";
const API_URL = process.env.NEXT_PUBLIC_BACKEND_API_URL || 'http://localhost:8000'; const API_URL =
process.env.NEXT_PUBLIC_BACKEND_API_URL || "http://localhost:8000";
const OPENAPI_URL = `${API_URL}/api/v1/openapi.json`; const OPENAPI_URL = `${API_URL}/api/v1/openapi.json`;
const OUTPUT_DIR = path.resolve(__dirname, './src/client'); const OUTPUT_DIR = path.resolve(__dirname, "./src/client");
// const OPENAPI_FILE = path.resolve(__dirname, './openapi.json'); // const OPENAPI_FILE = path.resolve(__dirname, './openapi.json');
export default defineConfig({ export default defineConfig({
input: input: OPENAPI_URL,
OPENAPI_URL,
output: { output: {
format: 'prettier', format: "prettier",
lint: 'eslint', lint: "eslint",
path: OUTPUT_DIR, path: OUTPUT_DIR,
}, },
plugins: [ plugins: [
'@hey-api/client-axios', "@hey-api/client-axios",
'@hey-api/schemas', "@hey-api/schemas",
'@hey-api/sdk', "@hey-api/sdk",
{ {
enums: 'javascript', enums: "javascript",
name: '@hey-api/typescript', name: "@hey-api/typescript",
}, },
'@tanstack/react-query', "@tanstack/react-query",
], ],
}); });

View File

@@ -26,7 +26,10 @@
"@types/react-dom": "^19", "@types/react-dom": "^19",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.2.0", "eslint-config-next": "15.2.0",
"eslint-config-prettier": "^10.0.2",
"eslint-plugin-prettier": "^5.2.3",
"openapi-typescript-codegen": "^0.29.0", "openapi-typescript-codegen": "^0.29.0",
"prettier": "^3.5.3",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5"
} }
@@ -890,6 +893,19 @@
"node": ">=12.4.0" "node": ">=12.4.0"
} }
}, },
"node_modules/@pkgr/core": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
"integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/@rtsao/scc": { "node_modules/@rtsao/scc": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -2594,6 +2610,19 @@
} }
} }
}, },
"node_modules/eslint-config-prettier": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz",
"integrity": "sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg==",
"dev": true,
"license": "MIT",
"bin": {
"eslint-config-prettier": "build/bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-import-resolver-node": { "node_modules/eslint-import-resolver-node": {
"version": "0.3.9", "version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -2763,6 +2792,37 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
} }
}, },
"node_modules/eslint-plugin-prettier": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
"integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.9.1"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"eslint-config-prettier": "*",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-plugin-react": { "node_modules/eslint-plugin-react": {
"version": "7.37.4", "version": "7.37.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz",
@@ -2938,6 +2998,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -5032,6 +5099,35 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/prop-types": { "node_modules/prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -5755,6 +5851,23 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/synckit": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@pkgr/core": "^0.1.0",
"tslib": "^2.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "4.0.9", "version": "4.0.9",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.9.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.9.tgz",

View File

@@ -9,6 +9,7 @@
"lint": "next lint", "lint": "next lint",
"docker:build": "docker build -t eventspace-frontend .", "docker:build": "docker build -t eventspace-frontend .",
"docker:run": "docker run -p 3000:3000 eventspace-frontend", "docker:run": "docker run -p 3000:3000 eventspace-frontend",
"format": "prettier --write .",
"openapi-ts": "openapi-ts" "openapi-ts": "openapi-ts"
}, },
"dependencies": { "dependencies": {
@@ -30,7 +31,10 @@
"@types/react-dom": "^19", "@types/react-dom": "^19",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.2.0", "eslint-config-next": "15.2.0",
"eslint-config-prettier": "^10.0.2",
"eslint-plugin-prettier": "^5.2.3",
"openapi-typescript-codegen": "^0.29.0", "openapi-typescript-codegen": "^0.29.0",
"prettier": "^3.5.3",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5"
} }

View File

@@ -1,22 +1,53 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import { type Options, rootGet, register, login, loginOauth, refreshToken, changePassword, getCurrentUserInfo } from '../sdk.gen'; import {
import { queryOptions, type UseMutationOptions } from '@tanstack/react-query'; type Options,
import type { RootGetData, RegisterData, RegisterError, RegisterResponse, LoginData, LoginError, LoginResponse, LoginOauthData, LoginOauthError, LoginOauthResponse, RefreshTokenData, RefreshTokenError, RefreshTokenResponse, ChangePasswordData, ChangePasswordError, GetCurrentUserInfoData } from '../types.gen'; rootGet,
import type { AxiosError } from 'axios'; register,
import { client as _heyApiClient } from '../client.gen'; login,
loginOauth,
refreshToken,
changePassword,
getCurrentUserInfo,
} from "../sdk.gen";
import { queryOptions, type UseMutationOptions } from "@tanstack/react-query";
import type {
RootGetData,
RegisterData,
RegisterError,
RegisterResponse,
LoginData,
LoginError,
LoginResponse,
LoginOauthData,
LoginOauthError,
LoginOauthResponse,
RefreshTokenData,
RefreshTokenError,
RefreshTokenResponse,
ChangePasswordData,
ChangePasswordError,
GetCurrentUserInfoData,
} from "../types.gen";
import type { AxiosError } from "axios";
import { client as _heyApiClient } from "../client.gen";
export type QueryKey<TOptions extends Options> = [ export type QueryKey<TOptions extends Options> = [
Pick<TOptions, 'baseURL' | 'body' | 'headers' | 'path' | 'query'> & { Pick<TOptions, "baseURL" | "body" | "headers" | "path" | "query"> & {
_id: string; _id: string;
_infinite?: boolean; _infinite?: boolean;
} },
]; ];
const createQueryKey = <TOptions extends Options>(id: string, options?: TOptions, infinite?: boolean): [ const createQueryKey = <TOptions extends Options>(
QueryKey<TOptions>[0] id: string,
] => { options?: TOptions,
const params: QueryKey<TOptions>[0] = { _id: id, baseURL: (options?.client ?? _heyApiClient).getConfig().baseURL } as QueryKey<TOptions>[0]; infinite?: boolean,
): [QueryKey<TOptions>[0]] => {
const params: QueryKey<TOptions>[0] = {
_id: id,
baseURL: (options?.client ?? _heyApiClient).getConfig().baseURL,
} as QueryKey<TOptions>[0];
if (infinite) { if (infinite) {
params._infinite = infinite; params._infinite = infinite;
} }
@@ -32,12 +63,11 @@ const createQueryKey = <TOptions extends Options>(id: string, options?: TOptions
if (options?.query) { if (options?.query) {
params.query = options.query; params.query = options.query;
} }
return [ return [params];
params
];
}; };
export const rootGetQueryKey = (options?: Options<RootGetData>) => createQueryKey('rootGet', options); export const rootGetQueryKey = (options?: Options<RootGetData>) =>
createQueryKey("rootGet", options);
export const rootGetOptions = (options?: Options<RootGetData>) => { export const rootGetOptions = (options?: Options<RootGetData>) => {
return queryOptions({ return queryOptions({
@@ -46,15 +76,16 @@ export const rootGetOptions = (options?: Options<RootGetData>) => {
...options, ...options,
...queryKey[0], ...queryKey[0],
signal, signal,
throwOnError: true throwOnError: true,
}); });
return data; return data;
}, },
queryKey: rootGetQueryKey(options) queryKey: rootGetQueryKey(options),
}); });
}; };
export const registerQueryKey = (options: Options<RegisterData>) => createQueryKey('register', options); export const registerQueryKey = (options: Options<RegisterData>) =>
createQueryKey("register", options);
export const registerOptions = (options: Options<RegisterData>) => { export const registerOptions = (options: Options<RegisterData>) => {
return queryOptions({ return queryOptions({
@@ -63,29 +94,34 @@ export const registerOptions = (options: Options<RegisterData>) => {
...options, ...options,
...queryKey[0], ...queryKey[0],
signal, signal,
throwOnError: true throwOnError: true,
}); });
return data; return data;
}, },
queryKey: registerQueryKey(options) queryKey: registerQueryKey(options),
}); });
}; };
export const registerMutation = (options?: Partial<Options<RegisterData>>) => { export const registerMutation = (options?: Partial<Options<RegisterData>>) => {
const mutationOptions: UseMutationOptions<RegisterResponse, AxiosError<RegisterError>, Options<RegisterData>> = { const mutationOptions: UseMutationOptions<
RegisterResponse,
AxiosError<RegisterError>,
Options<RegisterData>
> = {
mutationFn: async (localOptions) => { mutationFn: async (localOptions) => {
const { data } = await register({ const { data } = await register({
...options, ...options,
...localOptions, ...localOptions,
throwOnError: true throwOnError: true,
}); });
return data; return data;
} },
}; };
return mutationOptions; return mutationOptions;
}; };
export const loginQueryKey = (options: Options<LoginData>) => createQueryKey('login', options); export const loginQueryKey = (options: Options<LoginData>) =>
createQueryKey("login", options);
export const loginOptions = (options: Options<LoginData>) => { export const loginOptions = (options: Options<LoginData>) => {
return queryOptions({ return queryOptions({
@@ -94,29 +130,34 @@ export const loginOptions = (options: Options<LoginData>) => {
...options, ...options,
...queryKey[0], ...queryKey[0],
signal, signal,
throwOnError: true throwOnError: true,
}); });
return data; return data;
}, },
queryKey: loginQueryKey(options) queryKey: loginQueryKey(options),
}); });
}; };
export const loginMutation = (options?: Partial<Options<LoginData>>) => { export const loginMutation = (options?: Partial<Options<LoginData>>) => {
const mutationOptions: UseMutationOptions<LoginResponse, AxiosError<LoginError>, Options<LoginData>> = { const mutationOptions: UseMutationOptions<
LoginResponse,
AxiosError<LoginError>,
Options<LoginData>
> = {
mutationFn: async (localOptions) => { mutationFn: async (localOptions) => {
const { data } = await login({ const { data } = await login({
...options, ...options,
...localOptions, ...localOptions,
throwOnError: true throwOnError: true,
}); });
return data; return data;
} },
}; };
return mutationOptions; return mutationOptions;
}; };
export const loginOauthQueryKey = (options: Options<LoginOauthData>) => createQueryKey('loginOauth', options); export const loginOauthQueryKey = (options: Options<LoginOauthData>) =>
createQueryKey("loginOauth", options);
export const loginOauthOptions = (options: Options<LoginOauthData>) => { export const loginOauthOptions = (options: Options<LoginOauthData>) => {
return queryOptions({ return queryOptions({
@@ -125,29 +166,36 @@ export const loginOauthOptions = (options: Options<LoginOauthData>) => {
...options, ...options,
...queryKey[0], ...queryKey[0],
signal, signal,
throwOnError: true throwOnError: true,
}); });
return data; return data;
}, },
queryKey: loginOauthQueryKey(options) queryKey: loginOauthQueryKey(options),
}); });
}; };
export const loginOauthMutation = (options?: Partial<Options<LoginOauthData>>) => { export const loginOauthMutation = (
const mutationOptions: UseMutationOptions<LoginOauthResponse, AxiosError<LoginOauthError>, Options<LoginOauthData>> = { options?: Partial<Options<LoginOauthData>>,
) => {
const mutationOptions: UseMutationOptions<
LoginOauthResponse,
AxiosError<LoginOauthError>,
Options<LoginOauthData>
> = {
mutationFn: async (localOptions) => { mutationFn: async (localOptions) => {
const { data } = await loginOauth({ const { data } = await loginOauth({
...options, ...options,
...localOptions, ...localOptions,
throwOnError: true throwOnError: true,
}); });
return data; return data;
} },
}; };
return mutationOptions; return mutationOptions;
}; };
export const refreshTokenQueryKey = (options: Options<RefreshTokenData>) => createQueryKey('refreshToken', options); export const refreshTokenQueryKey = (options: Options<RefreshTokenData>) =>
createQueryKey("refreshToken", options);
export const refreshTokenOptions = (options: Options<RefreshTokenData>) => { export const refreshTokenOptions = (options: Options<RefreshTokenData>) => {
return queryOptions({ return queryOptions({
@@ -156,29 +204,36 @@ export const refreshTokenOptions = (options: Options<RefreshTokenData>) => {
...options, ...options,
...queryKey[0], ...queryKey[0],
signal, signal,
throwOnError: true throwOnError: true,
}); });
return data; return data;
}, },
queryKey: refreshTokenQueryKey(options) queryKey: refreshTokenQueryKey(options),
}); });
}; };
export const refreshTokenMutation = (options?: Partial<Options<RefreshTokenData>>) => { export const refreshTokenMutation = (
const mutationOptions: UseMutationOptions<RefreshTokenResponse, AxiosError<RefreshTokenError>, Options<RefreshTokenData>> = { options?: Partial<Options<RefreshTokenData>>,
) => {
const mutationOptions: UseMutationOptions<
RefreshTokenResponse,
AxiosError<RefreshTokenError>,
Options<RefreshTokenData>
> = {
mutationFn: async (localOptions) => { mutationFn: async (localOptions) => {
const { data } = await refreshToken({ const { data } = await refreshToken({
...options, ...options,
...localOptions, ...localOptions,
throwOnError: true throwOnError: true,
}); });
return data; return data;
} },
}; };
return mutationOptions; return mutationOptions;
}; };
export const changePasswordQueryKey = (options: Options<ChangePasswordData>) => createQueryKey('changePassword', options); export const changePasswordQueryKey = (options: Options<ChangePasswordData>) =>
createQueryKey("changePassword", options);
export const changePasswordOptions = (options: Options<ChangePasswordData>) => { export const changePasswordOptions = (options: Options<ChangePasswordData>) => {
return queryOptions({ return queryOptions({
@@ -187,41 +242,51 @@ export const changePasswordOptions = (options: Options<ChangePasswordData>) => {
...options, ...options,
...queryKey[0], ...queryKey[0],
signal, signal,
throwOnError: true throwOnError: true,
}); });
return data; return data;
}, },
queryKey: changePasswordQueryKey(options) queryKey: changePasswordQueryKey(options),
}); });
}; };
export const changePasswordMutation = (options?: Partial<Options<ChangePasswordData>>) => { export const changePasswordMutation = (
const mutationOptions: UseMutationOptions<unknown, AxiosError<ChangePasswordError>, Options<ChangePasswordData>> = { options?: Partial<Options<ChangePasswordData>>,
) => {
const mutationOptions: UseMutationOptions<
unknown,
AxiosError<ChangePasswordError>,
Options<ChangePasswordData>
> = {
mutationFn: async (localOptions) => { mutationFn: async (localOptions) => {
const { data } = await changePassword({ const { data } = await changePassword({
...options, ...options,
...localOptions, ...localOptions,
throwOnError: true throwOnError: true,
}); });
return data; return data;
} },
}; };
return mutationOptions; return mutationOptions;
}; };
export const getCurrentUserInfoQueryKey = (options?: Options<GetCurrentUserInfoData>) => createQueryKey('getCurrentUserInfo', options); export const getCurrentUserInfoQueryKey = (
options?: Options<GetCurrentUserInfoData>,
) => createQueryKey("getCurrentUserInfo", options);
export const getCurrentUserInfoOptions = (options?: Options<GetCurrentUserInfoData>) => { export const getCurrentUserInfoOptions = (
options?: Options<GetCurrentUserInfoData>,
) => {
return queryOptions({ return queryOptions({
queryFn: async ({ queryKey, signal }) => { queryFn: async ({ queryKey, signal }) => {
const { data } = await getCurrentUserInfo({ const { data } = await getCurrentUserInfo({
...options, ...options,
...queryKey[0], ...queryKey[0],
signal, signal,
throwOnError: true throwOnError: true,
}); });
return data; return data;
}, },
queryKey: getCurrentUserInfoQueryKey(options) queryKey: getCurrentUserInfoQueryKey(options),
}); });
}; };

View File

@@ -1,7 +1,12 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import type { ClientOptions } from './types.gen'; import type { ClientOptions } from "./types.gen";
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-axios'; import {
type Config,
type ClientOptions as DefaultClientOptions,
createClient,
createConfig,
} from "@hey-api/client-axios";
/** /**
* The `createClientConfig()` function will be called on client initialization * The `createClientConfig()` function will be called on client initialization
@@ -11,8 +16,13 @@ import { type Config, type ClientOptions as DefaultClientOptions, createClient,
* `setConfig()`. This is useful for example if you're using Next.js * `setConfig()`. This is useful for example if you're using Next.js
* to ensure your client always has the correct values. * to ensure your client always has the correct values.
*/ */
export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> = (override?: Config<DefaultClientOptions & T>) => Config<Required<DefaultClientOptions> & T>; export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> =
(
override?: Config<DefaultClientOptions & T>,
) => Config<Required<DefaultClientOptions> & T>;
export const client = createClient(createConfig<ClientOptions>({ export const client = createClient(
baseURL: 'http://localhost:8000' createConfig<ClientOptions>({
})); baseURL: "http://localhost:8000",
}),
);

View File

@@ -1,3 +1,3 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
export * from './types.gen'; export * from "./types.gen";
export * from './sdk.gen'; export * from "./sdk.gen";

View File

@@ -3,17 +3,17 @@
export const Body_change_passwordSchema = { export const Body_change_passwordSchema = {
properties: { properties: {
current_password: { current_password: {
type: 'string', type: "string",
title: 'Current Password' title: "Current Password",
}, },
new_password: { new_password: {
type: 'string', type: "string",
title: 'New Password' title: "New Password",
}
}, },
type: 'object', },
required: ['current_password', 'new_password'], type: "object",
title: 'Body_change_password' required: ["current_password", "new_password"],
title: "Body_change_password",
} as const; } as const;
export const Body_login_oauthSchema = { export const Body_login_oauthSchema = {
@@ -21,242 +21,249 @@ export const Body_login_oauthSchema = {
grant_type: { grant_type: {
anyOf: [ anyOf: [
{ {
type: 'string', type: "string",
pattern: '^password$' pattern: "^password$",
}, },
{ {
type: 'null' type: "null",
} },
], ],
title: 'Grant Type' title: "Grant Type",
}, },
username: { username: {
type: 'string', type: "string",
title: 'Username' title: "Username",
}, },
password: { password: {
type: 'string', type: "string",
title: 'Password' title: "Password",
}, },
scope: { scope: {
type: 'string', type: "string",
title: 'Scope', title: "Scope",
default: '' default: "",
}, },
client_id: { client_id: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'null' type: "null",
} },
], ],
title: 'Client Id' title: "Client Id",
}, },
client_secret: { client_secret: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'null' type: "null",
}
],
title: 'Client Secret'
}
}, },
type: 'object', ],
required: ['username', 'password'], title: "Client Secret",
title: 'Body_login_oauth' },
},
type: "object",
required: ["username", "password"],
title: "Body_login_oauth",
} as const; } as const;
export const HTTPValidationErrorSchema = { export const HTTPValidationErrorSchema = {
properties: { properties: {
detail: { detail: {
items: { items: {
'$ref': '#/components/schemas/ValidationError' $ref: "#/components/schemas/ValidationError",
}, },
type: 'array', type: "array",
title: 'Detail' title: "Detail",
}
}, },
type: 'object', },
title: 'HTTPValidationError' type: "object",
title: "HTTPValidationError",
} as const; } as const;
export const LoginRequestSchema = { export const LoginRequestSchema = {
properties: { properties: {
email: { email: {
type: 'string', type: "string",
format: 'email', format: "email",
title: 'Email' title: "Email",
}, },
password: { password: {
type: 'string', type: "string",
title: 'Password' title: "Password",
}
}, },
type: 'object', },
required: ['email', 'password'], type: "object",
title: 'LoginRequest' required: ["email", "password"],
title: "LoginRequest",
} as const; } as const;
export const RefreshTokenRequestSchema = { export const RefreshTokenRequestSchema = {
properties: { properties: {
refresh_token: { refresh_token: {
type: 'string', type: "string",
title: 'Refresh Token' title: "Refresh Token",
}
}, },
type: 'object', },
required: ['refresh_token'], type: "object",
title: 'RefreshTokenRequest' required: ["refresh_token"],
title: "RefreshTokenRequest",
} as const; } as const;
export const TokenSchema = { export const TokenSchema = {
properties: { properties: {
access_token: { access_token: {
type: 'string', type: "string",
title: 'Access Token' title: "Access Token",
}, },
refresh_token: { refresh_token: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'null' type: "null",
} },
], ],
title: 'Refresh Token' title: "Refresh Token",
}, },
token_type: { token_type: {
type: 'string', type: "string",
title: 'Token Type', title: "Token Type",
default: 'bearer' default: "bearer",
}
}, },
type: 'object', },
required: ['access_token'], type: "object",
title: 'Token' required: ["access_token"],
title: "Token",
} as const; } as const;
export const UserCreateSchema = { export const UserCreateSchema = {
properties: { properties: {
email: { email: {
type: 'string', type: "string",
format: 'email', format: "email",
title: 'Email' title: "Email",
}, },
first_name: { first_name: {
type: 'string', type: "string",
title: 'First Name' title: "First Name",
}, },
last_name: { last_name: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'null' type: "null",
} },
], ],
title: 'Last Name' title: "Last Name",
}, },
phone_number: { phone_number: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'null' type: "null",
} },
], ],
title: 'Phone Number' title: "Phone Number",
}, },
password: { password: {
type: 'string', type: "string",
title: 'Password' title: "Password",
}, },
is_superuser: { is_superuser: {
type: 'boolean', type: "boolean",
title: 'Is Superuser', title: "Is Superuser",
default: false default: false,
}
}, },
type: 'object', },
required: ['email', 'first_name', 'password'], type: "object",
title: 'UserCreate' required: ["email", "first_name", "password"],
title: "UserCreate",
} as const; } as const;
export const UserResponseSchema = { export const UserResponseSchema = {
properties: { properties: {
email: { email: {
type: 'string', type: "string",
format: 'email', format: "email",
title: 'Email' title: "Email",
}, },
first_name: { first_name: {
type: 'string', type: "string",
title: 'First Name' title: "First Name",
}, },
last_name: { last_name: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'null' type: "null",
} },
], ],
title: 'Last Name' title: "Last Name",
}, },
phone_number: { phone_number: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'null' type: "null",
} },
], ],
title: 'Phone Number' title: "Phone Number",
}, },
id: { id: {
type: 'string', type: "string",
format: 'uuid', format: "uuid",
title: 'Id' title: "Id",
}, },
is_active: { is_active: {
type: 'boolean', type: "boolean",
title: 'Is Active' title: "Is Active",
}, },
is_superuser: { is_superuser: {
type: 'boolean', type: "boolean",
title: 'Is Superuser' title: "Is Superuser",
}, },
created_at: { created_at: {
type: 'string', type: "string",
format: 'date-time', format: "date-time",
title: 'Created At' title: "Created At",
}, },
updated_at: { updated_at: {
anyOf: [ anyOf: [
{ {
type: 'string', type: "string",
format: 'date-time' format: "date-time",
}, },
{ {
type: 'null' type: "null",
}
],
title: 'Updated At'
}
}, },
type: 'object', ],
required: ['email', 'first_name', 'id', 'is_active', 'is_superuser', 'created_at'], title: "Updated At",
title: 'UserResponse' },
},
type: "object",
required: [
"email",
"first_name",
"id",
"is_active",
"is_superuser",
"created_at",
],
title: "UserResponse",
} as const; } as const;
export const ValidationErrorSchema = { export const ValidationErrorSchema = {
@@ -265,26 +272,26 @@ export const ValidationErrorSchema = {
items: { items: {
anyOf: [ anyOf: [
{ {
type: 'string' type: "string",
}, },
{ {
type: 'integer' type: "integer",
}
]
}, },
type: 'array', ],
title: 'Location' },
type: "array",
title: "Location",
}, },
msg: { msg: {
type: 'string', type: "string",
title: 'Message' title: "Message",
}, },
type: { type: {
type: 'string', type: "string",
title: 'Error Type' title: "Error Type",
}
}, },
type: 'object', },
required: ['loc', 'msg', 'type'], type: "object",
title: 'ValidationError' required: ["loc", "msg", "type"],
title: "ValidationError",
} as const; } as const;

View File

@@ -1,10 +1,37 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import { type Options as ClientOptions, type TDataShape, type Client, urlSearchParamsBodySerializer } from '@hey-api/client-axios'; import {
import type { RootGetData, RootGetResponse, RegisterData, RegisterResponse, RegisterError, LoginData, LoginResponse, LoginError, LoginOauthData, LoginOauthResponse, LoginOauthError, RefreshTokenData, RefreshTokenResponse, RefreshTokenError, ChangePasswordData, ChangePasswordError, GetCurrentUserInfoData, GetCurrentUserInfoResponse } from './types.gen'; type Options as ClientOptions,
import { client as _heyApiClient } from './client.gen'; type TDataShape,
type Client,
urlSearchParamsBodySerializer,
} from "@hey-api/client-axios";
import type {
RootGetData,
RootGetResponse,
RegisterData,
RegisterResponse,
RegisterError,
LoginData,
LoginResponse,
LoginError,
LoginOauthData,
LoginOauthResponse,
LoginOauthError,
RefreshTokenData,
RefreshTokenResponse,
RefreshTokenError,
ChangePasswordData,
ChangePasswordError,
GetCurrentUserInfoData,
GetCurrentUserInfoResponse,
} from "./types.gen";
import { client as _heyApiClient } from "./client.gen";
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & { export type Options<
TData extends TDataShape = TDataShape,
ThrowOnError extends boolean = boolean,
> = ClientOptions<TData, ThrowOnError> & {
/** /**
* You can provide a client instance returned by `createClient()` instead of * You can provide a client instance returned by `createClient()` instead of
* individual options. This might be also useful if you want to implement a * individual options. This might be also useful if you want to implement a
@@ -21,11 +48,17 @@ export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends
/** /**
* Root * Root
*/ */
export const rootGet = <ThrowOnError extends boolean = false>(options?: Options<RootGetData, ThrowOnError>) => { export const rootGet = <ThrowOnError extends boolean = false>(
return (options?.client ?? _heyApiClient).get<RootGetResponse, unknown, ThrowOnError>({ options?: Options<RootGetData, ThrowOnError>,
responseType: 'text', ) => {
url: '/', return (options?.client ?? _heyApiClient).get<
...options RootGetResponse,
unknown,
ThrowOnError
>({
responseType: "text",
url: "/",
...options,
}); });
}; };
@@ -36,14 +69,20 @@ export const rootGet = <ThrowOnError extends boolean = false>(options?: Options<
* Returns: * Returns:
* The created user information. * The created user information.
*/ */
export const register = <ThrowOnError extends boolean = false>(options: Options<RegisterData, ThrowOnError>) => { export const register = <ThrowOnError extends boolean = false>(
return (options.client ?? _heyApiClient).post<RegisterResponse, RegisterError, ThrowOnError>({ options: Options<RegisterData, ThrowOnError>,
url: '/api/v1/auth/register', ) => {
return (options.client ?? _heyApiClient).post<
RegisterResponse,
RegisterError,
ThrowOnError
>({
url: "/api/v1/auth/register",
...options, ...options,
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...options?.headers ...options?.headers,
} },
}); });
}; };
@@ -54,14 +93,20 @@ export const register = <ThrowOnError extends boolean = false>(options: Options<
* Returns: * Returns:
* Access and refresh tokens. * Access and refresh tokens.
*/ */
export const login = <ThrowOnError extends boolean = false>(options: Options<LoginData, ThrowOnError>) => { export const login = <ThrowOnError extends boolean = false>(
return (options.client ?? _heyApiClient).post<LoginResponse, LoginError, ThrowOnError>({ options: Options<LoginData, ThrowOnError>,
url: '/api/v1/auth/login', ) => {
return (options.client ?? _heyApiClient).post<
LoginResponse,
LoginError,
ThrowOnError
>({
url: "/api/v1/auth/login",
...options, ...options,
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...options?.headers ...options?.headers,
} },
}); });
}; };
@@ -72,15 +117,21 @@ export const login = <ThrowOnError extends boolean = false>(options: Options<Log
* Returns: * Returns:
* Access and refresh tokens. * Access and refresh tokens.
*/ */
export const loginOauth = <ThrowOnError extends boolean = false>(options: Options<LoginOauthData, ThrowOnError>) => { export const loginOauth = <ThrowOnError extends boolean = false>(
return (options.client ?? _heyApiClient).post<LoginOauthResponse, LoginOauthError, ThrowOnError>({ options: Options<LoginOauthData, ThrowOnError>,
) => {
return (options.client ?? _heyApiClient).post<
LoginOauthResponse,
LoginOauthError,
ThrowOnError
>({
...urlSearchParamsBodySerializer, ...urlSearchParamsBodySerializer,
url: '/api/v1/auth/login/oauth', url: "/api/v1/auth/login/oauth",
...options, ...options,
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', "Content-Type": "application/x-www-form-urlencoded",
...options?.headers ...options?.headers,
} },
}); });
}; };
@@ -91,14 +142,20 @@ export const loginOauth = <ThrowOnError extends boolean = false>(options: Option
* Returns: * Returns:
* New access and refresh tokens. * New access and refresh tokens.
*/ */
export const refreshToken = <ThrowOnError extends boolean = false>(options: Options<RefreshTokenData, ThrowOnError>) => { export const refreshToken = <ThrowOnError extends boolean = false>(
return (options.client ?? _heyApiClient).post<RefreshTokenResponse, RefreshTokenError, ThrowOnError>({ options: Options<RefreshTokenData, ThrowOnError>,
url: '/api/v1/auth/refresh', ) => {
return (options.client ?? _heyApiClient).post<
RefreshTokenResponse,
RefreshTokenError,
ThrowOnError
>({
url: "/api/v1/auth/refresh",
...options, ...options,
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...options?.headers ...options?.headers,
} },
}); });
}; };
@@ -108,20 +165,26 @@ export const refreshToken = <ThrowOnError extends boolean = false>(options: Opti
* *
* Requires authentication. * Requires authentication.
*/ */
export const changePassword = <ThrowOnError extends boolean = false>(options: Options<ChangePasswordData, ThrowOnError>) => { export const changePassword = <ThrowOnError extends boolean = false>(
return (options.client ?? _heyApiClient).post<unknown, ChangePasswordError, ThrowOnError>({ options: Options<ChangePasswordData, ThrowOnError>,
) => {
return (options.client ?? _heyApiClient).post<
unknown,
ChangePasswordError,
ThrowOnError
>({
security: [ security: [
{ {
scheme: 'bearer', scheme: "bearer",
type: 'http' type: "http",
} },
], ],
url: '/api/v1/auth/change-password', url: "/api/v1/auth/change-password",
...options, ...options,
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...options?.headers ...options?.headers,
} },
}); });
}; };
@@ -131,15 +194,21 @@ export const changePassword = <ThrowOnError extends boolean = false>(options: Op
* *
* Requires authentication. * Requires authentication.
*/ */
export const getCurrentUserInfo = <ThrowOnError extends boolean = false>(options?: Options<GetCurrentUserInfoData, ThrowOnError>) => { export const getCurrentUserInfo = <ThrowOnError extends boolean = false>(
return (options?.client ?? _heyApiClient).get<GetCurrentUserInfoResponse, unknown, ThrowOnError>({ options?: Options<GetCurrentUserInfoData, ThrowOnError>,
) => {
return (options?.client ?? _heyApiClient).get<
GetCurrentUserInfoResponse,
unknown,
ThrowOnError
>({
security: [ security: [
{ {
scheme: 'bearer', scheme: "bearer",
type: 'http' type: "http",
} },
], ],
url: '/api/v1/auth/me', url: "/api/v1/auth/me",
...options ...options,
}); });
}; };

View File

@@ -64,7 +64,7 @@ export type RootGetData = {
body?: never; body?: never;
path?: never; path?: never;
query?: never; query?: never;
url: '/'; url: "/";
}; };
export type RootGetResponses = { export type RootGetResponses = {
@@ -80,7 +80,7 @@ export type RegisterData = {
body: UserCreate; body: UserCreate;
path?: never; path?: never;
query?: never; query?: never;
url: '/api/v1/auth/register'; url: "/api/v1/auth/register";
}; };
export type RegisterErrors = { export type RegisterErrors = {
@@ -105,7 +105,7 @@ export type LoginData = {
body: LoginRequest; body: LoginRequest;
path?: never; path?: never;
query?: never; query?: never;
url: '/api/v1/auth/login'; url: "/api/v1/auth/login";
}; };
export type LoginErrors = { export type LoginErrors = {
@@ -130,7 +130,7 @@ export type LoginOauthData = {
body: BodyLoginOauth; body: BodyLoginOauth;
path?: never; path?: never;
query?: never; query?: never;
url: '/api/v1/auth/login/oauth'; url: "/api/v1/auth/login/oauth";
}; };
export type LoginOauthErrors = { export type LoginOauthErrors = {
@@ -155,7 +155,7 @@ export type RefreshTokenData = {
body: RefreshTokenRequest; body: RefreshTokenRequest;
path?: never; path?: never;
query?: never; query?: never;
url: '/api/v1/auth/refresh'; url: "/api/v1/auth/refresh";
}; };
export type RefreshTokenErrors = { export type RefreshTokenErrors = {
@@ -174,13 +174,14 @@ export type RefreshTokenResponses = {
200: Token; 200: Token;
}; };
export type RefreshTokenResponse = RefreshTokenResponses[keyof RefreshTokenResponses]; export type RefreshTokenResponse =
RefreshTokenResponses[keyof RefreshTokenResponses];
export type ChangePasswordData = { export type ChangePasswordData = {
body: BodyChangePassword; body: BodyChangePassword;
path?: never; path?: never;
query?: never; query?: never;
url: '/api/v1/auth/change-password'; url: "/api/v1/auth/change-password";
}; };
export type ChangePasswordErrors = { export type ChangePasswordErrors = {
@@ -190,7 +191,8 @@ export type ChangePasswordErrors = {
422: HttpValidationError; 422: HttpValidationError;
}; };
export type ChangePasswordError = ChangePasswordErrors[keyof ChangePasswordErrors]; export type ChangePasswordError =
ChangePasswordErrors[keyof ChangePasswordErrors];
export type ChangePasswordResponses = { export type ChangePasswordResponses = {
/** /**
@@ -203,7 +205,7 @@ export type GetCurrentUserInfoData = {
body?: never; body?: never;
path?: never; path?: never;
query?: never; query?: never;
url: '/api/v1/auth/me'; url: "/api/v1/auth/me";
}; };
export type GetCurrentUserInfoResponses = { export type GetCurrentUserInfoResponses = {
@@ -213,8 +215,9 @@ export type GetCurrentUserInfoResponses = {
200: UserResponse; 200: UserResponse;
}; };
export type GetCurrentUserInfoResponse = GetCurrentUserInfoResponses[keyof GetCurrentUserInfoResponses]; export type GetCurrentUserInfoResponse =
GetCurrentUserInfoResponses[keyof GetCurrentUserInfoResponses];
export type ClientOptions = { export type ClientOptions = {
baseURL: 'http://localhost:8000' | (string & {}); baseURL: "http://localhost:8000" | (string & {});
}; };

View File

@@ -1,12 +1,23 @@
'use client'; "use client";
import React, {createContext, useContext, useState, useEffect, ReactNode, useCallback, useMemo} from 'react'; import React, {
import {useRouter, usePathname} from 'next/navigation'; createContext,
import {jwtDecode} from 'jwt-decode'; ReactNode,
import {client} from '@/client/client.gen'; useCallback,
import {useMutation, useQueryClient, useQuery, type UseQueryOptions} from '@tanstack/react-query'; useContext,
import {loginMutation, getCurrentUserInfoOptions} from '@/client/@tanstack/react-query.gen'; useEffect,
import {LoginRequest, UserResponse, Token} from '@/client/types.gen'; useMemo,
useState,
} from "react";
import { usePathname, useRouter } from "next/navigation";
import { jwtDecode } from "jwt-decode";
import { client } from "@/client/client.gen";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
getCurrentUserInfoOptions,
loginMutation,
} from "@/client/@tanstack/react-query.gen";
import { LoginRequest, Token, UserResponse } from "@/client/types.gen";
// JWT token payload interface // JWT token payload interface
interface TokenPayload { interface TokenPayload {
@@ -34,12 +45,9 @@ const defaultAuthState: AuthContextState = {
isAuthenticated: false, isAuthenticated: false,
isLoading: true, isLoading: true,
error: null, error: null,
login: async () => { login: async () => {},
}, logout: () => {},
logout: () => { refreshToken: async () => {},
},
refreshToken: async () => {
},
}; };
// Create context // Create context
@@ -49,24 +57,27 @@ const AuthContext = createContext<AuthContextState>(defaultAuthState);
export const useAuth = () => { export const useAuth = () => {
const context = useContext(AuthContext); const context = useContext(AuthContext);
if (!context) { if (!context) {
throw new Error('useAuth must be used within an AuthProvider'); throw new Error("useAuth must be used within an AuthProvider");
} }
return context; return context;
}; };
// Token management // Token management
const TOKEN_KEY = 'accessToken'; const TOKEN_KEY = "accessToken";
const REFRESH_TOKEN_KEY = 'refreshToken'; const REFRESH_TOKEN_KEY = "refreshToken";
// Get token from storage // Get token from storage
const getStoredToken = (): string | null => { const getStoredToken = (): string | null => {
if (typeof window === 'undefined') return null; if (typeof window === "undefined") return null;
return localStorage.getItem(TOKEN_KEY); return localStorage.getItem(TOKEN_KEY);
}; };
// Store token in storage // Store token in storage
const storeToken = (token: string | null, refreshToken: string | null = null): void => { const storeToken = (
if (typeof window === 'undefined') return; token: string | null,
refreshToken: string | null = null,
): void => {
if (typeof window === "undefined") return;
if (token) { if (token) {
localStorage.setItem(TOKEN_KEY, token); localStorage.setItem(TOKEN_KEY, token);
@@ -97,7 +108,7 @@ interface AuthProviderProps {
} }
// Auth Provider Component // Auth Provider Component
export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProviderProps) => { export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [isInitializing, setIsInitializing] = useState(true); const [isInitializing, setIsInitializing] = useState(true);
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
@@ -112,12 +123,13 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
if (token) { if (token) {
client.setConfig({ client.setConfig({
headers: { headers: {
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`,
} },
}); });
} }
}, []); }, []);
// Check if current token is valid (not expired)
const hasValidToken = useCallback((): boolean => { const hasValidToken = useCallback((): boolean => {
const token = getStoredToken(); const token = getStoredToken();
return Boolean(token && !isTokenExpired(token)); return Boolean(token && !isTokenExpired(token));
@@ -129,10 +141,10 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
enabled: hasValidToken(), enabled: hasValidToken(),
retry: false, retry: false,
staleTime: 5 * 60 * 1000, // 5 minutes staleTime: 5 * 60 * 1000, // 5 minutes
} as UseQueryOptions; };
// Use the query without onError in the options // Use the query without onError in the options
const {data: user, isLoading, error, refetch} = useQuery(userQueryOptions); const { data: user, isLoading, error, refetch } = useQuery(userQueryOptions);
// Handle error with a separate effect // Handle error with a separate effect
useEffect(() => { useEffect(() => {
@@ -143,10 +155,11 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
}, [error]); }, [error]);
// Login function // Login function
const login = useCallback(async (credentials: LoginRequest): Promise<void> => { const login = useCallback(
async (credentials: LoginRequest): Promise<void> => {
try { try {
const response = await loginMutationHook.mutateAsync({ const response = await loginMutationHook.mutateAsync({
body: credentials body: credentials,
}); });
// Store tokens // Store tokens
@@ -155,22 +168,24 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
// Configure client with new token // Configure client with new token
client.setConfig({ client.setConfig({
headers: { headers: {
Authorization: `Bearer ${response.access_token}` Authorization: `Bearer ${response.access_token}`,
} },
}); });
// Trigger user data fetch // Trigger user data fetch
await refetch(); await refetch();
// Redirect after login // Redirect after login
if (pathname === '/login') { if (pathname === "/login") {
router.push('/dashboard'); router.push("/dashboard");
} }
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error("Login failed:", error);
throw error; throw error;
} }
}, [loginMutationHook, refetch, router, pathname]); },
[loginMutationHook, refetch, router, pathname],
);
// Logout function // Logout function
const logout = useCallback((): void => { const logout = useCallback((): void => {
@@ -180,17 +195,17 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
// Remove auth headers // Remove auth headers
client.setConfig({ client.setConfig({
headers: { headers: {
Authorization: undefined Authorization: undefined,
} },
}); });
// Clear user from cache // Clear user from cache
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: getCurrentUserInfoOptions().queryKey queryKey: getCurrentUserInfoOptions().queryKey,
}); });
// Redirect to login page // Redirect to login page
router.push('/login'); router.push("/login");
}, [router, queryClient]); }, [router, queryClient]);
// Refresh token function // Refresh token function
@@ -204,8 +219,8 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
try { try {
const response = await client.post<Token>({ const response = await client.post<Token>({
url: '/api/v1/auth/refresh', url: "/api/v1/auth/refresh",
body: {refresh_token: refreshTokenValue}, body: { refresh_token: refreshTokenValue },
}); });
const newTokens = response.data; const newTokens = response.data;
@@ -217,15 +232,14 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
// Update client headers // Update client headers
client.setConfig({ client.setConfig({
headers: { headers: {
Authorization: `Bearer ${newTokens.access_token}` Authorization: `Bearer ${newTokens.access_token}`,
} },
}); });
} }
// Refetch user data // Refetch user data
await refetch(); await refetch();
} catch (error) { } catch (error) {
console.error('Token refresh failed:', error); console.error("Token refresh failed:", error);
logout(); logout();
} }
}, [logout, refetch]); }, [logout, refetch]);
@@ -246,43 +260,41 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
if (isInitializing) return; if (isInitializing) return;
const protectedRoutes = [ const protectedRoutes = [
'/admin', "/admin",
'/dashboard', "/dashboard",
'/events/create', "/events/create",
'/events/edit', "/events/edit",
'/profile', "/profile",
]; ];
const publicOnlyRoutes = [ const publicOnlyRoutes = ["/login", "/register"];
'/login',
'/register',
];
const publicRoutes = [ const publicRoutes = ["/", "/invite", "/rsvp"];
'/',
'/invite',
'/rsvp',
];
const isProtectedRoute = protectedRoutes.some(route => pathname?.startsWith(route)); const isProtectedRoute = protectedRoutes.some((route) =>
const isPublicOnlyRoute = publicOnlyRoutes.some(route => pathname === route); pathname?.startsWith(route),
);
const isPublicOnlyRoute = publicOnlyRoutes.some(
(route) => pathname === route,
);
// Handle loading state // Handle loading state
if (isLoading && isProtectedRoute) return; if (isLoading && isProtectedRoute) return;
// Redirect to login if not authenticated but trying to access protected route // Redirect to login if not authenticated but trying to access protected route
if (isProtectedRoute && !user) { if (isProtectedRoute && !user) {
router.push(`/login?redirect=${encodeURIComponent(pathname || '')}`); router.push(`/login?redirect=${encodeURIComponent(pathname || "")}`);
} }
// Redirect to dashboard if authenticated but trying to access public-only route // Redirect to dashboard if authenticated but trying to access public-only route
if (isPublicOnlyRoute && user) { if (isPublicOnlyRoute && user) {
router.push('/dashboard'); router.push("/dashboard");
} }
}, [user, isLoading, pathname, router, isInitializing]); }, [user, isLoading, pathname, router, isInitializing]);
// Create the context value // Create the context value
const value = useMemo<AuthContextState>(() => ({ const value = useMemo<AuthContextState>(
() => ({
user: user as UserResponse | null, user: user as UserResponse | null,
isAuthenticated: !!user, isAuthenticated: !!user,
isLoading: isInitializing || isLoading, isLoading: isInitializing || isLoading,
@@ -290,12 +302,9 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProvid
login, login,
logout, logout,
refreshToken, refreshToken,
}), [user, isInitializing, isLoading, error, login, logout, refreshToken]); }),
[user, isInitializing, isLoading, error, login, logout, refreshToken],
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
); );
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};