feat(511-A 계산 자리 서버 창구): POST /api/projects/{id}/calc — 프로젝트 권한 · 본문 상한 8 MB · 늘 떠 있는 Node 하나(design_calc_node --serve · 죽음 · 묶음 바뀜 = 다시 띄움 · 1.4 MB 33 ms · 새 Node 110~170 ms) · 시험 길 id 따로(TRIAL_IDS) · 브라우저 길 design_calc_server(잠잠 400 ms · 늦은 답 버림 · 대조 · 기록 __aisloCalcServer) — 요구 320-4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
"""계산 자리 「서버」 창구(PLAN 511-A · 요구 320-4) — 조작 중 계산을 서버가 같은 TS 로.
|
||||
|
||||
POST /api/projects/{id}/calc {run: [id…], inputs: {…}} → {results, ms, wall_ms}
|
||||
|
||||
브라우저가 계산 자리 판(`calc_sites.json`)에서 묶음을 「서버」로 둔 때만 부름
|
||||
(`design_calc_server.ts`).
|
||||
권한 = 프로젝트 접근(`protected_with_company`) · 호출량 = `main.py` 미들웨어 · 본문 상한 `MAX_BODY`.
|
||||
실행 = 늘 떠 있는 Node 하나(`common_util_calc_runner.run_calc_served`) ·
|
||||
정본 저장 길은 이 창구를 안 씀.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from common_util.common_util_calc_runner import run_calc_served
|
||||
|
||||
router = APIRouter(prefix="/api/projects", tags=["B06 Calc Server"])
|
||||
|
||||
#: 본문 상한 — 종횡단 상세 한 벌(1.3~1.5 MB) 여유 · 넘으면 413.
|
||||
MAX_BODY = 8 * 1024 * 1024
|
||||
|
||||
|
||||
def _error(status: int, message: str) -> JSONResponse:
|
||||
return JSONResponse(status_code=status, content={"status": "error", "message": message})
|
||||
|
||||
|
||||
@router.post("/{project_id}/calc", response_model=None)
|
||||
async def run_calc_endpoint(project_id: UUID, request: Request) -> dict | JSONResponse:
|
||||
declared = request.headers.get("content-length")
|
||||
if declared and declared.isdigit() and int(declared) > MAX_BODY:
|
||||
return _error(413, "계산 입력이 너무 큼")
|
||||
body = b""
|
||||
async for chunk in request.stream():
|
||||
body += chunk
|
||||
if len(body) > MAX_BODY:
|
||||
return _error(413, "계산 입력이 너무 큼")
|
||||
try:
|
||||
payload = json.loads(body)
|
||||
except ValueError:
|
||||
return _error(400, "JSON 아님")
|
||||
run = payload.get("run") if isinstance(payload, dict) else None
|
||||
inputs = payload.get("inputs", {}) if isinstance(payload, dict) else None
|
||||
if not isinstance(run, list) or not all(isinstance(i, str) for i in run):
|
||||
return _error(400, "run = id 목록")
|
||||
if not isinstance(inputs, dict):
|
||||
return _error(400, "inputs = 객체")
|
||||
try:
|
||||
answer = await asyncio.to_thread(run_calc_served, run, inputs)
|
||||
except ValueError as exc:
|
||||
return _error(400, str(exc))
|
||||
if answer is None:
|
||||
return _error(503, "서버 계산을 못 돌림")
|
||||
if "error" in answer:
|
||||
return _error(422, str(answer["error"]))
|
||||
return {"status": "success", "project_id": str(project_id), **answer}
|
||||
Reference in New Issue
Block a user