From 1e14b464a468540421e49ae91c4532e30127261a Mon Sep 17 00:00:00 2001 From: ljpark Date: Fri, 24 Oct 2025 19:40:39 +0900 Subject: [PATCH] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=86=B5=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(with-layout)/stats/hasuStat/page.tsx | 0 apps/wr/app/(without-layout)/layout.tsx | 13 ++++ .../(without-layout)/stats/hasuStat/page.tsx | 65 +++++++++++++++++++ apps/wr/app/api/client-ip/route.ts | 13 ++++ 4 files changed, 91 insertions(+) delete mode 100644 apps/wr/app/(with-layout)/stats/hasuStat/page.tsx create mode 100644 apps/wr/app/(without-layout)/layout.tsx create mode 100644 apps/wr/app/(without-layout)/stats/hasuStat/page.tsx create mode 100644 apps/wr/app/api/client-ip/route.ts diff --git a/apps/wr/app/(with-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(with-layout)/stats/hasuStat/page.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/wr/app/(without-layout)/layout.tsx b/apps/wr/app/(without-layout)/layout.tsx new file mode 100644 index 000000000..3577697f5 --- /dev/null +++ b/apps/wr/app/(without-layout)/layout.tsx @@ -0,0 +1,13 @@ +"use client"; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + + + return ( + {children} + ); +} diff --git a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx new file mode 100644 index 000000000..a0166aa30 --- /dev/null +++ b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx @@ -0,0 +1,65 @@ +"use client"; +import { useEffect, useState } from "react"; +import Link from "next/link"; + +export default function HasuStatPage() { + const [status, setStatus] = useState("checking"); + + useEffect(() => { + let didCancel = false; + + async function checkAndRedirect() { + try { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 3000); + + const res = await fetch(`/api/client-ip`, { signal: controller.signal }); + clearTimeout(timeout); + + if (!res.ok) throw new Error("no-ip"); + const data = await res.json(); + const ip: string = data?.ip || ""; + console.log("Client IP:", ip); + if (didCancel) return; + + const is10 = /^10\./.test(ip); + setStatus(is10 ? "redirect-internal" : "redirect-root"); + + // // Use location.replace so user can't go back to this page + // if (is10) { + // // redirect to internal IP + // // window.location.replace("http://10.101.163.102:8888/cms/lay1/WS10000014/program.do"); + // window.open('http://10.101.163.102:8888/cms/lay1/WS10000014/program.do', '_blank'); + // } else { + // //window.location.replace("https://www.hasudoinfo.or.kr/cms/lay1/WS10000014/program.do"); + // window.open('https://www.hasudoinfo.or.kr/cms/lay1/WS10000014/program.do', '_blank'); + // } + } catch { + // If anything fails, fall back to root + if (!didCancel) { + setStatus("error-fallback"); + window.location.replace("/"); + } + } + } + + checkAndRedirect(); + + return () => { + didCancel = true; + }; + }, []); + + return ( +
+ {status === "checking" &&
접속 IP 확인 중...
} + {status === "redirect-internal" &&
내부망으로 이동합니다...
} + {status === "redirect-root" &&
외부에서 접속하여 루트로 이동합니다...
} + {status === "error-fallback" && ( +
+ 문제가 발생하여 루트로 이동합니다. 이동하지 않으면 여기를 클릭하세요. +
+ )} +
+ ); +} diff --git a/apps/wr/app/api/client-ip/route.ts b/apps/wr/app/api/client-ip/route.ts new file mode 100644 index 000000000..f883ba9d1 --- /dev/null +++ b/apps/wr/app/api/client-ip/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from 'next/server'; + +export async function GET(request: Request) { + const headers = request.headers; + // Prefer x-forwarded-for (may be comma separated list) + const xff = headers.get('x-forwarded-for') ?? headers.get('x-real-ip') ?? ''; + let ip = ''; + if (xff && xff.length > 0) { + ip = String(xff).split(',')[0].trim(); + } + + return NextResponse.json({ ip }); +}