Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d9042ac0f | ||
|
|
95c8416754 | ||
|
|
b514bdc472 | ||
|
|
dfa35542c5 | ||
|
|
f2d7e947d7 | ||
|
|
3c65e6f8f5 | ||
|
|
1efe2dbe89 |
@@ -26,14 +26,8 @@
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
".next/types/**/*.ts",
|
||||
"../../packages/common/types/**/*.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".next",
|
||||
"dist",
|
||||
"coverage",
|
||||
"storybook-static",
|
||||
"public"
|
||||
]
|
||||
"exclude": ["node_modules", ".next", "dist", "coverage", "storybook-static", "public"]
|
||||
}
|
||||
|
||||
+5
-17
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -23,23 +19,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
".next/types/**/*.ts",
|
||||
"../../packages/common/types/**/*.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".next",
|
||||
"dist",
|
||||
"coverage",
|
||||
"storybook-static",
|
||||
"public"
|
||||
]
|
||||
"exclude": ["node_modules", ".next", "dist", "coverage", "storybook-static", "public"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import "@/app/styles/base.css";
|
||||
|
||||
export const metadata = {
|
||||
title: "유역하수도 포탈",
|
||||
};
|
||||
|
||||
export default function AuthLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="ko">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from "next";
|
||||
import { App } from "@/app";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "유역하수도 통합 정보플랫폼",
|
||||
title: "유역하수도 포탈",
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -1,17 +1,9 @@
|
||||
import { cookies } from "next/headers";
|
||||
import { NextResponse } from "next/server";
|
||||
import {
|
||||
ACCESS_TOKEN,
|
||||
AUTH_USER_ID,
|
||||
CLAIM_NAME,
|
||||
REFRESH_TOKEN,
|
||||
} from "@/shared/config";
|
||||
import { COOKIE_DOMAIN, REFRESH_TOKEN } from "@/shared/config";
|
||||
|
||||
export async function POST() {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.delete(ACCESS_TOKEN);
|
||||
cookieStore.delete(REFRESH_TOKEN);
|
||||
cookieStore.delete(AUTH_USER_ID);
|
||||
cookieStore.delete(CLAIM_NAME);
|
||||
cookieStore.delete({ name: REFRESH_TOKEN, domain: COOKIE_DOMAIN });
|
||||
return NextResponse.json({ message: "Logged out" }, { status: 200 });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
|
||||
import "@/app/styles/base.css";
|
||||
import "@/app/styles/components.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "유역하수도 포탈",
|
||||
description: "페이지가 존재하지 않습니다. 관리자에게 문의하여 주십시오.",
|
||||
};
|
||||
|
||||
export default function GlobalNotFound() {
|
||||
return (
|
||||
<html lang="ko">
|
||||
<body>
|
||||
<main className="flex min-h-screen flex-col items-center justify-center bg-[#f0f4f8] px-4 py-16">
|
||||
<div className="mb-16 text-center">
|
||||
<h1 className="text-[3.2rem] font-bold leading-snug tracking-tight">
|
||||
<span className="text-primary">유역하수도 포탈</span>에 오신 것을
|
||||
환영합니다.
|
||||
</h1>
|
||||
<p className="mt-6 text-[2rem] text-[#666]">
|
||||
오류가 발생하였습니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section className="card card-border flex w-full max-w-[72rem] flex-col items-center !rounded-2xl !py-16">
|
||||
<Image
|
||||
src="/images/magnifying-glass-with-warning.png"
|
||||
alt=""
|
||||
width={280}
|
||||
height={280}
|
||||
className="size-[28rem] object-contain"
|
||||
/>
|
||||
<p className="mt-4 text-[2.4rem] font-bold text-[#444]">
|
||||
페이지가 존재하지 않습니다.
|
||||
</p>
|
||||
<p className="mt-2 text-[1.6rem] text-[#666]">
|
||||
관리자에게 문의하여 주십시오
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ const nextConfig: NextConfig = {
|
||||
output: "standalone",
|
||||
experimental: {
|
||||
proxyTimeout: 600000,
|
||||
globalNotFound: true,
|
||||
},
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 483 KiB |
@@ -89,6 +89,7 @@ async function silentRefresh(): Promise<void> {
|
||||
}
|
||||
|
||||
async function logout(): Promise<void> {
|
||||
await fetch("/api/auth/logout", { method: "POST" });
|
||||
tokenManager.clearAuthToken();
|
||||
clearDeviceId();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import presetConfig from "../../tailwind.config.preset";
|
||||
export default {
|
||||
presets: [presetConfig],
|
||||
content: [
|
||||
"./app/**/*.{js,ts,jsx,tsx}",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"../../packages/common/components/src/ui/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -27,16 +23,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
".next/types/**/*.ts",
|
||||
"../../packages/common/types/**/*.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -27,16 +23,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
".next/types/**/*.ts",
|
||||
"../../packages/common/types/**/*.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
||||
+42
-46
@@ -1,49 +1,45 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"verbatimModuleSyntax": true,
|
||||
"strictNullChecks": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
".eslintrc.*",
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
"../../packages/common/types/**/*.d.ts"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"verbatimModuleSyntax": true,
|
||||
"strictNullChecks": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".next",
|
||||
"dist",
|
||||
"coverage",
|
||||
"storybook-static",
|
||||
"public"
|
||||
]
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".next",
|
||||
"dist",
|
||||
"coverage",
|
||||
"storybook-static",
|
||||
"public"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { getSiteInfo } from "../api/site";
|
||||
import { getIpList } from "../api/ip";
|
||||
import { ENDPOINT } from "../config";
|
||||
import { getCookie } from "../lib";
|
||||
|
||||
const siteId = getCookie("site-id-key") ?? "";
|
||||
|
||||
const ClientIpGuard = ({
|
||||
currentPath,
|
||||
children,
|
||||
}: {
|
||||
currentPath?: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const [clientIp, setClientIp] = useState("");
|
||||
const [isAllowed, setIsAllowed] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const response = await fetch("https://api.ipify.org?format=json");
|
||||
const data = await response.json();
|
||||
if (!data?.ip) return;
|
||||
setClientIp(data.ip);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (!siteId || !clientIp) return;
|
||||
|
||||
const siteInfo = await queryClient.ensureQueryData({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.SITES, siteId],
|
||||
queryFn: () => getSiteInfo(siteId),
|
||||
});
|
||||
|
||||
if (siteInfo.siteKndCd === "O") return;
|
||||
|
||||
const { content: ipList } = await getIpList({
|
||||
siteId,
|
||||
prmYn: "Y",
|
||||
});
|
||||
|
||||
const allowedClientIp = ipList.some((ip) => ip.ipAddr === clientIp);
|
||||
setIsAllowed(allowedClientIp);
|
||||
})();
|
||||
}, [queryClient, clientIp, currentPath]);
|
||||
|
||||
if (!isAllowed) {
|
||||
alert("허용되지 않은 IP로 접근하였습니다.");
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export { ClientIpGuard };
|
||||
@@ -3,7 +3,6 @@ import type { QueryClient } from "@tanstack/react-query";
|
||||
import { QueryProvider } from "../../provider/query-provider";
|
||||
import { PopupProvider } from "../../provider/popup-provider";
|
||||
import { HelpDialogProvider } from "../../provider/help-dialog-context";
|
||||
import { ClientIpGuard } from "../../lib/client-ip-guard";
|
||||
import type { MenuItem } from "../../api";
|
||||
import { SidebarProvider } from "../sidebar";
|
||||
import { LNB } from "../lnb";
|
||||
@@ -29,30 +28,28 @@ export function BaseLayout({
|
||||
}) {
|
||||
return (
|
||||
<QueryProvider queryClient={queryClient}>
|
||||
<ClientIpGuard currentPath={currentPath}>
|
||||
<PopupProvider>
|
||||
<HelpDialogProvider>
|
||||
<SidebarProvider>
|
||||
<div className="flex-1">
|
||||
<Header
|
||||
authService={authService}
|
||||
tokenManager={authService ? undefined : tokenManager}
|
||||
<PopupProvider>
|
||||
<HelpDialogProvider>
|
||||
<SidebarProvider>
|
||||
<div className="flex-1">
|
||||
<Header
|
||||
authService={authService}
|
||||
tokenManager={authService ? undefined : tokenManager}
|
||||
/>
|
||||
<div className="flex">
|
||||
<LNB
|
||||
currentPath={currentPath}
|
||||
handleMenuClick={handleMenuClick}
|
||||
/>
|
||||
<div className="flex">
|
||||
<LNB
|
||||
currentPath={currentPath}
|
||||
handleMenuClick={handleMenuClick}
|
||||
/>
|
||||
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col">
|
||||
<div className="flex-1">{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col">
|
||||
<div className="flex-1">{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
</HelpDialogProvider>
|
||||
</PopupProvider>
|
||||
</ClientIpGuard>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
</HelpDialogProvider>
|
||||
</PopupProvider>
|
||||
</QueryProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { QueryClient } from "@tanstack/react-query";
|
||||
import { QueryProvider } from "../../provider/query-provider";
|
||||
import { PopupProvider } from "../../provider/popup-provider";
|
||||
import { HelpDialogProvider } from "../../provider/help-dialog-context";
|
||||
import { ClientIpGuard } from "../../lib/client-ip-guard";
|
||||
import type { MenuItem } from "../../api";
|
||||
import { SidebarProvider } from "../sidebar";
|
||||
import { LNB } from "../lnb";
|
||||
@@ -25,26 +24,24 @@ export function NoFooterLayout({
|
||||
}) {
|
||||
return (
|
||||
<QueryProvider queryClient={queryClient}>
|
||||
<ClientIpGuard currentPath={currentPath}>
|
||||
<PopupProvider>
|
||||
<HelpDialogProvider>
|
||||
<SidebarProvider>
|
||||
<div className="flex-1">
|
||||
<Header authService={authService} />
|
||||
<div className="flex">
|
||||
<LNB
|
||||
currentPath={currentPath}
|
||||
handleMenuClick={handleMenuClick}
|
||||
/>
|
||||
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto">
|
||||
<div className="flex-1">{children}</div>
|
||||
</div>
|
||||
<PopupProvider>
|
||||
<HelpDialogProvider>
|
||||
<SidebarProvider>
|
||||
<div className="flex-1">
|
||||
<Header authService={authService} />
|
||||
<div className="flex">
|
||||
<LNB
|
||||
currentPath={currentPath}
|
||||
handleMenuClick={handleMenuClick}
|
||||
/>
|
||||
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto">
|
||||
<div className="flex-1">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
</HelpDialogProvider>
|
||||
</PopupProvider>
|
||||
</ClientIpGuard>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
</HelpDialogProvider>
|
||||
</PopupProvider>
|
||||
</QueryProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { QueryClient } from "@tanstack/react-query";
|
||||
import { QueryProvider } from "../../provider/query-provider";
|
||||
import { PopupProvider } from "../../provider/popup-provider";
|
||||
import { HelpDialogProvider } from "../../provider/help-dialog-context";
|
||||
import { ClientIpGuard } from "../../lib/client-ip-guard";
|
||||
import type { MenuItem } from "../../api";
|
||||
import { SidebarProvider } from "../sidebar";
|
||||
import { Header } from "../header";
|
||||
@@ -14,7 +13,6 @@ export function NoLNBLayout({
|
||||
children,
|
||||
queryClient,
|
||||
authService,
|
||||
currentPath,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
queryClient?: QueryClient;
|
||||
@@ -24,23 +22,21 @@ export function NoLNBLayout({
|
||||
}) {
|
||||
return (
|
||||
<QueryProvider queryClient={queryClient}>
|
||||
<ClientIpGuard currentPath={currentPath}>
|
||||
<PopupProvider>
|
||||
<HelpDialogProvider>
|
||||
<SidebarProvider>
|
||||
<div className="flex-1">
|
||||
<Header authService={authService} />
|
||||
<div className="flex">
|
||||
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto">
|
||||
<div className="flex-1">{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
<PopupProvider>
|
||||
<HelpDialogProvider>
|
||||
<SidebarProvider>
|
||||
<div className="flex-1">
|
||||
<Header authService={authService} />
|
||||
<div className="flex">
|
||||
<div className="flex min-h-[calc(100vh-50px)] flex-1 flex-col overflow-auto">
|
||||
<div className="flex-1">{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
</HelpDialogProvider>
|
||||
</PopupProvider>
|
||||
</ClientIpGuard>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
</HelpDialogProvider>
|
||||
</PopupProvider>
|
||||
</QueryProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"coverage",
|
||||
"storybook-static"
|
||||
]
|
||||
"exclude": ["node_modules", "dist", "coverage", "storybook-static"]
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
declare module "*.css";
|
||||
Reference in New Issue
Block a user