feat: add landing page, enable mail-to-terminal, show success message
Some checks failed
Docker Build (PR) / Build Docker image (pull_request) Failing after 3m19s

closes #11
This commit is contained in:
2025-11-19 13:22:30 +01:00
parent 5e37b57c9b
commit 4738732517
4 changed files with 531 additions and 50 deletions

View File

@@ -2,16 +2,13 @@ import os
import re
import smtplib
from email.mime.text import MIMEText
from typing import Optional
from xml.etree.ElementTree import Element, SubElement, tostring
from dotenv import load_dotenv
from fastapi import FastAPI, Form, HTTPException, Request, status
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
load_dotenv()
app = FastAPI()
templates = Jinja2Templates(directory="app/templates")
@@ -22,22 +19,23 @@ app.mount("/static", StaticFiles(directory="app/static"), name="static")
EMAIL_REGEX = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
# SMTP / email configuration via environment
MAIL_ENABLED = os.getenv("MAIL_ENABLED", "false").lower() == "true"
SMTP_HOST = os.getenv("SMTP_HOST", "smtp")
SMTP_PORT = int(os.getenv("SMTP_PORT", "25"))
SENDER_MAIL = os.getenv("SENDER_MAIL", "noreply@example.com")
MAIL_FROM = os.getenv("MAIL_FROM", "noreply@example.com")
MAIL_TO = os.getenv("MAIL_TO", "destination@example.com")
MAIL_USERNAME = os.getenv("MAIL_USERNAME")
MAIL_PASSWORD = os.getenv("MAIL_PASSWORD")
print(
f"Using SMTP server {SMTP_HOST}:{SMTP_PORT} with user {MAIL_USERNAME}"
f" to send from {SENDER_MAIL} to {MAIL_TO}"
)
@app.get("/", response_class=HTMLResponse)
async def show_form(request: Request):
return templates.TemplateResponse("form.html", {"request": request})
async def landing_page(request: Request):
"""Landing page with service selection"""
return templates.TemplateResponse("index.html", {"request": request})
@app.get("/semesterapparat", response_class=HTMLResponse)
async def semesterapparat_form(request: Request):
"""Semesterapparat form page"""
return templates.TemplateResponse("semesterapparat_form.html", {"request": request})
@app.post("/submit")
@@ -45,67 +43,67 @@ async def handle_form(
request: Request,
name: str = Form(...),
lastname: str = Form(...),
title: Optional[str] = Form(None),
title: str = Form(...),
telno: str = Form(...),
mail: str = Form(...),
apparatsname: str = Form(...),
subject: str = Form(...),
semester_type: str = Form(...), # "summer" or "winter"
semester_year: str = Form(...),
dauerapparat: bool = Form(False),
authorname: list[str] = Form(...),
year: list[str] = Form(...),
booktitle: list[str] = Form(...),
signature: Optional[list[str]] = Form(None),
signature: list[str] = Form(...),
message: str = Form(default=""),
):
# Build XML
root = Element("form_submission")
static_data = SubElement(root, "static")
SubElement(static_data, "name").text = name
SubElement(static_data, "lastname").text = lastname
# Title is optional
SubElement(static_data, "title").text = title or ""
SubElement(static_data, "telno").text = telno
SubElement(static_data, "mail").text = mail
SubElement(static_data, "apparatsname").text = apparatsname
SubElement(static_data, "subject").text = subject
SubElement(static_data, "semester").text = f"{semester_type} {semester_year}"
SubElement(static_data, "dauerapparat").text = "true" if dauerapparat else "false"
# inside handle_form(), right after parameters are received, before building XML:
# Basic email validation (server-side)
if not EMAIL_REGEX.match(mail):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid email address format.",
)
# Build XML
root = Element("form_submission")
static_data = SubElement(root, "static")
SubElement(static_data, "name").text = name
SubElement(static_data, "lastname").text = lastname
SubElement(static_data, "title").text = title
SubElement(static_data, "telno").text = telno
SubElement(static_data, "mail").text = mail
SubElement(static_data, "apparatsname").text = apparatsname
SubElement(static_data, "subject").text = subject
SubElement(static_data, "semester").text = f"{semester_type} {semester_year}"
if message:
SubElement(static_data, "message").text = message
books = SubElement(root, "books")
for i in range(len(authorname)):
book = SubElement(books, "book")
SubElement(book, "authorname").text = authorname[i]
SubElement(book, "year").text = year[i]
SubElement(book, "title").text = booktitle[i]
# Signature is optional; write empty string if not provided
sig_value = ""
if signature and i < len(signature):
sig_raw = signature[i] or ""
sig_value = sig_raw.strip()
SubElement(book, "signature").text = sig_value
SubElement(book, "signature").text = signature[i]
xml_data = tostring(root, encoding="unicode")
# Send mail
msg = MIMEText(xml_data, "xml")
msg["Subject"] = "Antrag für neuen Semesterapparat"
msg["From"] = SENDER_MAIL
msg["Subject"] = "New Form Submission"
msg["From"] = MAIL_FROM
msg["To"] = MAIL_TO
with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT) as server:
server.connect(SMTP_HOST, SMTP_PORT)
# Login only if credentials are provided
if MAIL_USERNAME and MAIL_PASSWORD:
server.login(MAIL_USERNAME, MAIL_PASSWORD)
if MAIL_ENABLED:
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.send_message(msg)
else:
print("=" * 80)
print("MAIL SENDING DISABLED - Would have sent:")
print(f"From: {MAIL_FROM}")
print(f"To: {MAIL_TO}")
print(f"Subject: {msg['Subject']}")
print("-" * 80)
print(xml_data)
print("=" * 80)
server.send_message(msg, from_addr=SENDER_MAIL, to_addrs=[MAIL_TO])
return RedirectResponse("/", status_code=303)
return RedirectResponse("/?success=true", status_code=303)