From 2e4886a0a7c1d3f1a87a1a9af067319b2ec12ed8 Mon Sep 17 00:00:00 2001 From: kappa Date: Mon, 19 Jan 2026 09:31:41 +0900 Subject: [PATCH] =?UTF-8?q?feat(web):=20Excalidraw=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=B9=98=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=A6=AC=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20+=20=EB=AC=B8=EC=9D=98=20=ED=8F=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 웹페이지를 Excalidraw 스타일 손그림 디자인으로 전면 리디자인 - 라이트 모드 + 크림색 배경 + 격자 패턴 - 손글씨 폰트 (제목: Caveat, 본문: Noto Sans KR) - 스케치 스타일 카드, 버튼, 스티커 노트 컴포넌트 - 문의 폼 추가 (이메일 + 메시지) - /api/contact 엔드포인트 추가 (텔레그램 알림 연동) - 이메일 실시간 유효성 검사 Co-Authored-By: Claude Opus 4.5 --- src/index.ts | 80 +++++ web/index.html | 836 +++++++++++++++++++++++++++++-------------------- 2 files changed, 574 insertions(+), 342 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1ebcba1..89a4c50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -465,6 +465,86 @@ export default { } } + // 문의 폼 API (웹사이트용) + if (url.pathname === '/api/contact' && request.method === 'POST') { + // CORS preflight는 OPTIONS에서 처리 + const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type', + }; + + try { + const body = await request.json() as { + email: string; + message: string; + }; + + // 필수 필드 검증 + if (!body.email || !body.message) { + return Response.json( + { error: '이메일과 메시지는 필수 항목입니다.' }, + { status: 400, headers: corsHeaders } + ); + } + + // 이메일 형식 검증 + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(body.email)) { + return Response.json( + { error: '올바른 이메일 형식이 아닙니다.' }, + { status: 400, headers: corsHeaders } + ); + } + + // 메시지 길이 제한 + if (body.message.length > 2000) { + return Response.json( + { error: '메시지는 2000자 이내로 작성해주세요.' }, + { status: 400, headers: corsHeaders } + ); + } + + // 관리자에게 텔레그램 알림 + const adminId = env.DEPOSIT_ADMIN_ID || env.DOMAIN_OWNER_ID; + if (env.BOT_TOKEN && adminId) { + const timestamp = new Date().toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' }); + await sendMessage( + env.BOT_TOKEN, + parseInt(adminId), + `📬 웹사이트 문의\n\n` + + `📧 이메일: ${body.email}\n` + + `🕐 시간: ${timestamp}\n\n` + + `💬 내용:\n${body.message}` + ); + } + + console.log(`[Contact] 문의 수신: ${body.email}`); + + return Response.json( + { success: true, message: '문의가 성공적으로 전송되었습니다.' }, + { headers: corsHeaders } + ); + } catch (error) { + console.error('[Contact] 오류:', error); + return Response.json( + { error: '문의 전송 중 오류가 발생했습니다.' }, + { status: 500, headers: corsHeaders } + ); + } + } + + // CORS preflight for contact API + if (url.pathname === '/api/contact' && request.method === 'OPTIONS') { + return new Response(null, { + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type', + }, + }); + } + // Telegram Webhook 처리 if (url.pathname === '/webhook') { // 보안 검증 diff --git a/web/index.html b/web/index.html index 5f7f02e..b7aefdd 100644 --- a/web/index.html +++ b/web/index.html @@ -5,102 +5,213 @@ Anvil Hosting - 안정적인 호스팅 서비스 + - + - + -