diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b16def2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.11-slim + +WORKDIR /app + +# 필요한 패키지 설치 +RUN pip install --no-cache-dir fastapi uvicorn pydantic pydantic[email] + +# 앱 파일 복사 +COPY mail_contact.py . + +# 포트 8001 노출 +EXPOSE 8001 + +# 앱 실행 +CMD ["python", "mail_contact.py"] diff --git a/docker-compose.yml b/docker-compose.yml index 2cf583c..7b65e9a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,3 +20,22 @@ services: restart: unless-stopped volumes: - ./html:/usr/share/nginx/html:ro + + # 3. Contact Form Email Handler + mail-contact: + build: . + container_name: hanmo-mail-contact + restart: unless-stopped + ports: + - '8001:8001' + environment: + - PYTHONUNBUFFERED=1 + command: python mail_contact.py + networks: + - default + - mailcow-network + +networks: + mailcow-network: + external: true + name: mailcowdockerized_mailcow-network diff --git a/html/assets/js/script.js b/html/assets/js/script.js index ce1ab57..9eb4720 100644 --- a/html/assets/js/script.js +++ b/html/assets/js/script.js @@ -46,3 +46,78 @@ mobileNavLinks.forEach(link => { mobileMenu.classList.add('translate-x-full'); }); }); + +// Contact form handler +const contactSubmitBtn = document.getElementById('contact-submit'); +const contactForm = { + name: document.getElementById('contact-name'), + email: document.getElementById('contact-email'), + company: document.getElementById('contact-company'), + message: document.getElementById('contact-message') +}; +const contactStatus = document.getElementById('contact-status'); + +if (contactSubmitBtn) { + contactSubmitBtn.addEventListener('click', async (e) => { + e.preventDefault(); + + // 폼 데이터 수집 + const formData = { + name: contactForm.name.value, + email: contactForm.email.value, + company: contactForm.company.value, + message: contactForm.message.value + }; + + // 로딩 상태 표시 + contactSubmitBtn.disabled = true; + contactSubmitBtn.textContent = 'Sending...'; + contactStatus.classList.add('hidden'); + + try { + // Python FastAPI 엔드포인트로 요청 + const response = await fetch('http://localhost:8001/api/contact', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(formData), + mode: 'cors', + credentials: 'omit' + }); + + const result = await response.json(); + + // 응답 처리 + contactStatus.classList.remove('hidden'); + + if (response.ok && result.success !== false) { + // 성공 메시지 + contactStatus.classList.remove('bg-red-900/50', 'text-red-200'); + contactStatus.classList.add('bg-green-900/50', 'text-green-200'); + contactStatus.textContent = result.message; + + // 폼 초기화 + contactForm.name.value = ''; + contactForm.email.value = ''; + contactForm.company.value = ''; + contactForm.message.value = ''; + } else { + // 오류 메시지 + contactStatus.classList.remove('bg-green-900/50', 'text-green-200'); + contactStatus.classList.add('bg-red-900/50', 'text-red-200'); + contactStatus.textContent = result.detail || result.message || 'Failed to send message'; + } + } catch (error) { + // 네트워크 오류 + contactStatus.classList.remove('hidden', 'bg-green-900/50', 'text-green-200'); + contactStatus.classList.add('bg-red-900/50', 'text-red-200'); + contactStatus.textContent = 'Error: Could not send message. Please try again later.'; + console.error('Contact form error:', error); + } finally { + // 버튼 상태 복구 + contactSubmitBtn.disabled = false; + contactSubmitBtn.textContent = 'Send Message'; + } + }); +} diff --git a/html/index.html b/html/index.html index e468296..e71732e 100644 --- a/html/index.html +++ b/html/index.html @@ -399,12 +399,13 @@
- - + +
- - - + + + +
diff --git a/html/status.json b/html/status.json index 9396595..b9bae41 100644 --- a/html/status.json +++ b/html/status.json @@ -1 +1 @@ -{"cpu_temp": "40", "nvme_temp": "35", "uptime_days": 2, "last_update": "05:53:01"} \ No newline at end of file +{"cpu_temp": "43", "nvme_temp": "35", "uptime_days": 2, "last_update": "06:16:01"} \ No newline at end of file diff --git a/mail_contact.py b/mail_contact.py new file mode 100644 index 0000000..549439a --- /dev/null +++ b/mail_contact.py @@ -0,0 +1,125 @@ +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel, EmailStr, validator +from fastapi.middleware.cors import CORSMiddleware +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +import os +from typing import Optional + +app = FastAPI() + +# CORS 설정 +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +class ContactForm(BaseModel): + name: str + email: EmailStr + company: str + message: str + + @validator('name') + def name_not_empty(cls, v): + if not v or len(v.strip()) == 0: + raise ValueError('Full Name is required') + return v.strip() + + @validator('company') + def company_not_empty(cls, v): + if not v or len(v.strip()) == 0: + raise ValueError('Company Name is required') + return v.strip() + + @validator('message') + def message_valid(cls, v): + if not v or len(v.strip()) == 0: + raise ValueError('Project description is required') + if len(v) > 5000: + raise ValueError('Message is too long (max 5000 characters)') + return v.strip() + +@app.post("/api/contact") +async def send_contact_email(form: ContactForm): + """Contact form email handler""" + try: + # SMTP 설정 + smtp_server = "mailcowdockerized_postfix-mailcow_1" # Docker 네트워크 내 호스트명 + smtp_port = 25 # 일반 포트 (요청할 때) + + # 메시지 구성 + msg = MIMEMultipart('alternative') + msg['Subject'] = f"[Contact Form] {form.name} - {form.company}" + msg['From'] = form.email + msg['To'] = "windpacer@hanmocnn.co.kr" + + # 텍스트 본문 + text_body = f""" +New contact form submission: + +Name: {form.name} +Email: {form.email} +Company: {form.company} +Message: +{form.message} + +--- +Submitted from Contact Form +""" + + # HTML 본문 + html_body = f""" + + +

New contact form submission:

+

Name: {form.name}

+

Email: {form.email}

+

Company: {form.company}

+

Message:

+
{form.message}
+
+

Submitted from Hanmo Contact Form

+ + +""" + + part1 = MIMEText(text_body, 'plain') + part2 = MIMEText(html_body, 'html') + msg.attach(part1) + msg.attach(part2) + + # SMTP 연결 및 메일 전송 + with smtplib.SMTP(smtp_server, smtp_port, timeout=10) as server: + server.sendmail( + form.email, + "windpacer@hanmocnn.co.kr", + msg.as_string() + ) + + return { + "success": True, + "message": "Your message has been sent successfully. We will get back to you soon." + } + + except Exception as e: + import traceback + print(f"Error sending email: {str(e)}") + print(traceback.format_exc()) + raise HTTPException( + status_code=500, + detail="Failed to send email. Please try again later." + ) + +@app.get("/health") +async def health_check(): + """Health check endpoint""" + return {"status": "ok"} + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8001)