diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index c3f502a19..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# 디폴트 무시된 파일 -/shelf/ -/workspace.xml -# 에디터 기반 HTTP 클라이언트 요청 -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/apps/dm/app/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx b/apps/dm/app/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx index a57e345b3..8da2685da 100644 --- a/apps/dm/app/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx +++ b/apps/dm/app/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx @@ -99,7 +99,7 @@ export default function ModalSimilarPage({ sply = 0, usageCd = '', money = 0 }: selectArray={searchUseTypeList} value={searchUseType.value} onSelectValue={(val) => { - setSearchUseType({text: searchUseTypeList.find(item => item.val === val)?.text ?? '', value: val}); + setSearchUseType({text: searchUseTypeList.find(item => item.val === val)?.text ?? '', value: val.toString()}); }} className="w-[140px] ml-20" label="" @@ -123,7 +123,7 @@ export default function ModalSimilarPage({ sply = 0, usageCd = '', money = 0 }: className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id as keyof SimilarCaseType] === 'number' ? 'text-right' : 'text-center'} cursor-pointer`} key={`similar-case-cell-${head.id}-${index}-${el.ruseBizNm}`} rowSpan={(head.id === 'businessName' || head.id ==='businessDuration' || head.id === 'money') ? rowSpans[index] : undefined} - style={(head.id === 'businessName' || head.id === 'businessDuration' || head.id === 'money') && index > 0 && similarCaseList[index - 1]?.businessName === el.businessName ? { display: 'none' } : undefined} + style={(head.id === 'businessName' || head.id === 'businessDuration' || head.id === 'money') && index > 0 && similarCaseList[index - 1]?.ruseBizNm === el.ruseBizNm ? { display: 'none' } : undefined} onClick={() => handleCellClick(index, head.id)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { diff --git a/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx b/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx index e36d10781..119e1c487 100644 --- a/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx +++ b/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx @@ -185,7 +185,8 @@ function SdmInfoInsertContent() { dmdAplyPswd: "", index: 0, reqDt: "", - dmdInfoSn: 0 + dmdInfoSn: 0, + status : '', }; await insertSdmInfoData(apiData); diff --git a/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx b/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx index 89f6988a6..040304347 100644 --- a/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx +++ b/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx @@ -41,9 +41,11 @@ export default function MyForm() { }, [getDemandCompList]); const handleInsertSdmData = useCallback((data: import('@common/business_components/ui/sdmInsertForm/form').DemandCompType) => { - const convertedData: import('@/app/store/sdmMng').DemandCompType = { + const convertedData: DemandCompType = { ...data, - dmdInfoSn: '' + dmdInfoSn: '', + dmdInfoFk: '', + status: '', }; formDataRef.current = convertedData; insertSdmData(convertedData).catch(error => { diff --git a/apps/dm/components/ui/sdmInfoInsertForm/AccessibleFormField.tsx b/apps/dm/components/ui/sdmInfoInsertForm/AccessibleFormField.tsx index cfe985396..0c32db414 100644 --- a/apps/dm/components/ui/sdmInfoInsertForm/AccessibleFormField.tsx +++ b/apps/dm/components/ui/sdmInfoInsertForm/AccessibleFormField.tsx @@ -1,11 +1,10 @@ -import * as React from "react"; -import type { ReactNode } from "react"; -import type { ControllerRenderProps, FieldError, FieldValues } from "react-hook-form"; import { FormUi } from "@common/business_components/ui"; +import type { ReactNode } from "react"; +import type { ControllerRenderProps, FieldError, FieldValues, Path } from "react-hook-form"; interface AccessibleFormFieldProps { - field: ControllerRenderProps; + field: ControllerRenderProps>; label: string; required?: boolean; error?: FieldError; @@ -19,15 +18,10 @@ export function AccessibleFormField({ error, inputComponent, }: AccessibleFormFieldProps) { + const labelText = required ? `${label} *` : label; + return ( - - {label} - {required &&  *} - - } - > +
{inputComponent} {error?.message && Array.from(new Set(error.message.split('\n'))).map((msg, idx) => ( diff --git a/apps/dm/middleware.ts b/apps/dm/middleware.ts new file mode 100644 index 000000000..cb8365e4e --- /dev/null +++ b/apps/dm/middleware.ts @@ -0,0 +1,129 @@ +import { NextRequest, NextResponse } from "next/server"; +import { CS_URL } from "./components/ui/config"; +import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config"; + +// const CLAIM_NAME = "Authorization"; +// const AUTH_USER_ID = "token-id"; + const serverApiUrl = CS_URL; +const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || ""; +const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || ""; +const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ""; + +export function middleware(req: NextRequest) { + const pathname = req.nextUrl.pathname; + console.log(`[Middleware] Request URL: ${req.url}`); + const searchParams = req.nextUrl.searchParams; + + // if (pathname.startsWith("/server")) { + // const queryParams = searchParams.toString(); + // const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${ + // queryParams ? `?${queryParams}` : "" + // }`; + // console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`); + // return NextResponse.rewrite(new URL(newUrl)); + // } + + if (pathname.startsWith("/server")) { + const decodedParams = Array.from(searchParams.entries()) + .map(([key, value]) => `${key}=${decodeURIComponent(value)}`) + .join("&"); + + console.log(' sesesesesesevereveraserwereqwrewbrtkehjwrtg server') + const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${ + decodedParams ? `?${decodedParams}` : "" + }`; + return NextResponse.rewrite(new URL(newUrl)); + } + + + if (pathname.startsWith("/attach-service")) { + const requestHeaders = new Headers(req.headers); + // const token = + // "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhMGVjN2EwNy03NTJhLTRmN2YtODlhNi04NTRlNjNkMmUzYTAiLCJhdXRob3JpdGllcyI6IlJPTEVfQURNSU4sUk9MRV9BTk9OWU1PVVMiLCJtbmdyWW4iOiJZIiwiZXhwIjoxNzUyMTM1OTk3fQ.itGY6M6mC2iwKvEpXw2KS6354T_B_OFz4hD26ResIgwJhpwiDJCMuSXD-rZ3AarmAIKd0udpCuJR7UlvFMN2ww"; + // const userId = "a0ec7a07-752a-4f7f-89a6-854e63d2e3a0"; + const token = req.cookies.get(CLAIM_NAME)?.value; + const userId = req.cookies.get(AUTH_USER_ID)?.value; + const newUrl = `${serverApiUrl}${pathname}`; + + //wr:dev: pathname /attach-service/api/v1/tus/upload/5e60b70a-956e-40c3-be5a-cdd3100bf8ad + //http://dev.onasys.co.kr:8000/attach-service/api/v1/tus/upload/5e60b70a-956e-40c3-be5a-cdd3100bf8ad + if (token) requestHeaders.set(CLAIM_NAME, token); + if (userId) requestHeaders.set(AUTH_USER_ID, userId); + + console.log( + `[Middleware] Rewriting from '/attach-service' path to: ${newUrl}` + ); + return NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + } + + if (pathname.startsWith("/user-service")) { + const requestHeaders = new Headers(req.headers); + const newUrl = `${serverApiUrl}${pathname}`; + + console.log( + `[Middleware] Rewriting from '/user-service' path to: ${newUrl}` + ); + return NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders + }); + } + + if (pathname.startsWith("/geoserver")) { + let queryParams = searchParams.toString(); + const typename = searchParams.get("TYPENAME"); + console.log("typename ", typename); + + let newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`; + + if ( + typename == "hasudo:tb_il_swage_prcs_fclt" //하수처리시설 + || typename == "hasudo:tb_ms_swage_prcs_zone" //하수처리구역 + || typename == "hasudo:tb_ms_sbmrg_dngr_map" //침수위험지도 + || typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도 + ) { + newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`; + } + else if ( + typename == "hasudo:tb_ms_swage_ppl" //관로 + || typename == "hasudo:tb_ms_swage_mhl" //맨홀 + || typename == "hasudo:tb_ms_swage_pmstn" //펌프장 + || typename == "hasudo:tb_ms_stofcbr" //우수토실 + ) { + newUrl = `${LX_GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`; + } + else if ( + typename == "hasudo:om_dt_untrt_swg_rain" //중점우수토실 + || typename == "hasudo:om_wqlt_station" //관측소 + || typename == "hasudo:tb_wr_bdst_ruse_fclt" //재이용시설 + || typename == "hasudo:tb_wr_rnwtr_utztn_fclt" //빗물이용시설 + || typename == "hasudo:tb_wr_rswtr_fclt" //중수도시설 + || typename == "hasudo:tb_wr_htdfl_fclt" //온배수시설 + || typename == "hasudo:tb_wr_ruse_fclt_dmd_co" //수요처 + || typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처 + ){ + const url = req.nextUrl.clone(); + url.searchParams.set('TYPENAME', typename.replace("hasudo:","")); + queryParams = url.searchParams.toString(); + newUrl = `${SEWERAGE_API_URL}/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`; + } + + console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`); + return NextResponse.rewrite(new URL(newUrl)); + } + + if (pathname.startsWith("/sewerage-service")) { + const queryParams = searchParams.toString(); + const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${queryParams ? `?${queryParams}` : "" + }`; + console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`); + return NextResponse.rewrite(new URL(newUrl)); + } + + return NextResponse.next(); +} + +export const config = { + matcher: ["/server/:path*", "/user-service/:path*", "/attach-service/:path*", "/geoserver/:path*", "/sewerage-service/:path*"], +}; diff --git a/apps/dm/next.config.ts b/apps/dm/next.config.ts index 99301dab6..4d171bc44 100644 --- a/apps/dm/next.config.ts +++ b/apps/dm/next.config.ts @@ -2,6 +2,7 @@ import type { NextConfig } from "next"; import webpack from "webpack"; const nextConfig: NextConfig = { + output: "standalone", eslint: { ignoreDuringBuilds: true, }, diff --git a/apps/dm/public/img/wrOverview/img_ov_01_1.png b/apps/dm/public/img/wrOverview/img_ov_01_1.png index e09893ef9..e59a10192 100644 Binary files a/apps/dm/public/img/wrOverview/img_ov_01_1.png and b/apps/dm/public/img/wrOverview/img_ov_01_1.png differ diff --git a/apps/dm/src/entities/auth/api/login-service.ts b/apps/dm/src/entities/auth/api/login-service.ts new file mode 100644 index 000000000..3cd387507 --- /dev/null +++ b/apps/dm/src/entities/auth/api/login-service.ts @@ -0,0 +1,80 @@ + +import type { LoginService, ILogin } from "../model/login-interface"; +import { tokenManager } from "../lib/token-manager"; +import {PROXY_HOST, ACCESS_TOKEN, AUTH_USER_ID,SITE_ID, CLAIM_NAME, ENDPOINT, JWT_EXPIRATION_TIME } from "../../../shared/config"; + +export class LoginApiService implements LoginService { + private static instance: LoginApiService; + + private constructor() {} + + public static getInstance(): LoginApiService { + if (!LoginApiService.instance) { + LoginApiService.instance = new LoginApiService(); + } + return LoginApiService.instance; + } + + public async login(payload: ILogin): Promise { + try { + console.log(' ENDPOINT.USER_SERVICE.LOGIN ', ENDPOINT.USER_SERVICE.LOGIN) + const response = await fetch( PROXY_HOST+ ENDPOINT.USER_SERVICE.LOGIN, { + method: "POST", + body: JSON.stringify({ + userId: payload.userId, + password: payload.password, + }), + }); + // const data = response.headers.get(ACCESS_TOKEN); + const accessToken = response.headers.get(ACCESS_TOKEN); + const tokenId = response.headers.get(AUTH_USER_ID); + + console.log('accessToken ', accessToken) + console.log('tokenId ', tokenId) + + if (!accessToken || !tokenId) { + throw new Error("Missing required authentication tokens"); + } + + tokenManager.setToken(CLAIM_NAME, accessToken); + tokenManager.setToken(AUTH_USER_ID, tokenId); + tokenManager.setToken("site-id-key", SITE_ID); + tokenManager.setupRefreshTimer( + () => this.silentRefresh(), + JWT_EXPIRATION_TIME, + ); + } catch (error) { + + console.log(' login err ' , error) + throw new Error(`Login failed: ${error}`); + } + } + + + public async silentRefresh(): Promise { + console.log(' silentRefresh ') + + try { + const response = await fetch(PROXY_HOST+ + `${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, + { method: "PUT" }, + ); + + const data = await response.json(); + const accessToken = data[ACCESS_TOKEN]; + const tokenId = data[AUTH_USER_ID]; + + if (!accessToken || !tokenId) { + throw new Error("Missing required authentication tokens"); + } + + tokenManager.setToken(CLAIM_NAME, accessToken); + tokenManager.setToken(AUTH_USER_ID, tokenId); + } catch (error) { + tokenManager.clearAuthToken(); + throw new Error(`Silent refresh failed: ${error}`); + } + } +} + +export const LoginApi = LoginApiService.getInstance(); diff --git a/apps/dm/src/entities/auth/index.ts b/apps/dm/src/entities/auth/index.ts new file mode 100644 index 000000000..9dc9dd368 --- /dev/null +++ b/apps/dm/src/entities/auth/index.ts @@ -0,0 +1,2 @@ +export { LoginApi } from "./api/login-service"; +//export { AuthWrapper } from "./lib/auth-wrapper"; diff --git a/apps/dm/src/entities/auth/lib/auth-wrapper.tsx b/apps/dm/src/entities/auth/lib/auth-wrapper.tsx new file mode 100644 index 000000000..678ff50d5 --- /dev/null +++ b/apps/dm/src/entities/auth/lib/auth-wrapper.tsx @@ -0,0 +1,45 @@ +// "use client"; + +// import { useEffect } from "react"; +// import { useRouter } from "next/navigation"; +// import { LoginApi } from "../api/login-service"; + +// const AuthWrapper = ({ children }: { children: React.ReactNode }) => { +// const router = useRouter(); +// const { userInfo, setUserInfo } = useUserStore((state) => state); + +// async function authCheck() { +// const userId = getCookie(AUTH_USER_ID); +// if (!userId) { +// throw new Error("Missing required authentication tokens"); +// } + +// const response = await client.get( +// `${ENDPOINT.USER_SERVICE.USERS}/info/me`, +// ); +// if (!userInfo) setUserInfo(response.data); +// } + +// useEffect(() => { +// (async () => { +// try { +// await authCheck(); +// } catch (error) { +// console.error(error); + +// try { +// await LoginApi.silentRefresh(); +// await authCheck(); +// } catch (refreshError) { +// console.error("Token refresh failed:", refreshError); +// setUserInfo(null); +// router.push("/auth/login"); +// } +// } +// })(); +// }, []); + +// return <>{children}; +// }; + +// export { AuthWrapper }; diff --git a/apps/dm/src/entities/auth/lib/token-manager.ts b/apps/dm/src/entities/auth/lib/token-manager.ts new file mode 100644 index 000000000..40d32f33e --- /dev/null +++ b/apps/dm/src/entities/auth/lib/token-manager.ts @@ -0,0 +1,59 @@ +import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config"; +import { getCookie, removeCookie, setCookie } from "@/src/shared/lib/cookie"; + +export class TokenManager { + private static instance: TokenManager; + private refreshTimer: NodeJS.Timeout | null = null; + + private constructor() {} + + public static getInstance(): TokenManager { + if (!TokenManager.instance) { + TokenManager.instance = new TokenManager(); + } + return TokenManager.instance; + } + + public getToken(key: string): string | undefined { + return getCookie(key); + } + + public setToken(key: string, value: string): void { + setCookie(key, value, { + secure: window.location.protocol === "https:", + sameSite: "lax", + }); + } + + public removeToken(key: string): void { + removeCookie(key); + } + + public setupRefreshTimer( + callback: () => Promise, + expirationTime: number, + ): void { + if (this.refreshTimer) { + clearTimeout(this.refreshTimer); + } + + this.refreshTimer = setTimeout(() => { + callback().catch((error) => { + console.error("Silent refresh failed:", error); + this.clearAuthToken(); + }); + }, expirationTime - 60000); + } + + public clearAuthToken(): void { + if (this.refreshTimer) { + clearTimeout(this.refreshTimer); + this.refreshTimer = null; + } + + this.removeToken(CLAIM_NAME); + this.removeToken(AUTH_USER_ID); + } +} + +export const tokenManager = TokenManager.getInstance(); diff --git a/apps/dm/src/entities/auth/model/login-interface.ts b/apps/dm/src/entities/auth/model/login-interface.ts new file mode 100644 index 000000000..5aaf53044 --- /dev/null +++ b/apps/dm/src/entities/auth/model/login-interface.ts @@ -0,0 +1,9 @@ +export interface ILogin { + userId: string; + password: string; +} + +export interface LoginService { + login(payload: ILogin): Promise; + silentRefresh(): Promise; +} diff --git a/apps/dm/src/shared/config/api.ts b/apps/dm/src/shared/config/api.ts new file mode 100644 index 000000000..3eba9fa91 --- /dev/null +++ b/apps/dm/src/shared/config/api.ts @@ -0,0 +1,56 @@ +import { PROXY_HOST } from "./env"; + +export const BASE_URL = `${PROXY_HOST}/server`; + +export const ENDPOINT = { + ATTACH_SERVICE: { + TUS_UPLOAD: "/attach-service/api/v1/tus/upload", + EDITOR_UPLOAD: "/attach-service/api/v1/upload/editor", + IMAGES: "/attach-service/api/v1/images", + MOVIE_THUMBNAIL: "/attach-service/api/v1/movie/thumbnail", + ATTACHMENTS: "/attach-service/api/v1/atfl/attachments", + DOWNLOAD: "/attach-service/api/v1/atflcd/download", + STREAM_VIDEO: "/attach-service/api/v1/stream/video", + }, + USER_SERVICE: { + LOGIN: "/user-service/login", + USERS: "/user-service/api/v1/users", + SILENT_REFRESH: "/user-service/api/v1/users/token/refresh", + AUTHORIZATIONS: "/user-service/api/v1/authorizations", + ROLE_AUTHORIZATIONS: "/user-service/api/v1/role-authorizations", + ROLES: "/user-service/api/v1/roles", + }, + CMS_SERVICE: { + SITES: "/cms-service/api/v1/sites", + IPS: "/cms-service/api/v1/site-ips", + DOMAINS: "/cms-service/api/v1/site-dmns", + POSTINGS: "/cms-service/api/v1/site-pstgs", + CODE: "/cms-service/api/v1/code/cache", + MENUS: "/cms-service/api/v1/menus", + MENU_ROLES: "/cms-service/api/v1/menu-roles", + MENU_AUTHRTS: "/cms-service/api/v1/menu-authrts", + CONTENTS: "/cms-service/api/v1/contents", + CONTENTS_DETAILS: "/cms-service/api/v1/contents-details", + ORGANIZATIONS: "/cms-service/api/v1/ognzs", + USER_ORGANIZATION: "/cms-service/api/v1/user-ognz", + IMAGES: "/cms-service/api/v1/images", + BANNERS: "/cms-service/api/v1/bnrs", + TEMPLATE: "/cms-service/api/v1/tmplt", + TERMS: "/cms-service/api/v1/trms", + LWPO_CODE: "/cms-service/api/v1/lwpo-cds", + UP_CODE: "/cms-service/api/v1/up-codes", + POPUPS: "/cms-service/api/v1/popups", + SCHEDULES: "/cms-service/api/v1/schdls", + SURVEY: "/cms-service/api/v1/srvy", + SURVEY_QUESTION: "/cms-service/api/v1/srvy-qitem", + SURVEY_ARTICLE: "/cms-service/api/v1/srvy-artcl", + }, + BOARD_SERVICE: { + BBS: "/board-service/api/v1/bbs", + BBS_TEMPLATES: "/board-service/api/v1/bbsTmplt", + POSTS: "/board-service/api/v1/posts", + POSTS_COMMENTS: "/board-service/api/v1/posts-cmnts", + }, +}; + +export const JWT_EXPIRATION_TIME = 1800000; diff --git a/apps/dm/src/shared/config/env.ts b/apps/dm/src/shared/config/env.ts new file mode 100644 index 000000000..b01588df0 --- /dev/null +++ b/apps/dm/src/shared/config/env.ts @@ -0,0 +1,13 @@ +export const SITE_ID = process.env.NEXT_PUBLIC_SITE_ID || "DM"; +export const PORT = process.env.NEXT_PUBLIC_PORT || "3000"; +export const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST || `http://localhost:${PORT}`; +export const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL ||'http://con.mind-one.co.kr:11035/api/v1'; +export const SERVER_API_FO_URL = process.env.NEXT_PUBLIC_FO_SERVER_API_URL||' http://con.mind-one.co.kr:11082/api/v1'; +export const SERVER_API_WR_URL = process.env.NEXT_PUBLIC_WR_SERVER_API_URL||' http://con.mind-one.co.kr:11080/api/v1'; +export const SERVER_API_CS_URL = process.env.NEXT_PUBLIC_CS_SERVER_API_URL||'http://dev.onasys.co.kr:8000'; +export const SERVER_API_CS_TUS_URL = process.env.NEXT_PUBLIC_CS_TUS_UPLOAD ||'/attach-service/api/v1/tus/upload' +export const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization"; +export const ACCESS_TOKEN = process.env.NEXT_PUBLIC_ACCESS_TOKEN || "access-token"; +export const REFRESH_TOKEN =process.env.NEXT_PUBLIC_REFRESH_TOKEN || "refresh-token"; +export const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id"; +export const SERVER_SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL; \ No newline at end of file diff --git a/apps/dm/src/shared/config/index.ts b/apps/dm/src/shared/config/index.ts new file mode 100644 index 000000000..cd83447d5 --- /dev/null +++ b/apps/dm/src/shared/config/index.ts @@ -0,0 +1,2 @@ +export * from "./api"; +export * from "./env"; diff --git a/apps/dm/src/shared/lib/cookie.ts b/apps/dm/src/shared/lib/cookie.ts new file mode 100644 index 000000000..4bc0fe142 --- /dev/null +++ b/apps/dm/src/shared/lib/cookie.ts @@ -0,0 +1,31 @@ + +import Cookies from "js-cookie"; + + +export function getCookie(name: string): string | undefined { + if (typeof window === "undefined") { + return undefined; + } + return Cookies.get(name); +} + +export function setCookie( + name: string, + value: string, + options?: Cookies.CookieAttributes, +): void { + if (typeof window === "undefined") { + return; + } + Cookies.set(name, value, options); +} + +export function removeCookie( + name: string, + options?: Cookies.CookieAttributes, +): void { + if (typeof window === "undefined") { + return; + } + Cookies.remove(name, options); +} diff --git a/package.json b/package.json index e02227317..9e318b357 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "packages/*" ], "scripts": { - "build": "turbo run build --filter=!cs", + "build": "turbo run build", "build:common": "turbo run build --filter=@common/*", "dev": "turbo run dev", "dev:cs": "turbo run dev --filter=cs",