diff --git a/api/api/__main__.py b/api/api/__main__.py index 6e57ec2..f00b342 100644 --- a/api/api/__main__.py +++ b/api/api/__main__.py @@ -73,6 +73,7 @@ if __name__ == "__main__": log_level="info", ) +app.add_middleware(MiddlewareAccessTokenValidadtion) app.add_middleware( CORSMiddleware, allow_origins=origins, @@ -80,5 +81,3 @@ app.add_middleware( allow_methods=["GET", "POST", "OPTIONS", "DELETE", "PUT"], allow_headers=["*"], ) - -app.add_middleware(MiddlewareAccessTokenValidadtion) diff --git a/api/api/db/logic/auth.py b/api/api/db/logic/auth.py index 393598d..20e9573 100644 --- a/api/api/db/logic/auth.py +++ b/api/api/db/logic/auth.py @@ -50,13 +50,12 @@ async def get_user(connection: AsyncConnection, login: str) -> Optional[User]: return user, password -async def upgrade_old_refresh_token(connection: AsyncConnection, user, refresh_token) -> Optional[User]: +async def upgrade_old_refresh_token(connection: AsyncConnection, refresh_token) -> Optional[User]: new_status = KeyStatus.EXPIRED update_query = ( update(account_keyring_table) .where( - account_table.c.id == user.id, account_keyring_table.c.status == KeyStatus.ACTIVE, account_keyring_table.c.key_type == KeyType.REFRESH_TOKEN, account_keyring_table.c.key_value == refresh_token, diff --git a/api/api/endpoints/auth.py b/api/api/endpoints/auth.py index bd0cfe8..edad474 100644 --- a/api/api/endpoints/auth.py +++ b/api/api/endpoints/auth.py @@ -91,25 +91,21 @@ async def refresh( request: Request, connection: AsyncConnection = Depends(get_connection_dep), Authorize: AuthJWT = Depends() ): refresh_token = request.cookies.get("refresh_token_cookie") - # print("Refresh Token:", refresh_token) if not refresh_token: raise HTTPException(status_code=401, detail="Refresh token is missing") try: - Authorize.jwt_refresh_token_required() - current_user = Authorize.get_jwt_subject() - - except Exception as e: - await upgrade_old_refresh_token(connection, current_user, refresh_token) - + Authorize.jwt_refresh_token_required(refresh_token) + current_user = Authorize._verified_token(refresh_token).get("sub") + except Exception: + await upgrade_old_refresh_token(connection, refresh_token) raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token", ) access_token_expires = timedelta(minutes=get_settings().ACCESS_TOKEN_EXPIRE_MINUTES) - new_access_token = Authorize.create_access_token(subject=current_user, expires_time=access_token_expires) return Access(access_token=new_access_token) diff --git a/api/api/services/middleware.py b/api/api/services/middleware.py index a44070d..68e511c 100644 --- a/api/api/services/middleware.py +++ b/api/api/services/middleware.py @@ -1,3 +1,4 @@ +from fastapi_jwt_auth import AuthJWT from starlette.middleware.base import BaseHTTPMiddleware from fastapi import ( Request, @@ -11,9 +12,6 @@ import re from re import escape -from fastapi_jwt_auth import AuthJWT - - class MiddlewareAccessTokenValidadtion(BaseHTTPMiddleware): def __init__(self, app): super().__init__(app) @@ -22,40 +20,34 @@ class MiddlewareAccessTokenValidadtion(BaseHTTPMiddleware): self.excluded_routes = [ re.compile(r"^" + re.escape(self.prefix) + r"/auth/refresh/?$"), re.compile(r"^" + re.escape(self.prefix) + r"/auth/?$"), + re.compile(r"^" + r"/swagger"), + re.compile(r"^" + r"/openapi"), ] async def dispatch(self, request: Request, call_next): - if request.method in ["GET", "POST", "PUT", "DELETE"]: - if any(pattern.match(request.url.path) for pattern in self.excluded_routes): - return await call_next(request) - else: - auth_header = request.headers.get("Authorization") - if not auth_header: - return JSONResponse( - status_code=status.HTTP_401_UNAUTHORIZED, - content={"detail": "Missing authorization header."}, - headers={"WWW-Authenticate": "Bearer"}, - ) - - token = auth_header.split(" ")[1] - Authorize = AuthJWT(request) - - try: - current_user = Authorize.get_jwt_subject() - request.state.current_user = current_user - return await call_next(request) - - except Exception: - return JSONResponse( - status_code=status.HTTP_401_UNAUTHORIZED, - content={"detail": "The access token is invalid or expired."}, - headers={"WWW-Authenticate": "Bearer"}, - ) - - # async with get_connection() as connection: - # authorize_user = await get_user_login(connection, current_user) - # print(authorize_user) - # if authorize_user is None : - # return JSONResponse( - # status_code=status.HTTP_404_NOT_FOUND , - # detail="User not found.") + if request.method not in ["GET", "POST", "PUT", "DELETE"]: + return JSONResponse( + status_code=status.HTTP_405_METHOD_NOT_ALLOWED, + content={"detail": "Method not allowed"}, + ) + if any(pattern.match(request.url.path) for pattern in self.excluded_routes): + return await call_next(request) + auth_header = request.headers.get("Authorization") + if not auth_header: + return JSONResponse( + status_code=status.HTTP_401_UNAUTHORIZED, + content={"detail": "Missing authorization header."}, + headers={"WWW-Authenticate": "Bearer"}, + ) + try: + token = auth_header.split(" ")[1] + Authorize = AuthJWT(request) + current_user = Authorize.get_jwt_subject() + request.state.current_user = current_user + except Exception: + return JSONResponse( + status_code=status.HTTP_401_UNAUTHORIZED, + content={"detail": "The access token is invalid or expired."}, + headers={"WWW-Authenticate": "Bearer"}, + ) + return await call_next(request) diff --git a/client/package-lock.json b/client/package-lock.json index 838d78d..4f0bc7c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "client", - "version": "0.0.1", + "version": "0.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "client", - "version": "0.0.1", + "version": "0.0.2", "dependencies": { "@ant-design/icons": "^5.6.1", "@testing-library/dom": "^10.4.0", @@ -18,6 +18,8 @@ "@types/react": "^19.0.11", "@types/react-dom": "^19.0.4", "antd": "^5.24.7", + "axios": "^1.9.0", + "axios-retry": "^4.5.0", "i18next": "^25.0.1", "i18next-browser-languagedetector": "^8.0.5", "react": "^18.3.1", @@ -26,7 +28,8 @@ "react-router-dom": "^7.5.0", "react-scripts": "5.0.1", "typescript": "^4.9.5", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^5.0.5" } }, "node_modules/@adobe/css-tools": { @@ -5262,6 +5265,45 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios-retry": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", + "license": "Apache-2.0", + "dependencies": { + "is-retry-allowed": "^2.2.0" + }, + "peerDependencies": { + "axios": "0.x || 1.x" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -10063,6 +10105,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-root": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", @@ -14085,6 +14139,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -18743,6 +18803,35 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.5.tgz", + "integrity": "sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/client/package.json b/client/package.json index c991ffb..cc6e3a8 100644 --- a/client/package.json +++ b/client/package.json @@ -13,6 +13,8 @@ "@types/react": "^19.0.11", "@types/react-dom": "^19.0.4", "antd": "^5.24.7", + "axios": "^1.9.0", + "axios-retry": "^4.5.0", "i18next": "^25.0.1", "i18next-browser-languagedetector": "^8.0.5", "react": "^18.3.1", @@ -21,7 +23,8 @@ "react-router-dom": "^7.5.0", "react-scripts": "5.0.1", "typescript": "^4.9.5", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^5.0.5" }, "scripts": { "start": "react-scripts start", diff --git a/client/public/icons/logo.svg b/client/public/icons/logo.svg new file mode 100644 index 0000000..882c169 --- /dev/null +++ b/client/public/icons/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/src/App.tsx b/client/src/App.tsx index 7fc96c3..3d98dae 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -2,12 +2,13 @@ import React from 'react'; import { Route, Routes } from 'react-router-dom'; import MainLayout from './pages/MainLayout'; import ProtectedRoute from './pages/ProtectedRoute'; +import LoginPage from './pages/LoginPage'; function App() { return (
- login
} /> + } /> }> }> diff --git a/client/src/api/api.ts b/client/src/api/api.ts new file mode 100644 index 0000000..c691e11 --- /dev/null +++ b/client/src/api/api.ts @@ -0,0 +1,88 @@ +import axios from 'axios'; +import { Access, Auth } from '../types/auth'; +import { User } from '../types/user'; +import { AuthService } from '../services/auth'; +import axiosRetry from 'axios-retry'; + +const baseURL = `${process.env.REACT_APP_HTTP_PROTOCOL}://${process.env.REACT_APP_API_URL}/api/v1`; + +const base = axios.create({ + baseURL, + withCredentials: true, + headers: { + accepts: 'application/json', + }, +}); + +base.interceptors.request.use((config) => { + const token = localStorage.getItem('accessToken'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +axiosRetry(base, { + retries: 3, + retryDelay: (retryCount: number) => { + console.log(`retry attempt: ${retryCount}`); + return retryCount * 2000; + }, + retryCondition: async (error: any) => { + if (error.code === 'ERR_CANCELED') { + return true; + } + return false; + }, +}); + +base.interceptors.response.use( + (response) => { + return response; + }, + async function (error) { + console.log('error', error); + const originalRequest = error.response.config; + console.log('originalRequest._retry', originalRequest); + const urlTokens = error?.request?.responseURL.split('/'); + const url = urlTokens[urlTokens.length - 1]; + console.log('url', url); + if ( + error.response.status === 401 && + !(originalRequest?._retry != null) && + url !== 'login' && + url !== 'refresh' && + url !== 'logout' + ) { + originalRequest._retry = true; + const res = await AuthService.refresh().catch(async () => { + await AuthService.logout(); + }); + console.log('res', res); + return await base(originalRequest); + } + return await Promise.reject(error); + } +); + +const api = { + // auth + async login(auth: Auth): Promise { + console.log(auth); + const response = await base.post('/auth', auth); + return response.data; + }, + + async refreshToken(): Promise { + const response = await base.post('/auth/refresh'); + return response.data; + }, + + // user + async getProfile(): Promise { + const response = await base.get('/profile'); + return response.data; + }, +}; + +export default api; diff --git a/client/src/pages/LoginPage.tsx b/client/src/pages/LoginPage.tsx new file mode 100644 index 0000000..93fe37b --- /dev/null +++ b/client/src/pages/LoginPage.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { Form, Input, Button, Typography } from 'antd'; +import { + EyeInvisibleOutlined, + EyeTwoTone, + UserOutlined, +} from '@ant-design/icons'; +import { AuthService } from '../services/auth'; +import { Auth } from '../types/auth'; +import { useNavigate } from 'react-router-dom'; + +const { Text, Link } = Typography; + +export default function LoginPage() { + const navigate = useNavigate(); + + const onFinish = async (values: any) => { + await AuthService.login(values as Auth); + navigate('/'); + }; + + return ( +
+
+
+ logo +
+ +
+ + } /> + + + + + visible ? : + } + /> + + + + + + + + Нажимая кнопку Войти, Вы полностью принимаете{' '} + + Публичную оферту + {' '} + и{' '} + + Политику обработки персональных данных + + +
+ +
+ Забыли пароль? +
+
+
+ ); +} diff --git a/client/src/pages/MainLayout.tsx b/client/src/pages/MainLayout.tsx index 2933a89..599a88b 100644 --- a/client/src/pages/MainLayout.tsx +++ b/client/src/pages/MainLayout.tsx @@ -9,6 +9,8 @@ import RunningProcessesPage from './RunningProcessesPage'; import AccountsPage from './AccountsPage'; import EventsListPage from './EventsListPage'; import ConfigurationPage from './ConfigurationPage'; +import { useSetUserSelector } from '../store/user'; +import { UserService } from '../services/user'; export default function MainLayout() { const navigate = useNavigate(); @@ -19,6 +21,8 @@ export default function MainLayout() { const [width, setWidth] = useState('15%'); const [collapsedWidth, setCollapsedWidth] = useState(50); + const setUser = useSetUserSelector(); + const calculateWidths = () => { const windowWidth = window.innerWidth; const expanded = Math.min(Math.max(windowWidth * 0.15, 180), 240); @@ -54,6 +58,21 @@ export default function MainLayout() { navigate(key); } + useEffect(() => { + const token = localStorage.getItem('accessToken'); + if (!token) { + navigate('/login'); + } else { + if (localStorage.getItem('user')) { + setUser(JSON.parse(localStorage.getItem('user') as string)); + } else { + UserService.getProfile().then((user) => { + setUser(user); + }); + } + } + }, []); + return ( { + const user = api.getProfile(); + + return user; + } +} diff --git a/client/src/store/user.ts b/client/src/store/user.ts new file mode 100644 index 0000000..137d3e8 --- /dev/null +++ b/client/src/store/user.ts @@ -0,0 +1,38 @@ +import { create } from 'zustand'; +import { devtools, persist } from 'zustand/middleware'; +import { User } from '../types/user'; + +const userInfo = localStorage.getItem('userInfo'); + +type UserStoreState = { + user: User; + loading: boolean; +}; + +type UserStoreActions = { + setUser: (user: User) => void; + removeUser: () => void; +}; + +type UserStore = UserStoreState & UserStoreActions; + +export const useUserStore = create()( + devtools( + persist( + (set, get) => ({ + user: userInfo != null ? JSON.parse(userInfo) : ({} as User), + loading: false, + setUser: (user: User) => set({ user }), + removeUser: () => set({ user: {} as User }), + }), + { name: 'userInfo' } + ) + ) +); + +export const useUserSelector = () => { + return useUserStore((state) => state.user); +}; +export const useSetUserSelector = () => { + return useUserStore((state) => state.setUser); +}; diff --git a/client/src/types/auth.ts b/client/src/types/auth.ts new file mode 100644 index 0000000..f1ac5a4 --- /dev/null +++ b/client/src/types/auth.ts @@ -0,0 +1,4 @@ +import { components } from './openapi-types'; + +export type Auth = components['schemas']['Auth']; +export type Access = components['schemas']['Access']; diff --git a/client/src/types/openapi-types.ts b/client/src/types/openapi-types.ts new file mode 100644 index 0000000..7e7f9e6 --- /dev/null +++ b/client/src/types/openapi-types.ts @@ -0,0 +1,692 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/api/v1/auth": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Login For Access Token + * @description Авторизирует, выставляет токены в куки. + */ + post: operations["login_for_access_token_api_v1_auth_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/auth/refresh": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Refresh */ + post: operations["refresh_api_v1_auth_refresh_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/profile": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Profile */ + get: operations["get_profile_api_v1_profile_get"]; + /** Update Profile */ + put: operations["update_profile_api_v1_profile_put"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/account": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get All Account */ + get: operations["get_all_account_api_v1_account_get"]; + put?: never; + /** Create Account */ + post: operations["create_account_api_v1_account_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/account/{user_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Account */ + get: operations["get_account_api_v1_account__user_id__get"]; + /** Update Account */ + put: operations["update_account_api_v1_account__user_id__put"]; + post?: never; + /** Delete Account */ + delete: operations["delete_account_api_v1_account__user_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/keyring/{user_id}/{key_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Keyring */ + get: operations["get_keyring_api_v1_keyring__user_id___key_id__get"]; + /** Update Keyring */ + put: operations["update_keyring_api_v1_keyring__user_id___key_id__put"]; + /** Create Keyring */ + post: operations["create_keyring_api_v1_keyring__user_id___key_id__post"]; + /** Delete Keyring */ + delete: operations["delete_keyring_api_v1_keyring__user_id___key_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** Access */ + Access: { + /** Accesstoken */ + accessToken: string; + }; + /** AccountKeyring */ + AccountKeyring: { + /** Ownerid */ + ownerId: number; + keyType: components["schemas"]["KeyType"]; + /** Keyid */ + keyId?: string | null; + /** Keyvalue */ + keyValue: string; + /** + * Createdat + * Format: date-time + */ + createdAt: string; + /** Expiry */ + expiry?: string | null; + status: components["schemas"]["KeyStatus"]; + }; + /** AccountKeyringUpdate */ + AccountKeyringUpdate: { + /** Ownerid */ + ownerId?: number | null; + keyType?: components["schemas"]["KeyType"] | null; + /** Keyid */ + keyId?: string | null; + /** Keyvalue */ + keyValue?: string | null; + /** Createdat */ + createdAt?: string | null; + /** Expiry */ + expiry?: string | null; + status?: components["schemas"]["KeyStatus"] | null; + }; + /** + * AccountRole + * @enum {string} + */ + AccountRole: "OWNER" | "ADMIN" | "EDITOR" | "VIEWER"; + /** + * AccountStatus + * @enum {string} + */ + AccountStatus: "ACTIVE" | "DISABLED" | "BLOCKED" | "DELETED"; + /** AllUser */ + AllUser: { + /** Id */ + id: number; + /** Name */ + name: string; + /** Login */ + login: string; + /** Email */ + email?: string | null; + /** Bindtenantid */ + bindTenantId?: string | null; + role: components["schemas"]["AccountRole"]; + /** + * Createdat + * Format: date-time + */ + createdAt: string; + status: components["schemas"]["AccountStatus"]; + }; + /** AllUserResponse */ + AllUserResponse: { + /** Users */ + users: components["schemas"]["AllUser"][]; + /** Amountcount */ + amountCount: number; + /** Amountpages */ + amountPages: number; + }; + /** Auth */ + Auth: { + /** Login */ + login: string; + /** Password */ + password: string; + }; + /** HTTPValidationError */ + HTTPValidationError: { + /** Detail */ + detail?: components["schemas"]["ValidationError"][]; + }; + /** + * KeyStatus + * @enum {string} + */ + KeyStatus: "ACTIVE" | "EXPIRED" | "DELETED"; + /** + * KeyType + * @enum {string} + */ + KeyType: "PASSWORD" | "ACCESS_TOKEN" | "REFRESH_TOKEN" | "API_KEY"; + /** User */ + User: { + /** Id */ + id?: number | null; + /** Name */ + name: string; + /** Login */ + login: string; + /** Email */ + email?: string | null; + /** Bindtenantid */ + bindTenantId?: string | null; + role: components["schemas"]["AccountRole"]; + /** Meta */ + meta: { + [key: string]: unknown; + }; + /** Creatorid */ + creatorId?: number | null; + /** + * Createdat + * Format: date-time + */ + createdAt: string; + status: components["schemas"]["AccountStatus"]; + }; + /** UserUpdate */ + UserUpdate: { + /** Id */ + id?: number | null; + /** Name */ + name?: string | null; + /** Login */ + login?: string | null; + /** Email */ + email?: string | null; + /** Bindtenantid */ + bindTenantId?: string | null; + role?: components["schemas"]["AccountRole"] | null; + /** Meta */ + meta?: { + [key: string]: unknown; + } | null; + /** Creatorid */ + creatorId?: number | null; + /** Createdat */ + createdAt?: string | null; + status?: components["schemas"]["AccountStatus"] | null; + }; + /** ValidationError */ + ValidationError: { + /** Location */ + loc: (string | number)[]; + /** Message */ + msg: string; + /** Error Type */ + type: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + login_for_access_token_api_v1_auth_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["Auth"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Access"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + refresh_api_v1_auth_refresh_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Access"]; + }; + }; + }; + }; + get_profile_api_v1_profile_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + }; + }; + update_profile_api_v1_profile_put: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UserUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_all_account_api_v1_account_get: { + parameters: { + query?: { + page?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AllUserResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_account_api_v1_account_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UserUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_account_api_v1_account__user_id__get: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_account_api_v1_account__user_id__put: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UserUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_account_api_v1_account__user_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_keyring_api_v1_keyring__user_id___key_id__get: { + parameters: { + query?: never; + header?: never; + path: { + key_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AccountKeyring"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_keyring_api_v1_keyring__user_id___key_id__put: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + key_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AccountKeyringUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AccountKeyring"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_keyring_api_v1_keyring__user_id___key_id__post: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + key_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AccountKeyringUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AccountKeyring"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_keyring_api_v1_keyring__user_id___key_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + key_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AccountKeyring"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; +} diff --git a/client/src/types/user.ts b/client/src/types/user.ts new file mode 100644 index 0000000..9134b70 --- /dev/null +++ b/client/src/types/user.ts @@ -0,0 +1,3 @@ +import { components } from "./openapi-types" + +export type User = components["schemas"]["User"];