Merge remote-tracking branch 'origin/main' into iipbs/wr/dev

This commit is contained in:
2026-07-21 11:38:33 +09:00
19 changed files with 159 additions and 151 deletions
+1 -12
View File
@@ -68,7 +68,7 @@ async function handler(
cookieStore.set(REFRESH_TOKEN, refreshToken, { cookieStore.set(REFRESH_TOKEN, refreshToken, {
httpOnly: true, httpOnly: true,
sameSite: "strict", sameSite: "strict",
domain: getCookieDomain(), domain: COOKIE_DOMAIN,
path: "/", path: "/",
}); });
} }
@@ -92,14 +92,3 @@ export async function PUT(request: Request, props: { params: Promise<{ url: stri
const params = await props.params; const params = await props.params;
return handler(request, params, "PUT"); return handler(request, params, "PUT");
} }
function getCookieDomain(): string | undefined {
if (typeof window === "undefined") return undefined;
if (COOKIE_DOMAIN) return COOKIE_DOMAIN;
const { hostname } = window.location;
if (hostname === "localhost" || hostname === "127.0.0.1") return undefined;
return hostname;
}
+1
View File
@@ -1,6 +1,7 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: "standalone",
reactStrictMode: true, reactStrictMode: true,
transpilePackages: ["@common/assets"], transpilePackages: ["@common/assets"],
}; };
+2
View File
@@ -2,6 +2,7 @@ import {
ACCESS_TOKEN, ACCESS_TOKEN,
AUTH_USER_ID, AUTH_USER_ID,
CLAIM_NAME, CLAIM_NAME,
COOKIE_DOMAIN,
REFRESH_TOKEN, REFRESH_TOKEN,
SERVER_API_URL, SERVER_API_URL,
SITE_ID, SITE_ID,
@@ -135,6 +136,7 @@ async function handler(
cookieStore.set(REFRESH_TOKEN, refreshToken, { cookieStore.set(REFRESH_TOKEN, refreshToken, {
httpOnly: true, httpOnly: true,
sameSite: "lax", sameSite: "lax",
domain: COOKIE_DOMAIN,
path: "/", path: "/",
}); });
} }
+1 -12
View File
@@ -70,7 +70,7 @@ async function handler(
response.cookies.set(REFRESH_TOKEN, refreshToken, { response.cookies.set(REFRESH_TOKEN, refreshToken, {
httpOnly: true, httpOnly: true,
sameSite: "strict", sameSite: "strict",
domain: getCookieDomain(), domain: COOKIE_DOMAIN,
path: "/", path: "/",
}); });
} }
@@ -105,14 +105,3 @@ export async function PUT(
const params = await props.params; const params = await props.params;
return handler(request, params, "PUT"); return handler(request, params, "PUT");
} }
function getCookieDomain(): string | undefined {
if (typeof window === "undefined") return undefined;
if (COOKIE_DOMAIN) return COOKIE_DOMAIN;
const { hostname } = window.location;
if (hostname === "localhost" || hostname === "127.0.0.1") return undefined;
return hostname;
}
+4 -4
View File
@@ -41,7 +41,7 @@ export function middleware(req: NextRequest) {
logMessages.push(` Rewritten URL: ${newUrl}`); logMessages.push(` Rewritten URL: ${newUrl}`);
logMessages.push("\n--------------------------------\n"); logMessages.push("\n--------------------------------\n");
// console.log(logMessages.join("\n")); console.log(logMessages.join("\n"));
const response = NextResponse.rewrite(new URL(newUrl)); const response = NextResponse.rewrite(new URL(newUrl));
return addSecurityHeaders(response); return addSecurityHeaders(response);
@@ -58,7 +58,7 @@ export function middleware(req: NextRequest) {
logMessages.push(` Rewritten URL: ${newUrl}`); logMessages.push(` Rewritten URL: ${newUrl}`);
logMessages.push("\n--------------------------------\n"); logMessages.push("\n--------------------------------\n");
// console.log(logMessages.join("\n")); console.log(logMessages.join("\n"));
const response = NextResponse.rewrite(new URL(newUrl), { const response = NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders, headers: requestHeaders,
@@ -83,7 +83,7 @@ export function middleware(req: NextRequest) {
logMessages.push(` Link API Rewritten URL: ${newUrl}`); logMessages.push(` Link API Rewritten URL: ${newUrl}`);
logMessages.push("\n--------------------------------\n"); logMessages.push("\n--------------------------------\n");
// console.log(logMessages.join("\n")); console.log(logMessages.join("\n"));
const response = NextResponse.rewrite(new URL(newUrl), { const response = NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders, headers: requestHeaders,
@@ -97,7 +97,7 @@ export function middleware(req: NextRequest) {
} }
logMessages.push("\n--------------------------------\n"); logMessages.push("\n--------------------------------\n");
// console.log(logMessages.join("\n")); console.log(logMessages.join("\n"));
const response = NextResponse.next(); const response = NextResponse.next();
return addSecurityHeaders(response); return addSecurityHeaders(response);
+2
View File
@@ -5,6 +5,7 @@ import {
ACCESS_TOKEN, ACCESS_TOKEN,
AUTH_USER_ID, AUTH_USER_ID,
CLAIM_NAME, CLAIM_NAME,
COOKIE_DOMAIN,
REFRESH_TOKEN, REFRESH_TOKEN,
SERVER_API_URL, SERVER_API_URL,
SITE_ID, SITE_ID,
@@ -75,6 +76,7 @@ async function handler(
cookieStore.set(REFRESH_TOKEN, refreshToken, { cookieStore.set(REFRESH_TOKEN, refreshToken, {
httpOnly: true, httpOnly: true,
sameSite: "lax", sameSite: "lax",
domain: COOKIE_DOMAIN,
path: "/", path: "/",
}); });
} }
+37 -37
View File
@@ -1,47 +1,47 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const subDomain = const subDomain =
process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13000"; process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13000";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
// output: "standalone", output: "standalone",
async headers() { async headers() {
return [ return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy",
value: "img-src 'self' data: https: blob:;",
},
],
},
];
},
async rewrites() {
return {
beforeFiles: [
{
source: "/_next/static/:path*",
destination: `${subDomain}/_next/static/:path*`,
has: [
{ {
type: "header", source: "/(.*)",
key: "referer", headers: [
value: "(.*)/cms(.*)", {
key: "Content-Security-Policy",
value: "img-src 'self' data: https: blob:;",
},
],
}, },
], ];
}, },
], async rewrites() {
afterFiles: [ return {
{ beforeFiles: [
source: "/cms/:path*", {
destination: `${subDomain}/cms/:path*`, source: "/_next/static/:path*",
}, destination: `${subDomain}/_next/static/:path*`,
], has: [
fallback: [], {
}; type: "header",
}, key: "referer",
value: "(.*)/cms(.*)",
},
],
},
],
afterFiles: [
{
source: "/cms/:path*",
destination: `${subDomain}/cms/:path*`,
},
],
fallback: [],
};
},
}; };
export default nextConfig; export default nextConfig;
@@ -84,7 +84,7 @@ export function ScheduleInfoPanel({ params }: ScheduleInfoPanelProps) {
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between rounded-sm border border-border p-3"> <div className="flex items-center justify-between rounded-sm border border-border p-3">
<Badge size="lg" className="font-semibold"> <Badge size="lg" className="font-semibold">
{data.inptSeCd}차 입력기간 입력기간
</Badge> </Badge>
<p className="text-2xl"> <p className="text-2xl">
{dayjs(data.inptBgngYmd).format( {dayjs(data.inptBgngYmd).format(
@@ -102,7 +102,7 @@ export function ScheduleInfoPanel({ params }: ScheduleInfoPanelProps) {
color="gray" color="gray"
className="font-semibold" className="font-semibold"
> >
{data.vrfcSeCd}차 검증기간 검증기간
</Badge> </Badge>
<p className="text-2xl"> <p className="text-2xl">
{dayjs(data.vrfcBgngYmd).format( {dayjs(data.vrfcBgngYmd).format(
+19 -23
View File
@@ -1,35 +1,31 @@
import { getApiClient } from "./client";
import { getConfig } from "@common/config"; import { getConfig } from "@common/config";
import { ENDPOINT } from "../config"; import { ENDPOINT } from "../config";
import { getAuthHeaders } from "../lib/auth-header";
export interface Banner { export interface Banner {
bnrSn: number; bnrSn: number;
bnrTypeCd: string; bnrTypeCd: string;
bnrTypeNm: string; bnrTypeNm: string;
bnrTtl: string; bnrTtl: string;
lnkgUrlAddr: string; lnkgUrlAddr: string;
useYn: string; useYn: string;
atflCd: string; atflCd: string;
mdfcnDt: string; mdfcnDt: string;
mdfr: string; mdfr: string;
mdfrNm: string; mdfrNm: string;
} }
const { PROXY_HOST } = getConfig();
const BASE_URL = `${PROXY_HOST}/server`;
const { PROXY_HOST } = getConfig(); export const getBannerList = async (siteId: string): Promise<Banner[]> => {
const BASE_URL = `${PROXY_HOST}/server`; const response = await fetch(
const client = getApiClient();
export const getBannerList = async (siteId: string) => {
const response = await client.get<Banner[]>(
`${BASE_URL}${ENDPOINT.CMS_SERVICE.BANNERS}/${siteId}`, `${BASE_URL}${ENDPOINT.CMS_SERVICE.BANNERS}/${siteId}`,
{ {
method: "GET", method: "GET",
headers: { headers: getAuthHeaders(),
"Content-Type": "application/json", credentials: "include",
},
}, },
); );
return response.data; return response.json();
}; };
+23 -13
View File
@@ -1,4 +1,4 @@
import { getApiClient } from "./client"; import { getAuthHeaders } from "../lib/auth-header";
import { ENDPOINT } from "../config"; import { ENDPOINT } from "../config";
import { getConfig } from "@common/config"; import { getConfig } from "@common/config";
@@ -27,22 +27,33 @@ export interface TreeMenu extends MenuItem {
menuHlpCn: string; menuHlpCn: string;
} }
const client = getApiClient();
const { PROXY_HOST } = getConfig(); const { PROXY_HOST } = getConfig();
const BASE_URL = `${PROXY_HOST}/server`; const BASE_URL = `${PROXY_HOST}/server`;
export const getMenuList = async (siteId: string) => { export const getMenuList = async (siteId: string): Promise<MenuItem[]> => {
const response = await client.get<MenuItem[]>( const response = await fetch(
`${ENDPOINT.CMS_SERVICE.MENU_ROLES}/${siteId}`, `${BASE_URL}${ENDPOINT.CMS_SERVICE.MENU_ROLES}/${siteId}`,
{
method: "GET",
headers: getAuthHeaders(),
credentials: "include",
},
); );
return response.data; return response.json();
}; };
export const getPreviewTreeMenuList = async (siteId: string) => { export const getPreviewTreeMenuList = async (
const response = await client.get<TreeMenu[]>( siteId: string,
`${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree/preview`, ): Promise<TreeMenu[]> => {
const response = await fetch(
`${BASE_URL}${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree/preview`,
{
method: "GET",
headers: getAuthHeaders(),
credentials: "include",
},
); );
return response.data; return response.json();
}; };
export const getAnonymouseMenuList = async ( export const getAnonymouseMenuList = async (
@@ -52,9 +63,8 @@ export const getAnonymouseMenuList = async (
`${BASE_URL}${ENDPOINT.CMS_SERVICE.MENU_ANONYMOUS}/user/${siteId}`, `${BASE_URL}${ENDPOINT.CMS_SERVICE.MENU_ANONYMOUS}/user/${siteId}`,
{ {
method: "GET", method: "GET",
headers: { headers: getAuthHeaders(),
"Content-Type": "application/json", credentials: "include",
},
}, },
); );
return response.json(); return response.json();
+10 -17
View File
@@ -1,6 +1,6 @@
import { getApiClient } from "./client";
import { getConfig } from "@common/config"; import { getConfig } from "@common/config";
import { ENDPOINT } from "../config"; import { ENDPOINT } from "../config";
import { getAuthHeaders } from "../lib/auth-header";
export interface SiteInfo { export interface SiteInfo {
siteId: string; siteId: string;
@@ -62,25 +62,18 @@ export const getSiteInfo = async (siteId: string) => {
`${BASE_URL}${ENDPOINT.CMS_SERVICE.SITES}/${siteId}`, `${BASE_URL}${ENDPOINT.CMS_SERVICE.SITES}/${siteId}`,
{ {
method: "GET", method: "GET",
headers: { headers: getAuthHeaders(),
"Content-Type": "application/json", credentials: "include",
},
}, },
); );
return response.json() as Promise<SiteInfo>; return response.json() as Promise<SiteInfo>;
}; };
const client = getApiClient(); export const getSiteList = async (): Promise<SiteListResponse> => {
const response = await fetch(`${BASE_URL}${ENDPOINT.CMS_SERVICE.SITE_LIST}`, {
export const getSiteList = async () => { method: "GET",
const response = await client.get<SiteListResponse>( headers: getAuthHeaders(),
`${BASE_URL}${ENDPOINT.CMS_SERVICE.SITE_LIST}`, credentials: "include",
{ });
method: "GET", return response.json();
headers: {
"Content-Type": "application/json",
},
},
);
return response.data;
}; };
@@ -0,0 +1,22 @@
import Cookies from "js-cookie";
import { getConfig } from "@common/config";
export function getAuthHeaders(): HeadersInit {
const { CLAIM_NAME, AUTH_USER_ID } = getConfig();
if (!CLAIM_NAME || !AUTH_USER_ID) {
throw new Error("Required environment variables are not set");
}
const headers: Record<string, string> = {
"Content-Type": "application/json",
};
const token = Cookies.get(CLAIM_NAME);
const userId = Cookies.get(AUTH_USER_ID);
if (token) headers[CLAIM_NAME] = token;
if (userId) headers[AUTH_USER_ID] = userId;
return headers;
}
@@ -12,8 +12,6 @@ import {
CarouselPrevious, CarouselPrevious,
} from "../carousel"; } from "../carousel";
const siteId = getCookie("site-id-key") ?? "";
type BannerImageProps = { type BannerImageProps = {
atflCd: string; atflCd: string;
bnrSn: number; bnrSn: number;
@@ -64,6 +62,8 @@ const BannerImage = ({ atflCd, lnkgUrlAddr }: BannerImageProps) => {
}; };
export function Footer() { export function Footer() {
const siteId = getCookie("site-id-key") ?? "";
const { data: siteInfo } = useQuery({ const { data: siteInfo } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SITES, siteId], queryKey: [ENDPOINT.CMS_SERVICE.SITES, siteId],
queryFn: () => getSiteInfo(siteId), queryFn: () => getSiteInfo(siteId),
@@ -29,7 +29,6 @@ import { ScrollArea, ScrollBar } from "../scroll-area";
const appConfig = getConfig(); const appConfig = getConfig();
const userId = getCookie(appConfig.AUTH_USER_ID ?? ""); const userId = getCookie(appConfig.AUTH_USER_ID ?? "");
const siteId = getCookie("site-id-key") ?? "";
const isLoggedIn = !!userId; const isLoggedIn = !!userId;
function getRootDomain(hostname: string): string { function getRootDomain(hostname: string): string {
@@ -144,6 +143,7 @@ export function Header({
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const logoRef = useRef<HTMLImageElement>(null); const logoRef = useRef<HTMLImageElement>(null);
const remainingMs = useTokenRemainingMs(mounted && isLoggedIn && !viewOnly); const remainingMs = useTokenRemainingMs(mounted && isLoggedIn && !viewOnly);
const siteId = getCookie("site-id-key") ?? "";
useSessionExtendConfirm({ useSessionExtendConfirm({
enabled: enabled:
@@ -8,15 +8,18 @@ import { SidebarProvider } from "../sidebar";
import { LNB } from "../lnb"; import { LNB } from "../lnb";
import { Header } from "../header"; import { Header } from "../header";
import "./common.css"; import "./common.css";
import type { AuthService } from "@common/auth";
export function NoFooterLayout({ export function NoFooterLayout({
children, children,
queryClient, queryClient,
authService,
currentPath, currentPath,
handleMenuClick, handleMenuClick,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
queryClient?: QueryClient; queryClient?: QueryClient;
authService?: AuthService;
currentPath?: string; currentPath?: string;
handleMenuClick?: (item: MenuItem) => void; handleMenuClick?: (item: MenuItem) => void;
}) { }) {
@@ -26,19 +29,19 @@ export function NoFooterLayout({
<PopupProvider> <PopupProvider>
<HelpDialogProvider> <HelpDialogProvider>
<SidebarProvider> <SidebarProvider>
<div className="flex-1"> <div className="flex-1">
<Header /> <Header authService={authService} />
<div className="flex"> <div className="flex">
<LNB <LNB
currentPath={currentPath} currentPath={currentPath}
handleMenuClick={handleMenuClick} handleMenuClick={handleMenuClick}
/> />
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto"> <div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto">
<div className="flex-1">{children}</div> <div className="flex-1">{children}</div>
</div>
</div> </div>
</div> </div>
</div> </SidebarProvider>
</SidebarProvider>
</HelpDialogProvider> </HelpDialogProvider>
</PopupProvider> </PopupProvider>
</ClientIpGuard> </ClientIpGuard>
@@ -6,7 +6,6 @@ import { HelpDialogProvider } from "../../provider/help-dialog-context";
import { ClientIpGuard } from "../../lib/client-ip-guard"; import { ClientIpGuard } from "../../lib/client-ip-guard";
import type { MenuItem } from "../../api"; import type { MenuItem } from "../../api";
import { SidebarProvider } from "../sidebar"; import { SidebarProvider } from "../sidebar";
import { LNB } from "../lnb";
import { Header } from "../header"; import { Header } from "../header";
import { Footer } from "../footer"; import { Footer } from "../footer";
import "./common.css"; import "./common.css";
@@ -17,7 +16,6 @@ export function NoLNBLayout({
authService, authService,
tokenManager, tokenManager,
currentPath, currentPath,
handleMenuClick,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
queryClient?: QueryClient; queryClient?: QueryClient;
@@ -33,19 +31,19 @@ export function NoLNBLayout({
<PopupProvider> <PopupProvider>
<HelpDialogProvider> <HelpDialogProvider>
<SidebarProvider> <SidebarProvider>
<div className="flex-1"> <div className="flex-1">
<Header <Header
authService={authService} authService={authService}
tokenManager={authService ? undefined : tokenManager} tokenManager={authService ? undefined : tokenManager}
/> />
<div className="flex"> <div className="flex">
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto"> <div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto">
<div className="flex-1">{children}</div> <div className="flex-1">{children}</div>
<Footer /> <Footer />
</div>
</div> </div>
</div> </div>
</div> </SidebarProvider>
</SidebarProvider>
</HelpDialogProvider> </HelpDialogProvider>
</PopupProvider> </PopupProvider>
</ClientIpGuard> </ClientIpGuard>
@@ -42,7 +42,6 @@ import {
const appConfig = getConfig(); const appConfig = getConfig();
const userId = getCookie(appConfig.AUTH_USER_ID ?? ""); const userId = getCookie(appConfig.AUTH_USER_ID ?? "");
const appSiteId = getCookie("site-id-key") ?? "";
const isLoggedIn = !!userId; const isLoggedIn = !!userId;
export function LNB({ export function LNB({
@@ -74,6 +73,7 @@ export function LNB({
helpContent, helpContent,
} = useHelpDialog(); } = useHelpDialog();
const appSiteId = getCookie("site-id-key") ?? "";
const siteId = propSiteId ?? appSiteId; const siteId = propSiteId ?? appSiteId;
const { data: menuListData } = useQuery({ const { data: menuListData } = useQuery({
+5 -2
View File
@@ -247,9 +247,9 @@ importers:
'@common/components': '@common/components':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/common/components version: link:../../packages/common/components
'@common/config': '@common/network':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/common/config version: link:../../packages/common/network
'@dnd-kit/core': '@dnd-kit/core':
specifier: ^6.3.1 specifier: ^6.3.1
version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -1042,6 +1042,9 @@ importers:
js-cookie: js-cookie:
specifier: ^3.0.5 specifier: ^3.0.5
version: 3.0.5 version: 3.0.5
lucide-react:
specifier: ^0.474.0
version: 0.474.0(react@19.2.4)
moment: moment:
specifier: ^2.30.1 specifier: ^2.30.1
version: 2.30.1 version: 2.30.1