#!/usr/bin/env python3 """ Email API Router - Maildir + SMTP access for all RMI email accounts. Maildir is mounted from host at /var/mail/vhosts. SMTP uses host gateway for outbound delivery. """ import email import os import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel router = APIRouter(prefix="/email", tags=["email"]) SMTP_HOST = os.getenv("SMTP_HOST", "172.20.0.1") SMTP_PORT = int(os.getenv("SMTP_PORT", "25")) MAILDIR_BASE = "/var/mail/vhosts" ACCOUNTS = { "admin": "admin@rugmunch.io", "biz": "biz@rugmunch.io", "security": "security@rugmunch.io", "hello": "hello@cryptorugmunch.com", "media": "media@cryptorugmunch.com", } class SendRequest(BaseModel): sender_account: str to: str subject: str body: str def _maildir_for(account: str) -> str: addr = ACCOUNTS.get(account) if not addr: raise HTTPException(404, f"Unknown account: {account}") local, domain = addr.split("@") return f"{MAILDIR_BASE}/{domain}/{local}" def _read_msg(filepath: str, msg_id: int, full: bool = False) -> dict: with open(filepath, "rb") as f: msg = email.message_from_binary_file(f) result = { "id": msg_id, "subject": str(msg.get("Subject", "")), "sender": str(msg.get("From", "")), "recipient": str(msg.get("To", "")), "date": str(msg.get("Date", "")), } if full: body = "" if msg.is_multipart(): for part in msg.walk(): if part.get_content_type() == "text/plain": payload = part.get_payload(decode=True) if payload: body = payload.decode(errors="replace") break else: payload = msg.get_payload(decode=True) if payload: body = payload.decode(errors="replace") result["body"] = body[:10000] return result def _list_files(maildir: str) -> list: files = [] for sub in ["new", "cur"]: d = os.path.join(maildir, sub) if os.path.isdir(d): files.extend(os.path.join(d, f) for f in os.listdir(d)) files.sort(key=lambda p: os.path.getmtime(p), reverse=True) return files @router.get("/accounts") def list_accounts(): return {"accounts": dict(ACCOUNTS.items())} @router.get("/{account}/inbox") def list_inbox(account: str, limit: int = Query(20, le=100)): if account not in ACCOUNTS: raise HTTPException(404, f"Unknown account. Options: {list(ACCOUNTS.keys())}") try: maildir = _maildir_for(account) files = _list_files(maildir)[:limit] msgs = [_read_msg(f, i + 1) for i, f in enumerate(files)] return {"account": ACCOUNTS[account], "count": len(msgs), "messages": msgs} except HTTPException: raise except Exception as e: raise HTTPException(500, str(e)) @router.get("/{account}/message/{msg_id}") def read_message(account: str, msg_id: int): if account not in ACCOUNTS: raise HTTPException(404, "Unknown account") try: maildir = _maildir_for(account) files = _list_files(maildir) if msg_id < 1 or msg_id > len(files): raise HTTPException(404, "Message not found") return _read_msg(files[msg_id - 1], msg_id, full=True) except HTTPException: raise except Exception as e: raise HTTPException(500, str(e)) @router.post("/send") def send_email(req: SendRequest): if req.sender_account not in ACCOUNTS: raise HTTPException(404, "Unknown sender account") sender_addr = ACCOUNTS[req.sender_account] msg = MIMEMultipart() msg["From"] = sender_addr msg["To"] = req.to msg["Subject"] = req.subject msg.attach(MIMEText(req.body, "plain")) try: with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=10) as smtp: smtp.send_message(msg) return {"status": "sent", "from": sender_addr, "to": req.to} except Exception as e: raise HTTPException(500, f"Send failed: {e}")