from fastapi import FastAPI, Request, HTTPException, BackgroundTasks
from fastapi.responses import HTMLResponse
import logging
import os
import json
from queue_manager import JobQueue
# Logging Setup
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("connector-webhook")
app = FastAPI(title="SuperOffice Connector Webhook", version="2.0")
queue = JobQueue()
WEBHOOK_TOKEN = os.getenv("WEBHOOK_TOKEN", "changeme")
@app.post("/webhook")
async def receive_webhook(request: Request, background_tasks: BackgroundTasks):
"""
Endpoint for SuperOffice Webhooks.
"""
# 1. Verify Secret (Basic Security)
# SuperOffice puts signature in headers, but for custom webhook we might just use query param or header
# Or simply a secret in the URL: /webhook?token=...
token = request.query_params.get("token")
if token != WEBHOOK_TOKEN:
logger.warning(f"Invalid webhook token attempt: {token}")
raise HTTPException(403, "Invalid Token")
try:
payload = await request.json()
logger.info(f"Received webhook payload: {payload}")
event_type = payload.get("Event", "unknown")
# --- DEDUPLICATION AT INGRESS (Added March 2026) ---
# Before adding a job, check if an identical one is already pending.
if queue.is_duplicate_pending(event_type, payload):
return {"status": "skipped_duplicate"}
# Add to local Queue
queue.add_job(event_type, payload)
return {"status": "queued"}
except Exception as e:
logger.error(f"Error processing webhook: {e}", exc_info=True)
raise HTTPException(500, "Internal Server Error")
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/stats")
def stats():
return queue.get_stats()
@app.get("/api/jobs")
def get_jobs():
return queue.get_recent_jobs(limit=100)
@app.get("/api/accounts")
def get_accounts():
return queue.get_account_summary(limit=500)
@app.get("/dashboard", response_class=HTMLResponse)
def dashboard():
html_content = """
Connector Dashboard
š SuperOffice Connector Dashboard
| Account / Person |
Responsible |
ID |
Process Progress |
Duration |
Status |
Last Update |
Details |
| Loading Accounts... |
| ID |
Status |
Updated |
Event |
Payload / Error |
| Loading Events... |
"""
return HTMLResponse(content=html_content, status_code=200)
if __name__ == "__main__":
import uvicorn
uvicorn.run("webhook_app:app", host="0.0.0.0", port=8000, reload=True)