Import ClientFlow production v4928.1.5.132.4
This commit is contained in:
@@ -7,20 +7,22 @@ from __future__ import annotations
|
||||
|
||||
from hmac import compare_digest
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Request
|
||||
from sqlalchemy import text
|
||||
|
||||
from app.config import settings
|
||||
from app.config import is_production_like_env, settings
|
||||
from app.db import engine
|
||||
|
||||
def require_internal_access(request: Request) -> None:
|
||||
expected = (settings.clientflow_admin_token or "").strip()
|
||||
if not expected:
|
||||
if is_production_like_env():
|
||||
raise HTTPException(status_code=503, detail="internal api auth not configured")
|
||||
return
|
||||
received = (
|
||||
request.headers.get("X-ClientFlow-Admin-Token")
|
||||
or request.cookies.get("clientflow_admin_token")
|
||||
or request.query_params.get("admin_token")
|
||||
or (request.query_params.get("admin_token") if not is_production_like_env() else None)
|
||||
or ""
|
||||
).strip()
|
||||
if not received or not compare_digest(received, expected):
|
||||
@@ -41,6 +43,38 @@ def internal_health() -> dict:
|
||||
}
|
||||
|
||||
|
||||
@router.post("/tasks/{task_id}/reply-draft")
|
||||
def internal_task_reply_draft(task_id: str, payload: dict | None = Body(default=None)) -> dict:
|
||||
"""Generate a safe editable reply draft for a task.
|
||||
|
||||
Internal API counterpart of the admin UI button. It persists a draft in
|
||||
message_drafts, returns the suggested message and never sends it.
|
||||
"""
|
||||
payload = payload or {}
|
||||
try:
|
||||
from app.reply_assistant_service import generate_reply_draft
|
||||
|
||||
state = generate_reply_draft(
|
||||
task_id,
|
||||
template_code=str(payload.get("template_code") or "").strip() or None,
|
||||
selected_document_ids=payload.get("selected_document_ids") or [],
|
||||
persist=True,
|
||||
)
|
||||
return {
|
||||
"draft_id": state.get("draft_id"),
|
||||
"task_id": task_id,
|
||||
"template_code": (state.get("template") or {}).get("code"),
|
||||
"message_body": state.get("message_body"),
|
||||
"blockers": state.get("blockers") or [],
|
||||
"warnings": state.get("warnings") or [],
|
||||
"email_agent": state.get("email_agent") or {},
|
||||
"intent_gate": state.get("intent_gate") or {},
|
||||
"business_knowledge": state.get("business_knowledge") or {},
|
||||
}
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.get("/outbox/summary")
|
||||
def outbox_summary() -> dict:
|
||||
with engine.begin() as conn:
|
||||
@@ -98,3 +132,10 @@ def communications_summary() -> dict:
|
||||
def communications_recent(limit: int = 20) -> dict:
|
||||
from app.communication_service import list_communications
|
||||
return {"items": list_communications(limit=limit)}
|
||||
|
||||
|
||||
@router.get("/forecast")
|
||||
@router.get("/revenue-forecast")
|
||||
def revenue_forecast(limit: int = 1000, month: str | None = None, metric: str = "invoiced") -> dict:
|
||||
from app.revenue_forecast_service import get_revenue_forecast
|
||||
return get_revenue_forecast(limit=limit, month=month, metric=metric)
|
||||
|
||||
Reference in New Issue
Block a user