diff --git a/app/api/contents/update/route.ts b/app/api/contents/update/route.ts index cf09aad..2f8f622 100644 --- a/app/api/contents/update/route.ts +++ b/app/api/contents/update/route.ts @@ -28,10 +28,16 @@ function toInt(v: any): number { export async function GET(request: Request) { try { - // 세션 사용자 확인 (핸들 매핑용) - const session = await auth(); - if (!session?.user?.email) { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + const urlObj = new URL(request.url) + const hostHeader = (request.headers.get('host') || '').toLowerCase() + const isLocal = hostHeader.includes('localhost') || hostHeader.includes('127.0.0.1') || urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1' + + if (!isLocal) { + // 세션 사용자 확인 (핸들 매핑용) + const session = await auth(); + if (!session?.user?.email) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } } const { searchParams } = new URL(request.url) diff --git a/datareq/datareq.js b/datareq/datareq.js new file mode 100644 index 0000000..a7528d2 --- /dev/null +++ b/datareq/datareq.js @@ -0,0 +1,134 @@ +#!/usr/bin/env node + +// 요청 예시: https://www.everfactory.kr/api/contents/update?date=YYYYMMDD +// 사용법 +// 1) 오늘(가장 최신)부터 특정 일자까지 요청: +// node datareq/datareq.js 20251014 +// 2) 특정 최신일자부터 특정 과거일자까지 요청: +// node datareq/datareq.js 20251015 20251001 +// 환경변수로 호출 간 지연(ms) 조절: RATE_MS=500 node datareq/datareq.js 20251014 + +const BASE_URL = "http://localhost:9551/api/contents/update"; +const DEFAULT_RATE_MS = Number(process.env.RATE_MS || 400); + +function isValidYyyyMmDd(value) { + return typeof value === "string" && /^\d{8}$/.test(value); +} + +function truncateText(text, maxLen = 256) { + if (typeof text !== "string") return ""; + return text.length > maxLen ? text.slice(0, maxLen) : text; +} + +function parseYyyyMmDdToDate(yyyyMmDd) { + const year = Number(yyyyMmDd.slice(0, 4)); + const month = Number(yyyyMmDd.slice(4, 6)) - 1; // 0-based + const day = Number(yyyyMmDd.slice(6, 8)); + // Date(YYYY, M, D)는 로컬 타임존 기준. 간단성을 위해 로컬 기준 사용. + return new Date(year, month, day); +} + +function formatDateToYyyyMmDd(date) { + const y = String(date.getFullYear()); + const m = String(date.getMonth() + 1).padStart(2, "0"); + const d = String(date.getDate()).padStart(2, "0"); + return `${y}${m}${d}`; +} + +function addDays(date, delta) { + const next = new Date(date); + next.setDate(next.getDate() + delta); + return next; +} + +async function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function requestForDate(dateStr) { + const url = `${BASE_URL}?date=${dateStr}`; + try { + const res = await fetch(url, { method: "GET" }); + const text = await res.text().catch(() => ""); + if (!res.ok) { + const preview = truncateText(text, 256); + console.error(`[FAIL] ${dateStr} | ${url} -> ${res.status} ${res.statusText} ${preview ? `| ${preview}` : ""}`); + return { ok: false, status: res.status, body: text }; + } + const preview = truncateText(text, 256); + console.log(`[OK] ${dateStr} | ${url} -> ${preview || res.status}`); + return { ok: true, status: res.status, body: text }; + } catch (err) { + const errMsg = err instanceof Error ? err.message : String(err); + console.error(`[ERROR] ${dateStr} | ${url} -> ${truncateText(errMsg, 256)}`); + return { ok: false, error: err }; + } +} + +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.log("사용법: node datareq/datareq.js <끝일자YYYYMMDD> [시작(최신)일자YYYYMMDD]"); + console.log("예시1: node datareq/datareq.js 20251014"); + console.log("예시2: node datareq/datareq.js 20251015 20251001"); + process.exit(1); + } + + let newestStr; // 최신일자 (내림차순 시작점) + let oldestStr; // 종료일자 (내림차순 끝점) + + if (args.length === 1) { + const today = new Date(); + newestStr = formatDateToYyyyMmDd(today); + oldestStr = args[0]; + } else { + // args.length >= 2 인 경우: [최신일자, 종료일자] + newestStr = args[0]; + oldestStr = args[1]; + } + + if (!isValidYyyyMmDd(newestStr) || !isValidYyyyMmDd(oldestStr)) { + console.error("날짜 형식은 YYYYMMDD 여야 합니다."); + process.exit(1); + } + + let newestDate = parseYyyyMmDdToDate(newestStr); + let oldestDate = parseYyyyMmDdToDate(oldestStr); + + // 최신 >= 종료 가 아니면 자동 교환 + if (newestDate < oldestDate) { + const tmp = newestDate; + newestDate = oldestDate; + oldestDate = tmp; + newestStr = formatDateToYyyyMmDd(newestDate); + oldestStr = formatDateToYyyyMmDd(oldestDate); + } + + console.log(`요청 범위: 최신 ${newestStr} → 종료 ${oldestStr} (내림차순)`); + console.log(`요청 간 지연: ${DEFAULT_RATE_MS}ms`); + + let current = newestDate; + while (current >= oldestDate) { + const dateStr = formatDateToYyyyMmDd(current); + await requestForDate(dateStr); + // 너무 빠른 연속 호출 방지 + await sleep(DEFAULT_RATE_MS); + current = addDays(current, -1); + } + + console.log("모든 요청이 완료되었습니다."); +} + +// Node 18+ 환경 가정: fetch 전역 제공. 없으면 종료 안내. +if (typeof fetch !== "function") { + console.error("현재 Node 런타임에 fetch가 없습니다. Node 18+를 사용하거나 폴리필을 추가하세요."); + process.exit(1); +} + +main().catch((err) => { + console.error("예상치 못한 오류:", err); + process.exit(1); +}); + + diff --git a/datareq/datareq_latest.js b/datareq/datareq_latest.js new file mode 100644 index 0000000..b6d6d16 --- /dev/null +++ b/datareq/datareq_latest.js @@ -0,0 +1,39 @@ +#!/usr/bin/env node + +const BASE_URL = "http://localhost:9551/api/contents/update"; + +function truncateText(text, maxLen = 256) { + if (typeof text !== "string") return ""; + return text.length > maxLen ? text.slice(0, maxLen) : text; +} + +async function requestLatest() { + const url = `${BASE_URL}?date=latest`; + try { + const res = await fetch(url, { method: "GET" }); + const text = await res.text().catch(() => ""); + if (!res.ok) { + const preview = truncateText(text, 256); + console.error(`[FAIL] latest | ${url} -> ${res.status} ${res.statusText} ${preview ? `| ${preview}` : ""}`); + return { ok: false, status: res.status, body: text }; + } + const preview = truncateText(text, 256); + console.log(`[OK] latest | ${url} -> ${preview || res.status}`); + return { ok: true, status: res.status, body: text }; + } catch (err) { + const errMsg = err instanceof Error ? err.message : String(err); + console.error(`[ERROR] latest | ${url} -> ${truncateText(errMsg, 256)}`); + return { ok: false, error: err }; + } +} + +// Node 18+ 환경 가정: fetch 전역 제공. 없으면 종료 안내. +if (typeof fetch !== "function") { + console.error("현재 Node 런타임에 fetch가 없습니다. Node 18+를 사용하거나 폴리필을 추가하세요."); + process.exit(1); +} + +requestLatest().catch((err) => { + console.error("예상치 못한 오류:", err); + process.exit(1); +}); diff --git a/middleware.ts b/middleware.ts index 4bcdb32..e960bb0 100644 --- a/middleware.ts +++ b/middleware.ts @@ -10,10 +10,12 @@ export default auth(async (req) => { // 경로 추출 const { pathname } = req.nextUrl; + console.log("request host:", req.headers.get('host')); // 로컬 접속 확인 (특정 경로만 우회) const isLocal = req.headers.get('host')?.includes('localhost') || req.headers.get('host')?.includes('127.0.0.1'); + if (isLocal && pathname.startsWith("/api/contents")) { return NextResponse.next(); } diff --git a/parsingServer/server.js b/parsingServer/server.js index 634423d..f3b031a 100644 --- a/parsingServer/server.js +++ b/parsingServer/server.js @@ -148,6 +148,16 @@ function formatKoreanDateFromYMD(ymd) { return `${yyyy}. ${mm}. ${dd}.`; } +// '2025. 9. 1.' → '20250901' 로 변환 +function dotKoreanDateToYMD(dot) { + const m = dot?.match(/^(\d{4})\.\s*(\d{1,2})\.\s*(\d{1,2})\.?$/); + if (!m) return null; + const yyyy = m[1]; + const mm = String(parseInt(m[2], 10)).padStart(2, '0'); + const dd = String(parseInt(m[3], 10)).padStart(2, '0'); + return `${yyyy}${mm}${dd}`; +} + // 입력칸의 기존 값을 지우고 새 값 입력 async function clearAndType(inputLocator, page, value) { await inputLocator.click(); @@ -179,6 +189,13 @@ async function configureDateRangeSingleDay(page, ymdTarget) { await clearAndType(startInput, page, endVal); startVal = endVal; } else if (ymdTarget) { + // 최신 종료일(=현재 UI가 가진 가장 최근 일자)과 비교하여 이후면 에러 + const latestEndVal = await endInput.inputValue(); + const latestYmd = dotKoreanDateToYMD(latestEndVal); + if (!latestYmd) throw new Error('최신 종료일 파싱에 실패했습니다.'); + if (!/^\d{8}$/.test(ymdTarget)) throw new Error('잘못된 날짜 형식입니다. (예: 20250901)'); + if (ymdTarget > latestYmd) throw new Error(`요청한 날짜(${ymdTarget})가 최신 데이터(${latestYmd})보다 이후입니다.`); + const formatted = formatKoreanDateFromYMD(ymdTarget); if (!formatted) throw new Error(`잘못된 날짜 형식입니다. (예: 20250901)`); // 시작/종료 모두 동일 날짜로 설정