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") # Serve static files (CSS, images) app.mount("/static", StaticFiles(directory="app/static"), name="static") # add somewhere near the top-level constants EMAIL_REGEX = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") # SMTP / email configuration via environment 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_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}) @app.post("/submit") async def handle_form( request: Request, name: str = Form(...), lastname: str = Form(...), title: Optional[str] = Form(None), 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), ): # 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.", ) 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 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["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) server.send_message(msg, from_addr=SENDER_MAIL, to_addrs=[MAIL_TO]) return RedirectResponse("/", status_code=303)