import sqlite3 import os from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel DB_PATH = os.environ.get("DB_PATH", "/srv/form-backend/submissions.db") def get_db(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row conn.execute("PRAGMA journal_mode=WAL") return conn @asynccontextmanager async def lifespan(app: FastAPI): db = get_db() db.execute(""" CREATE TABLE IF NOT EXISTS submissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL, name TEXT NOT NULL, domain TEXT NOT NULL, created_at TEXT NOT NULL ) """) db.commit() db.close() yield app = FastAPI(title="Form Backend", lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) class Submission(BaseModel): email: str name: str domain: str @app.post("/api/submit", status_code=201) def submit(data: Submission): if not data.email or "@" not in data.email: return {"error": "Invalid email"}, 422 if not data.name.strip(): return {"error": "Name required"}, 422 if not data.domain.strip(): return {"error": "Domain required"}, 422 db = get_db() cur = db.execute( "INSERT INTO submissions (email, name, domain, created_at) VALUES (?, ?, ?, datetime(\"now\"))", (data.email.strip(), data.name.strip(), data.domain.strip()) ) db.commit() row_id = cur.lastrowid db.close() return {"ok": True, "id": row_id} @app.get("/api/submissions") def list_submissions(): db = get_db() rows = db.execute("SELECT id, email, name, domain, created_at FROM submissions ORDER BY id DESC LIMIT 100").fetchall() db.close() return [dict(r) for r in rows] @app.get("/health") def health(): return {"status": "ok"}