Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import json | |
| import os | |
| from pathlib import Path | |
| from typing import Any, Dict | |
| from dotenv import load_dotenv | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import FileResponse, PlainTextResponse | |
| from backend.worker.gmail_client import GmailClient | |
| app = FastAPI(title="PDF Trainer API", version="1.0") | |
| # Allow Vite dev server | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=[ | |
| "http://localhost:5173", | |
| "http://127.0.0.1:5173", | |
| ], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| REPO_ROOT = Path(__file__).resolve().parents[1] | |
| BACKEND_DIR = REPO_ROOT / "backend" | |
| UPLOADS_DIR = BACKEND_DIR / "worker" / "uploads" | |
| # Load backend/.env explicitly ONCE for this process | |
| load_dotenv(BACKEND_DIR / ".env", override=True) | |
| CREDENTIALS_JSON = Path(os.environ.get("GMAIL_CREDENTIALS_JSON", str(BACKEND_DIR / "credentials.json"))) | |
| TOKEN_JSON = Path(os.environ.get("GMAIL_TOKEN_JSON", str(BACKEND_DIR / "token.json"))) | |
| def _gmail() -> GmailClient: | |
| return GmailClient(CREDENTIALS_JSON, TOKEN_JSON) | |
| def _get_env_required(key: str) -> str: | |
| v = (os.environ.get(key) or "").strip() | |
| if not v: | |
| raise HTTPException(status_code=500, detail=f"Server missing {key} env var") | |
| return v | |
| def health(): | |
| return {"ok": True} | |
| def get_pdf(pdf_id: str): | |
| path = UPLOADS_DIR / f"{pdf_id}.pdf" | |
| if not path.exists(): | |
| raise HTTPException(status_code=404, detail="PDF not found") | |
| name_path = UPLOADS_DIR / f"{pdf_id}.name.txt" | |
| pdf_name = name_path.read_text(encoding="utf-8").strip() if name_path.exists() else f"{pdf_id}.pdf" | |
| resp = FileResponse(path, media_type="application/pdf", filename=pdf_name) | |
| resp.headers["X-PDF-Name"] = pdf_name | |
| return resp | |
| async def send_config(payload: Dict[str, Any]): | |
| """ | |
| PIPELINE SUBMISSION EMAIL (after rep saves config) | |
| REQUIRED payload: | |
| - pdf_id: str | |
| - template_id: str | |
| - config: dict | |
| Sends to PIPELINE inbox: | |
| - PDF_PIPELINE_PIPELINE_NOTIFY_TO | |
| Requirements: | |
| - Subject includes template_id | |
| - Body includes pdf_id | |
| - Attachments: JSON + PDF | |
| """ | |
| pdf_id = (payload.get("pdf_id") or "").strip() | |
| template_id = (payload.get("template_id") or "").strip() | |
| config = payload.get("config") | |
| if not pdf_id: | |
| raise HTTPException(status_code=400, detail="Missing pdf_id") | |
| if not template_id: | |
| raise HTTPException(status_code=400, detail="Missing template_id") | |
| if not isinstance(config, dict): | |
| raise HTTPException(status_code=400, detail="Missing config object") | |
| pipeline_to = _get_env_required("PDF_PIPELINE_PIPELINE_NOTIFY_TO") | |
| notify_from = _get_env_required("PDF_PIPELINE_NOTIFY_FROM") | |
| trainer_base_url = (os.environ.get("PDF_TRAINER_BASE_URL") or "http://localhost:5173").strip() | |
| pdf_path = UPLOADS_DIR / f"{pdf_id}.pdf" | |
| if not pdf_path.exists(): | |
| raise HTTPException(status_code=404, detail="PDF not found for pdf_id") | |
| name_path = UPLOADS_DIR / f"{pdf_id}.name.txt" | |
| pdf_name = name_path.read_text(encoding="utf-8").strip() if name_path.exists() else f"{pdf_id}.pdf" | |
| trainer_link = f"{trainer_base_url.rstrip('/')}/?pdf_id={pdf_id}" | |
| subject = f"PDF_TRAINER_CONFIG_SUBMITTED | template_id={template_id}" | |
| body = ( | |
| "Hi,\n\n" | |
| "A PDF Trainer configuration was submitted.\n\n" | |
| f"template_id: {template_id}\n" | |
| f"pdf_id: {pdf_id}\n" | |
| f"trainer_link: {trainer_link}\n\n" | |
| "Attachments:\n" | |
| f"- trainer_config_{pdf_id}_{template_id}.json\n" | |
| f"- {pdf_name}\n\n" | |
| "Thank you,\n" | |
| "Inserio Automation\n" | |
| ) | |
| cfg_bytes = json.dumps( | |
| {"pdf_id": pdf_id, "template_id": template_id, "config": config}, | |
| indent=2, | |
| ).encode("utf-8") | |
| attachments = [ | |
| (f"trainer_config_{pdf_id}_{template_id}.json", cfg_bytes), | |
| (pdf_name, pdf_path.read_bytes()), | |
| ] | |
| gmail = _gmail() | |
| gmail.send_email( | |
| to_email=pipeline_to, | |
| from_email=notify_from, | |
| subject=subject, | |
| body_text=body, | |
| attachments=attachments, | |
| ) | |
| return {"ok": True} | |
| async def notify_unknown(payload: Dict[str, Any]): | |
| """ | |
| UNKNOWN TEMPLATE NOTIFICATION (rep email) | |
| REQUIRED payload: | |
| - pdf_id: str | |
| OPTIONAL: | |
| - reason: str | |
| Sends to REP inbox: | |
| - PDF_PIPELINE_NOTIFY_TO | |
| Requirements: | |
| - Includes trainer link with PDF pre-loaded | |
| - Attaches PDF | |
| - No JSON | |
| """ | |
| pdf_id = (payload.get("pdf_id") or "").strip() | |
| reason = (payload.get("reason") or "").strip() | |
| if not pdf_id: | |
| raise HTTPException(status_code=400, detail="Missing pdf_id") | |
| rep_to = _get_env_required("PDF_PIPELINE_NOTIFY_TO") | |
| notify_from = _get_env_required("PDF_PIPELINE_NOTIFY_FROM") | |
| trainer_base_url = (os.environ.get("PDF_TRAINER_BASE_URL") or "http://localhost:5173").strip() | |
| pdf_path = UPLOADS_DIR / f"{pdf_id}.pdf" | |
| if not pdf_path.exists(): | |
| raise HTTPException(status_code=404, detail="PDF not found for pdf_id") | |
| name_path = UPLOADS_DIR / f"{pdf_id}.name.txt" | |
| pdf_name = name_path.read_text(encoding="utf-8").strip() if name_path.exists() else f"{pdf_id}.pdf" | |
| trainer_link = f"{trainer_base_url.rstrip('/')}/?pdf_id={pdf_id}" | |
| subject = "Action required: Unknown PDF format (template not found)" | |
| body = ( | |
| "Hi,\n\n" | |
| "We received a PDF that does not match any existing templates in the system.\n\n" | |
| + (f"Reason: {reason}\n\n" if reason else "") | |
| + "Please open the PDF Trainer using the link below and create or update the template configuration:\n" | |
| f"{trainer_link}\n\n" | |
| "The original PDF is attached for reference.\n\n" | |
| "Thank you,\n" | |
| "Inserio Automation\n" | |
| ) | |
| attachments = [(pdf_name, pdf_path.read_bytes())] | |
| gmail = _gmail() | |
| gmail.send_email( | |
| to_email=rep_to, | |
| from_email=notify_from, | |
| subject=subject, | |
| body_text=body, | |
| attachments=attachments, | |
| ) | |
| return {"ok": True} | |
| def root(): | |
| return "PDF Trainer API. Use /health" |