SysOps: approved_backup — 2026-08-16 11:01 UTC
This commit is contained in:
@@ -5,7 +5,7 @@ import asyncio
|
||||
import json
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi import File, UploadFile, APIRouter, HTTPException, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@@ -545,3 +545,121 @@ def api_execute_approved_request(request_id: int, body: ExecuteBody) -> dict[str
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
return {"ok": True, "request": req}
|
||||
|
||||
|
||||
# --- War Room chat ---
|
||||
class WarroomMessageBody(BaseModel):
|
||||
content: str = Field("", max_length=8000)
|
||||
attachments: list[dict] = Field(default_factory=list)
|
||||
|
||||
|
||||
@router.get("/warroom/rooms")
|
||||
async def warroom_list_rooms() -> dict[str, Any]:
|
||||
from app.services import agent_chat
|
||||
agent_chat.ensure_warroom()
|
||||
return {"items": agent_chat.list_rooms()}
|
||||
|
||||
|
||||
@router.post("/warroom/rooms/dm/{agent_key}")
|
||||
async def warroom_open_dm(agent_key: str) -> dict[str, Any]:
|
||||
from app.services import agent_chat
|
||||
try:
|
||||
room = agent_chat.get_or_create_dm(agent_key)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(404, str(exc)) from exc
|
||||
return {"room": room}
|
||||
|
||||
|
||||
@router.get("/warroom/rooms/{room_id}/messages")
|
||||
async def warroom_list_messages(room_id: int, limit: int = 100, after_id: int | None = None) -> dict[str, Any]:
|
||||
from app.services import agent_chat
|
||||
room = agent_chat.get_room(room_id)
|
||||
if not room:
|
||||
raise HTTPException(404, "Room niet gevonden")
|
||||
return {"room": room, "items": agent_chat.list_messages(room_id, limit=min(limit, 200), after_id=after_id)}
|
||||
|
||||
|
||||
@router.post("/warroom/rooms/{room_id}/messages")
|
||||
async def warroom_post_message(request: Request, room_id: int, body: WarroomMessageBody) -> dict[str, Any]:
|
||||
from app.services import agent_chat
|
||||
user = getattr(request.state, "user", None) or {}
|
||||
try:
|
||||
result = await agent_chat.post_user_message(
|
||||
room_id,
|
||||
body.content,
|
||||
username=(user.get("username") if isinstance(user, dict) else None),
|
||||
role=(user.get("role") if isinstance(user, dict) else None),
|
||||
sender_avatar_url=(user.get("avatar_url") if isinstance(user, dict) else None),
|
||||
attachments=body.attachments,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(400, str(exc)) from exc
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/warroom/rooms/{room_id}/messages/stream")
|
||||
async def warroom_post_message_stream(request: Request, room_id: int, body: WarroomMessageBody):
|
||||
"""SSE stream: user → typing tokens → agent message(s) → done."""
|
||||
import json
|
||||
from app.services import agent_chat
|
||||
|
||||
user = getattr(request.state, "user", None) or {}
|
||||
|
||||
async def gen():
|
||||
try:
|
||||
async for ev in agent_chat.post_user_message_stream(
|
||||
room_id,
|
||||
body.content,
|
||||
username=(user.get("username") if isinstance(user, dict) else None),
|
||||
role=(user.get("role") if isinstance(user, dict) else None),
|
||||
sender_avatar_url=(user.get("avatar_url") if isinstance(user, dict) else None),
|
||||
attachments=body.attachments,
|
||||
):
|
||||
yield f"data: {json.dumps(ev, default=str)}\n\n"
|
||||
except ValueError as exc:
|
||||
yield f"data: {json.dumps({'event': 'error', 'detail': str(exc)})}\n\n"
|
||||
except Exception as exc:
|
||||
yield f"data: {json.dumps({'event': 'error', 'detail': str(exc)})}\n\n"
|
||||
|
||||
return StreamingResponse(
|
||||
gen(),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"X-Accel-Buffering": "no",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/warroom/upload")
|
||||
async def warroom_upload(file: UploadFile = File(...)) -> dict[str, Any]:
|
||||
"""Upload a file to share in War Room (images/docs)."""
|
||||
from pathlib import Path as P
|
||||
import uuid
|
||||
import re
|
||||
|
||||
raw_name = (file.filename or "upload.bin").strip()
|
||||
safe = re.sub(r"[^a-zA-Z0-9._-]+", "_", raw_name)[:120] or "upload.bin"
|
||||
ext = P(safe).suffix.lower()
|
||||
allowed = {
|
||||
".png", ".jpg", ".jpeg", ".gif", ".webp", ".pdf", ".txt", ".csv",
|
||||
".xlsx", ".xls", ".doc", ".docx", ".zip", ".json", ".md",
|
||||
}
|
||||
if ext not in allowed:
|
||||
raise HTTPException(400, f"Bestandstype niet toegestaan: {ext or '(geen)'}")
|
||||
data = await file.read()
|
||||
if len(data) > 12 * 1024 * 1024:
|
||||
raise HTTPException(400, "Max 12MB per bestand")
|
||||
dest_dir = P(__file__).resolve().parent.parent.parent / "static" / "uploads" / "warroom"
|
||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
||||
fname = f"{uuid.uuid4().hex[:12]}_{safe}"
|
||||
(dest_dir / fname).write_bytes(data)
|
||||
url = f"/static/uploads/warroom/{fname}"
|
||||
return {
|
||||
"ok": True,
|
||||
"url": url,
|
||||
"name": raw_name[:180],
|
||||
"mime": file.content_type or "application/octet-stream",
|
||||
"size": len(data),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user