feat(web): Excalidraw 스케치 스타일 리디자인 + 문의 폼
- 웹페이지를 Excalidraw 스타일 손그림 디자인으로 전면 리디자인 - 라이트 모드 + 크림색 배경 + 격자 패턴 - 손글씨 폰트 (제목: Caveat, 본문: Noto Sans KR) - 스케치 스타일 카드, 버튼, 스티커 노트 컴포넌트 - 문의 폼 추가 (이메일 + 메시지) - /api/contact 엔드포인트 추가 (텔레그램 알림 연동) - 이메일 실시간 유효성 검사 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
80
src/index.ts
80
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),
|
||||
`📬 <b>웹사이트 문의</b>\n\n` +
|
||||
`📧 이메일: <code>${body.email}</code>\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') {
|
||||
// 보안 검증
|
||||
|
||||
836
web/index.html
836
web/index.html
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user