export const runtime = 'nodejs' import { NextResponse } from 'next/server' import { PrismaClient } from '@/app/generated/prisma' import { auth } from '@/auth' import fs from 'fs/promises' import path from 'path' import { parse } from 'csv-parse/sync' function parseKoreanDateToISO(dateStr: string): string | null { // 입력 예: '2025. 9. 1.' → ISO YYYY-MM-DDT00:00:00.000Z (로컬 기준 단순화) const m = dateStr?.match(/^(\d{4})\.\s*(\d{1,2})\.\s*(\d{1,2})\.?$/); if (!m) return null; const yyyy = Number(m[1]); const mm = Number(m[2]); const dd = Number(m[3]); // UTC 자정으로 맞춤 const d = new Date(Date.UTC(yyyy, mm - 1, dd, 0, 0, 0)); return d.toISOString(); } function toInt(v: any): number { if (v == null) return 0; if (typeof v === 'number') return Math.floor(v); const s = String(v).replace(/,/g, '').trim(); const n = Number(s); return Number.isFinite(n) ? Math.floor(n) : 0; } export async function GET(request: Request) { try { // 세션 사용자 확인 (핸들 매핑용) const session = await auth(); if (!session?.user?.email) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const { searchParams } = new URL(request.url) const dateParam = searchParams.get('date') || 'latest' let rows: any[] = [] let dateStr: string | undefined = undefined if (dateParam === 'files') { // Read from local CSV and use fixed date 250831 const filePath = path.join(process.cwd(), 'datas', 'to0831.csv') const csv = await fs.readFile(filePath, 'utf-8') rows = parse(csv, { columns: true, skip_empty_lines: true, bom: true, relax_column_count: true, trim: true, }) as any[] dateStr = '250831' } else { const upstream = await fetch(`http://localhost:9556/data?date=${encodeURIComponent(dateParam)}`, { method: 'GET', }) const text = await upstream.text() let data: any = null try { data = text ? JSON.parse(text) : null } catch { data = { message: text } } rows = Array.isArray(data?.data) ? data.data : [] dateStr = data?.date if (!dateStr) { return NextResponse.json({ error: 'invalid upstream payload' }, { status: 502 }) } } // 유효성 체크 if (!dateStr || rows.length === 0) { return NextResponse.json({ error: 'invalid upstream payload' }, { status: 502 }) } if (rows.length <= 1) { return NextResponse.json({ success: true, message: 'no rows to import', date: dateStr }) } // Convert date string: supports 'YYYY. M. D.' or 'YYMMDD' const parseYYMMDD = (s: string): string | null => { const m = s.match(/^(\d{2})(\d{2})(\d{2})$/) if (!m) return null const yyyy = 2000 + Number(m[1]) const mm = Number(m[2]) const dd = Number(m[3]) return new Date(Date.UTC(yyyy, mm - 1, dd, 0, 0, 0)).toISOString() } const isoDate = parseKoreanDateToISO(dateStr) || parseYYMMDD(dateStr) || new Date().toISOString(); const prisma = new PrismaClient(); try { // handle 연결은 비워둠 (요청에 따라 핸들 매핑 생략) let upserted = 0; // 2번째 행부터 반영 for (let i = 1; i < rows.length; i++) { const row = rows[i] || {}; const contentId: string | undefined = row['콘텐츠'] ?? row['contentId'] ?? row['콘텐츠 ID']; const subject: string = row['동영상 제목'] ?? row['제목'] ?? row['title'] ?? row['동영상'] ?? `content-${i}`; const publishAtRaw: string | undefined = row['동영상 게시 시간'] ?? row['게시 시간'] ?? row['publishedAt']; const publishAtDate = publishAtRaw ? new Date(publishAtRaw) : new Date(isoDate); // Content upsert: id=콘텐츠, subject=동영상 제목, pubDate=동영상 게시 시간 const upsertedContent = await prisma.content.upsert({ where: { id: String(contentId ?? `content-${i}`) }, update: { subject, pubDate: publishAtDate, }, create: { id: String(contentId ?? `content-${i}`), subject, pubDate: publishAtDate, // handle 연결 없음 }, select: { id: true }, }); const views = toInt(row['조회수']); const validViews = toInt(row['유효 조회수']); const premiumViews = toInt(row['YouTube Premium 조회수']); const watchTime = toInt(row['시청 시간(단위: 시간)']); // contentId + date 복합 유니크로 upsert await prisma.contentDayView.upsert({ where: { contentId_date: { contentId: upsertedContent.id, date: new Date(isoDate) } }, update: { views, validViews, premiumViews, watchTime }, create: { contentId: upsertedContent.id, date: new Date(isoDate), views, validViews, premiumViews, watchTime, }, }); upserted += 1; } return NextResponse.json({ success: true, date: dateStr, upserted, data: rows }) } finally { try { await (prisma as any).$disconnect() } catch {} } } catch (e) { console.error('contents/update upstream error:', e) return NextResponse.json({ error: '업스트림 요청 실패' }, { status: 502 }) } }