diff --git a/apps/cs/app/api/proxy/[...url]/route.ts b/apps/cs/app/api/proxy/[...url]/route.ts index 53f5a6e41..5397e5fe6 100644 --- a/apps/cs/app/api/proxy/[...url]/route.ts +++ b/apps/cs/app/api/proxy/[...url]/route.ts @@ -9,21 +9,21 @@ import { import { cookies } from "next/headers"; import { NextResponse } from "next/server"; -export async function POST( +async function handler( request: Request, - props: { params: Promise<{ url: string[] }> }, + params: { url: string[] }, + method: "GET" | "POST" | "PUT" | "DELETE", ) { - const params = await props.params; - + const cookieStore = await cookies(); const headers: Record = { "Content-Type": "application/json", - "X-Site-Id": SITE_ID ?? "", + "X-Site-Id": SITE_ID, }; - - const cookieStore = await cookies(); + let body = {}; + const isRefreshHandler = params.url.includes("refresh"); // silent refresh - if (params.url.includes("refresh")) { + if (isRefreshHandler) { const refreshToken = cookieStore.get(REFRESH_TOKEN); headers[CLAIM_NAME] = refreshToken?.value ?? ""; @@ -33,9 +33,12 @@ export async function POST( } try { - const body = await request.json(); + if (!isRefreshHandler) { + body = await request.json(); + } + const result = await fetch(`${SERVER_API_URL}/${params.url.join("/")}`, { - method: "POST", + method, headers, body: JSON.stringify(body), }); @@ -79,3 +82,19 @@ export async function POST( ); } } + +export async function POST( + request: Request, + props: { params: Promise<{ url: string[] }> }, +) { + const params = await props.params; + return handler(request, params, "POST"); +} + +export async function PUT( + request: Request, + props: { params: Promise<{ url: string[] }> }, +) { + const params = await props.params; + return handler(request, params, "PUT"); +} diff --git a/apps/cs/src/app/index.tsx b/apps/cs/src/app/index.tsx index a78e1c124..672023d15 100644 --- a/apps/cs/src/app/index.tsx +++ b/apps/cs/src/app/index.tsx @@ -1,13 +1,15 @@ import "./styles/base.css"; import "./styles/components.css"; -import { CookieProvider, QueryProvider } from "@/shared/providers"; +import { AuthProvider, CookieProvider, QueryProvider } from "@/shared/provider"; const App = ({ children }: Readonly<{ children: React.ReactNode }>) => { return ( - {children} + + {children} + diff --git a/apps/cs/src/pages/auth/login/api/login.ts b/apps/cs/src/entities/auth/login/api/login-service.ts similarity index 83% rename from apps/cs/src/pages/auth/login/api/login.ts rename to apps/cs/src/entities/auth/login/api/login-service.ts index fc1d94ca1..722470184 100644 --- a/apps/cs/src/pages/auth/login/api/login.ts +++ b/apps/cs/src/entities/auth/login/api/login-service.ts @@ -1,13 +1,8 @@ import { AxiosError } from "axios"; -import { - ACCESS_TOKEN, - AUTH_USER_ID, - CLAIM_NAME, - ENDPOINT, - JWT_EXPIRATION_TIME, -} from "@/shared/config"; +import { ENDPOINT, JWT_EXPIRATION_TIME } from "@/shared/config/api"; +import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME } from "@/shared/config/env"; +import type { LoginService, ILogin } from "../model/login-interface"; import { tokenManager } from "../lib/token-manager"; -import type { LoginService, ILogin } from "../model/login"; export class LoginApiService implements LoginService { private static instance: LoginApiService; @@ -36,7 +31,7 @@ export class LoginApiService implements LoginService { const tokenId = data[AUTH_USER_ID]; if (!accessToken || !tokenId) { - throw new Error("Missing required authentication tokens"); + throw new Error("[login] Missing required authentication tokens"); } tokenManager.setToken(CLAIM_NAME, accessToken); @@ -67,13 +62,16 @@ export class LoginApiService implements LoginService { const tokenId = data[AUTH_USER_ID]; if (!accessToken || !tokenId) { - throw new Error("Missing required authentication tokens"); + throw new Error( + "[silentRefresh] Missing required authentication tokens", + ); } tokenManager.setToken(CLAIM_NAME, accessToken); tokenManager.setToken(AUTH_USER_ID, tokenId); } catch (error) { console.warn("Token has expired, logging out."); + tokenManager.clearAuthToken(); if (error instanceof AxiosError) { throw new Error( `Silent refresh failed: ${error.response?.data?.message || error.message}`, diff --git a/apps/cs/src/entities/auth/login/index.ts b/apps/cs/src/entities/auth/login/index.ts new file mode 100644 index 000000000..8c7981ac9 --- /dev/null +++ b/apps/cs/src/entities/auth/login/index.ts @@ -0,0 +1 @@ +export { LoginApi } from "./api/login-service"; diff --git a/apps/cs/src/pages/auth/login/lib/token-manager.ts b/apps/cs/src/entities/auth/login/lib/token-manager.ts similarity index 68% rename from apps/cs/src/pages/auth/login/lib/token-manager.ts rename to apps/cs/src/entities/auth/login/lib/token-manager.ts index c704bbb5c..4429c7d1b 100644 --- a/apps/cs/src/pages/auth/login/lib/token-manager.ts +++ b/apps/cs/src/entities/auth/login/lib/token-manager.ts @@ -1,5 +1,5 @@ -import { AUTH_USER_ID, CLAIM_NAME } from "@/shared/config"; -import { setToken, removeToken } from "@/shared/lib/cookie"; +import { AUTH_USER_ID, CLAIM_NAME } from "@/shared/config/env"; +import { getCookie, setCookie, removeCookie } from "@/shared/lib/cookie"; export class TokenManager { private static instance: TokenManager; @@ -14,13 +14,21 @@ export class TokenManager { return TokenManager.instance; } + public getToken(key: string): string | undefined { + return getCookie(key); + } + public setToken(key: string, value: string): void { - setToken(key, value, { + setCookie(key, value, { secure: window.location.protocol === "https:", sameSite: "lax", }); } + public removeToken(key: string): void { + removeCookie(key); + } + public setupRefreshTimer( callback: () => Promise, expirationTime: number, @@ -32,19 +40,19 @@ export class TokenManager { this.refreshTimer = setTimeout(() => { callback().catch((error) => { console.error("Silent refresh failed:", error); - this.clearAuth(); + this.clearAuthToken(); }); }, expirationTime - 60000); } - public clearAuth(): void { + public clearAuthToken(): void { if (this.refreshTimer) { clearTimeout(this.refreshTimer); this.refreshTimer = null; } - removeToken(CLAIM_NAME); - removeToken(AUTH_USER_ID); + this.removeToken(CLAIM_NAME); + this.removeToken(AUTH_USER_ID); } } diff --git a/apps/cs/src/pages/auth/login/model/login.ts b/apps/cs/src/entities/auth/login/model/login-interface.ts similarity index 80% rename from apps/cs/src/pages/auth/login/model/login.ts rename to apps/cs/src/entities/auth/login/model/login-interface.ts index 2fe9acc49..5aaf53044 100644 --- a/apps/cs/src/pages/auth/login/model/login.ts +++ b/apps/cs/src/entities/auth/login/model/login-interface.ts @@ -5,4 +5,5 @@ export interface ILogin { export interface LoginService { login(payload: ILogin): Promise; + silentRefresh(): Promise; } diff --git a/apps/cs/src/entities/site/api/site-service.ts b/apps/cs/src/entities/site/api/site-service.ts new file mode 100644 index 000000000..89ba82350 --- /dev/null +++ b/apps/cs/src/entities/site/api/site-service.ts @@ -0,0 +1,20 @@ +import { client } from "@/shared/api/client"; +import { ENDPOINT } from "@/shared/config/api"; +import type { + ISiteQuery, + ISiteInfo, + SiteService, +} from "../model/site-interface"; + +export class SiteApiService implements SiteService { + public async siteInfo(query: ISiteQuery): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.SITES}`, + { params: query }, + ); + + return response.data; + } +} + +export const SiteApi = new SiteApiService(); diff --git a/apps/cs/src/entities/site/index.ts b/apps/cs/src/entities/site/index.ts new file mode 100644 index 000000000..92ccd0ff7 --- /dev/null +++ b/apps/cs/src/entities/site/index.ts @@ -0,0 +1 @@ +export { SiteApi } from "./api/site-service"; diff --git a/apps/cs/src/entities/site/model/site-interface.ts b/apps/cs/src/entities/site/model/site-interface.ts new file mode 100644 index 000000000..8ba2da298 --- /dev/null +++ b/apps/cs/src/entities/site/model/site-interface.ts @@ -0,0 +1,49 @@ +export interface ISiteQuery { + siteNm: string; + siteKndCd: string; + useYn: string; + page: number; + size: number; + sort: string[]; +} + +export interface ISite { + siteId: string; + siteNm: string; + siteKindCode: string; + bscSiteYn: string; + useYn: string; +} + +export interface ISiteInfo { + totalPages: number; + totalElements: number; + number: number; + sort: { + empty: boolean; + sorted: boolean; + unsorted: boolean; + }; + size: number; + content: ISite[]; + pageable: { + sort: { + empty: boolean; + sorted: boolean; + unsorted: boolean; + }; + offset: number; + pageNumber: number; + pageSize: number; + paged: boolean; + unpaged: boolean; + }; + numberOfElements: number; + first: boolean; + last: boolean; + empty: boolean; +} + +export interface SiteService { + siteInfo(query: ISiteQuery): Promise; +} diff --git a/apps/cs/src/entities/user/api/user-service.ts b/apps/cs/src/entities/user/api/user-service.ts index 5d0f57eed..41319bc1f 100644 --- a/apps/cs/src/entities/user/api/user-service.ts +++ b/apps/cs/src/entities/user/api/user-service.ts @@ -1,10 +1,10 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; +import { client } from "@/shared/api/client"; +import { ENDPOINT } from "@/shared/config/api"; import type { IUser, IUserInfo, UserService } from "../model/user-interface"; export class UsersApiService implements UserService { - public async userInfo(payload: IUserInfo): Promise { - const response = await client.get( + public async userInfo(payload: IUser): Promise { + const response = await client.get( `${ENDPOINT.USER_SERVICE.USERS}/${payload.userUniqId}`, ); diff --git a/apps/cs/src/entities/user/lib/use-user.ts b/apps/cs/src/entities/user/lib/use-user.ts index a48c441c4..97cd2f034 100644 --- a/apps/cs/src/entities/user/lib/use-user.ts +++ b/apps/cs/src/entities/user/lib/use-user.ts @@ -1,15 +1,16 @@ import { useQuery } from "@tanstack/react-query"; +import { ENDPOINT } from "@/shared/config/api"; +import { AUTH_USER_ID } from "@/shared/config/env"; +import { useCookie } from "@/shared/provider"; import { UserApi } from "../api/user-service"; -import { useCookie } from "@/shared/providers"; -import { AUTH_USER_ID, ENDPOINT } from "@/shared/config"; const useUser = () => { - const { getToken } = useCookie(); + const { getCookie } = useCookie(); const { data } = useQuery({ queryKey: [ENDPOINT.USER_SERVICE.USERS], queryFn: () => UserApi.userInfo({ - userUniqId: getToken(AUTH_USER_ID) ?? "", + userUniqId: getCookie(AUTH_USER_ID) ?? "", }), }); diff --git a/apps/cs/src/entities/user/model/user-interface.ts b/apps/cs/src/entities/user/model/user-interface.ts index 1af75fbb7..cba4a6206 100644 --- a/apps/cs/src/entities/user/model/user-interface.ts +++ b/apps/cs/src/entities/user/model/user-interface.ts @@ -1,8 +1,8 @@ -export interface IUserInfo { +export interface IUser { userUniqId: string; } -export interface IUser { +export interface IUserInfo { regDt: string; mdfcnDt: string; rgtr: string; @@ -37,5 +37,5 @@ export interface IUser { } export interface UserService { - userInfo: (payload: IUserInfo) => Promise; + userInfo: (payload: IUser) => Promise; } diff --git a/apps/cs/src/pages/auth/login/ui/login-page.tsx b/apps/cs/src/pages/auth/login/ui/login-page.tsx index 41c8f57df..c8eb1e2f9 100644 --- a/apps/cs/src/pages/auth/login/ui/login-page.tsx +++ b/apps/cs/src/pages/auth/login/ui/login-page.tsx @@ -2,7 +2,7 @@ import Image from "next/image"; import { images } from "@common/assets"; -import { LoginApi } from "../api/login"; +import { LoginApi } from "@/entities/auth/login"; import "./login.css"; function LoginPage() { diff --git a/apps/cs/src/pages/site/model/__mocks__.tsx b/apps/cs/src/pages/site/model/__mocks__.tsx index f1e13e9f8..2b55da7d8 100644 --- a/apps/cs/src/pages/site/model/__mocks__.tsx +++ b/apps/cs/src/pages/site/model/__mocks__.tsx @@ -1,59 +1,43 @@ import { createColumnHelper } from "@tanstack/react-table"; -import { fakerKO as faker } from "@faker-js/faker"; +import type { ISite } from "@/entities/site/model/site-interface"; -type Site = { - id: number; - 사이트코드: string; - 사이트명: string; - 대표_URL: string; - 기본_사이트_여부: boolean; - 사용_여부: boolean; - 등록자: string; - 등록일: string; -}; - -faker.seed(123); - -const data: Site[] = Array.from({ length: 100 }, (_, i) => ({ - id: i + 1, - 사이트코드: faker.string.alpha(2).toUpperCase(), - 사이트명: faker.lorem.word(), - 대표_URL: faker.internet.url(), - 기본_사이트_여부: faker.datatype.boolean(), - 사용_여부: faker.datatype.boolean(), - 등록자: faker.person.fullName(), - 등록일: faker.date.recent().toISOString().split("T")[0] as string, -})); - -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor("id", { + columnHelper.accessor((_, index) => index + 1, { + id: "no", header: "NO", }), - columnHelper.accessor("사이트코드", { + columnHelper.accessor("siteId", { + id: "siteId", header: "사이트코드", }), - columnHelper.accessor("사이트명", { + columnHelper.accessor("siteNm", { + id: "siteNm", header: "사이트명", }), - columnHelper.accessor("대표_URL", { + columnHelper.accessor("siteKindCode", { + id: "unknown1", header: "대표 URL", }), - columnHelper.accessor("기본_사이트_여부", { + columnHelper.accessor("bscSiteYn", { + id: "bscSiteYn", header: "기본 사이트 여부", cell: (info) => (info.getValue() ? "Y" : "N"), }), - columnHelper.accessor("사용_여부", { + columnHelper.accessor("useYn", { + id: "useYn", header: "사용 여부", cell: (info) => (info.getValue() ? "Y" : "N"), }), - columnHelper.accessor("등록자", { + columnHelper.accessor("siteId", { + id: "unknown2", header: "등록자", }), - columnHelper.accessor("등록일", { + columnHelper.accessor("siteId", { + id: "ununknown3", header: "등록일", }), ]; -export { data, columns }; +export { columns }; diff --git a/apps/cs/src/pages/site/ui/site-page.tsx b/apps/cs/src/pages/site/ui/site-page.tsx index 171a7cf07..c13284ce8 100644 --- a/apps/cs/src/pages/site/ui/site-page.tsx +++ b/apps/cs/src/pages/site/ui/site-page.tsx @@ -3,30 +3,67 @@ import "@common/assets/styles/grid.css"; import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { Button, Input, DataTable } from "@common/components"; -import { data, columns } from "../model/__mocks__"; +import { SiteApi } from "@/entities/site/api/site-service"; +import { ENDPOINT } from "@/shared/config/api"; +import { columns } from "../model/__mocks__"; import { SitePageLayout } from "./layout"; +import type { ISiteQuery } from "@/entities/site/model/site-interface"; function SitePage() { + const [siteQuery, setSiteQuery] = useState({ + siteNm: "", + siteKndCd: "", + useYn: "", + page: 0, + size: 0, + sort: [], + }); + + const { data: siteInfo, refetch } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.SITES], + queryFn: () => SiteApi.siteInfo(siteQuery), + enabled: false, + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + refetch(); + }; + const router = useRouter(); const table = useReactTable({ - data, + data: siteInfo?.content ?? [], columns, getCoreRowModel: getCoreRowModel(), }); + useEffect(() => { + refetch(); + }, []); + return (
-
+
- - -
+ + setSiteQuery({ ...siteQuery, siteNm: e.target.value }) + } + /> + +
diff --git a/apps/cs/src/shared/api/client.ts b/apps/cs/src/shared/api/client.ts index 231887202..709bb44a5 100644 --- a/apps/cs/src/shared/api/client.ts +++ b/apps/cs/src/shared/api/client.ts @@ -1,5 +1,6 @@ import { Client } from "@common/network"; -import { BASE_URL, SITE_ID } from "../config"; +import { BASE_URL } from "../config/api"; +import { SITE_ID } from "../config/env"; export const client = Client.getInstance({ baseURL: BASE_URL, diff --git a/apps/cs/src/shared/api/index.ts b/apps/cs/src/shared/api/index.ts deleted file mode 100644 index b7b9d6121..000000000 --- a/apps/cs/src/shared/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { client } from "./client"; diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index b02f439be..052eea56a 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -8,6 +8,9 @@ export const ENDPOINT = { USERS: "/user-service/api/v1/users", SILENT_REFRESH: "/user-service/api/v1/users/token/refresh", }, + CMS_SERVICE: { + SITES: "/cms-service/api/v1/sites", + }, }; export const JWT_EXPIRATION_TIME = 1800000; diff --git a/apps/cs/src/shared/config/index.ts b/apps/cs/src/shared/config/index.ts deleted file mode 100644 index cd83447d5..000000000 --- a/apps/cs/src/shared/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./api"; -export * from "./env"; diff --git a/apps/cs/src/shared/lib/cookie.ts b/apps/cs/src/shared/lib/cookie.ts index 3e9c91d16..82db637bd 100644 --- a/apps/cs/src/shared/lib/cookie.ts +++ b/apps/cs/src/shared/lib/cookie.ts @@ -12,7 +12,7 @@ export function getCookie(name: string): string | undefined { export function setCookie( name: string, value: string, - options: Cookies.CookieAttributes, + options?: Cookies.CookieAttributes, ): void { if (typeof window === "undefined") { return; @@ -22,29 +22,10 @@ export function setCookie( export function removeCookie( name: string, - options: Cookies.CookieAttributes, + options?: Cookies.CookieAttributes, ): void { if (typeof window === "undefined") { return; } Cookies.remove(name, options); } - -export function getToken(key: string): string | undefined { - return getCookie(key); -} - -export function setToken( - key: string, - token: string, - options: Cookies.CookieAttributes, -): void { - setCookie(key, token, { - ...options, - path: "/", - }); -} - -export function removeToken(key: string): void { - removeCookie(key, { path: "/" }); -} diff --git a/apps/cs/src/shared/provider/auth-provider.tsx b/apps/cs/src/shared/provider/auth-provider.tsx new file mode 100644 index 000000000..7c827e948 --- /dev/null +++ b/apps/cs/src/shared/provider/auth-provider.tsx @@ -0,0 +1,97 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; +import { createContext, useContext, useState } from "react"; +import { client } from "../api/client"; +import { ENDPOINT } from "../config/api"; +import { AUTH_USER_ID } from "../config/env"; +import { getCookie } from "../lib/cookie"; +import { AxiosError } from "axios"; +import { LoginApi } from "@/entities/auth/login"; + +interface IUserInfo { + regDt: string; + mdfcnDt: string; + rgtr: string; + mdfr: string; + userUnqId: string; + userId: string; + userName: string; + email: string; + encryptedPassword: string; + refreshToken: string; + userStateCode: string; + lastLoginDate: string; + loginFailCount: number; + pswdHnt: string; + pswdAns: string; + mphn: string; + telno: string; + zip: string; + addr: string; + daddr: string; + laeSeCd: string; + ogdpSeCd: string; + aprvYn: string; + autzrId: string; + expryYmd: string; + lastPswdChgDt: string; + googleId: string; + kakaoId: string; + naverId: string; + isSocialUser: boolean; + hasPassword: boolean; +} + +export interface AuthContextType { + userInfo: IUserInfo | null; +} + +export const AuthContext = createContext(null); + +export const AuthProvider = ({ children }: { children: React.ReactNode }) => { + const [userInfo, setUserInfo] = useState(null); + + useQuery({ + queryKey: [ENDPOINT.USER_SERVICE.USERS], + queryFn: async () => { + try { + const response = await client.get( + `${ENDPOINT.USER_SERVICE.USERS}/${getCookie(AUTH_USER_ID)}`, + ); + setUserInfo(response.data); + return response; + } catch (error) { + console.error(error); + if (error instanceof AxiosError) { + if (error.response?.status === 401) { + try { + await LoginApi.silentRefresh(); + } catch (refreshError) { + console.error("Token refresh failed:", refreshError); + setUserInfo(null); + } + } + } + } + }, + }); + + const authContextValue: AuthContextType = { + userInfo, + }; + + return ( + + {children} + + ); +}; + +export const useAuth = (): AuthContextType => { + const context = useContext(AuthContext); + if (!context) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +}; diff --git a/apps/cs/src/shared/providers/cookie/cookie-provider.tsx b/apps/cs/src/shared/provider/cookie-provider.tsx similarity index 72% rename from apps/cs/src/shared/providers/cookie/cookie-provider.tsx rename to apps/cs/src/shared/provider/cookie-provider.tsx index f92ca8a6f..c50050cf5 100644 --- a/apps/cs/src/shared/providers/cookie/cookie-provider.tsx +++ b/apps/cs/src/shared/provider/cookie-provider.tsx @@ -1,24 +1,9 @@ "use client"; -import { - getCookie, - setCookie, - removeCookie, - getToken, - setToken, - removeToken, -} from "../../lib/cookie"; - import { createContext, useContext } from "react"; +import { getCookie, setCookie, removeCookie } from "../lib/cookie"; export interface CookieContextType { - getToken: (key: string) => string | undefined; - setToken: ( - key: string, - token: string, - options: Cookies.CookieAttributes, - ) => void; - removeToken: (key: string) => void; getCookie: (name: string) => string | undefined; setCookie: ( name: string, @@ -32,9 +17,6 @@ export const CookieContext = createContext(null); export function CookieProvider({ children }: { children: React.ReactNode }) { const cookieContextValue: CookieContextType = { - getToken, - setToken, - removeToken, getCookie, setCookie, removeCookie, diff --git a/apps/cs/src/shared/provider/index.ts b/apps/cs/src/shared/provider/index.ts new file mode 100644 index 000000000..8d018ff08 --- /dev/null +++ b/apps/cs/src/shared/provider/index.ts @@ -0,0 +1,3 @@ +export { AuthProvider, useAuth } from "./auth-provider"; +export { CookieProvider, useCookie } from "./cookie-provider"; +export { QueryProvider } from "./query-provider"; diff --git a/apps/cs/src/shared/providers/query/query-client.tsx b/apps/cs/src/shared/provider/query-provider.tsx similarity index 100% rename from apps/cs/src/shared/providers/query/query-client.tsx rename to apps/cs/src/shared/provider/query-provider.tsx diff --git a/apps/cs/src/shared/providers/index.ts b/apps/cs/src/shared/providers/index.ts deleted file mode 100644 index d3e4a1ff3..000000000 --- a/apps/cs/src/shared/providers/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { - CookieContext, - CookieProvider, - useCookie, -} from "./cookie/cookie-provider"; -export { QueryProvider } from "./query/query-client"; diff --git a/packages/common/network/client.ts b/packages/common/network/client.ts index c61ce01e9..705c45d5d 100644 --- a/packages/common/network/client.ts +++ b/packages/common/network/client.ts @@ -1,4 +1,4 @@ -import axios from "axios"; +import axios, { AxiosError } from "axios"; import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; import Cookies from "js-cookie"; import { AUTH_USER_ID, CLAIM_NAME } from "./env"; @@ -27,7 +27,19 @@ class Client { const userId = Cookies.get(AUTH_USER_ID); if (!token || !userId) { - throw new Error("Missing required authentication tokens"); + throw new AxiosError( + "[Client] Missing required authentication tokens", + "UNAUTHORIZED", + config, + null, + { + data: { message: "Missing required authentication tokens" }, + status: 401, + statusText: "Unauthorized", + headers: this.axiosInstance.defaults.headers, + config: config, + } as AxiosResponse + ); } config.headers[CLAIM_NAME] = token;