5d60d33db1
Volledige Foodlinkk Command Center uitbreiding met social automatisering, reclamefolder filters, Proxmox monitoring en documentatie.
82 lines
2.1 KiB
Python
82 lines
2.1 KiB
Python
"""Foodlinkk Email Agent — IMAP sync + CEO approval workflow."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from contextlib import asynccontextmanager
|
|
from typing import Any
|
|
|
|
from fastapi import FastAPI, HTTPException
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.config import settings
|
|
from app.db import close_pool, fetch_one, init_pool
|
|
from app import imap_sync
|
|
|
|
|
|
class DraftIn(BaseModel):
|
|
title: str = Field(..., min_length=3, max_length=255)
|
|
to: str
|
|
subject: str
|
|
body: str
|
|
|
|
|
|
async def _sync_loop() -> None:
|
|
while True:
|
|
try:
|
|
imap_sync.sync_all_accounts()
|
|
except Exception:
|
|
pass
|
|
await asyncio.sleep(settings.SYNC_INTERVAL_SEC)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
init_pool()
|
|
task = asyncio.create_task(_sync_loop())
|
|
yield
|
|
task.cancel()
|
|
close_pool()
|
|
|
|
|
|
app = FastAPI(title="Foodlinkk Email Agent", version="1.0.0", lifespan=lifespan)
|
|
|
|
|
|
@app.get("/health")
|
|
def health() -> dict[str, Any]:
|
|
db_ok = bool(fetch_one("SELECT 1 AS ok"))
|
|
accounts = fetch_one(
|
|
"SELECT COUNT(*) AS n FROM email_accounts WHERE is_active = TRUE AND sync_enabled = TRUE"
|
|
)
|
|
return {
|
|
"status": "ok" if db_ok else "degraded",
|
|
"database": "connected" if db_ok else "error",
|
|
"imap_accounts": int((accounts or {}).get("n") or 0),
|
|
}
|
|
|
|
|
|
@app.post("/sync")
|
|
def sync_now() -> dict[str, Any]:
|
|
return imap_sync.sync_all_accounts()
|
|
|
|
|
|
@app.get("/drafts/pending")
|
|
def pending_drafts() -> dict[str, Any]:
|
|
items = imap_sync.list_pending_drafts()
|
|
return {"items": items, "count": len(items)}
|
|
|
|
|
|
@app.post("/drafts")
|
|
def create_draft(payload: DraftIn) -> dict[str, Any]:
|
|
row = imap_sync.create_draft(payload.title, payload.body, payload.to, payload.subject)
|
|
return {"draft": row}
|
|
|
|
|
|
@app.post("/drafts/{rec_id}/approve")
|
|
def approve_draft(rec_id: int) -> dict[str, Any]:
|
|
try:
|
|
return imap_sync.approve_draft(rec_id)
|
|
except ValueError as exc:
|
|
raise HTTPException(400, str(exc)) from exc
|
|
except Exception as exc: # noqa: BLE001
|
|
raise HTTPException(502, str(exc)) from exc
|