Страница разбора расшифровки в браузере
По одному JSON не понять, чья ошибка: распознавание склеило реплики или
LLM неверно расставила роли. Страница показывает обе стороны и принимает
ответ модели, чтобы диалог собрался на глазах.
GET /v1/jobs/{id}/view - токен заголовком либо ссылкой ?token=ЗНАЧЕНИЕ.
Страница самодостаточна: ни одного внешнего запроса.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
020706a69a
commit
0dd531aaa1
+31
-8
@@ -14,7 +14,7 @@ from urllib.parse import quote
|
||||
from fastapi import Depends, FastAPI, File, HTTPException, Query, Request, UploadFile
|
||||
from fastapi.openapi.docs import get_swagger_ui_html
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from fastapi.security import HTTPBearer
|
||||
|
||||
from app.config import ConfigError, Settings, load_settings
|
||||
@@ -25,6 +25,7 @@ from app.pipeline import ModelsMissing, Pipeline, to_wav16k
|
||||
from app.security import (check_token, client_address, ip_allowed,
|
||||
parse_allowlist, token_matches)
|
||||
from app.store import JobStatus, JobStore
|
||||
from app.view import render_job
|
||||
from app.webhook import deliver_async, secret_fingerprint
|
||||
from app.version import __version__
|
||||
|
||||
@@ -227,6 +228,21 @@ def guard(request: Request) -> None:
|
||||
raise HTTPException(status_code=401, detail="неверный или отсутствующий токен")
|
||||
|
||||
|
||||
def page_guard(request: Request) -> str:
|
||||
"""Пускает к странице для браузера и возвращает токен для ссылок на ней.
|
||||
|
||||
Токен принимается заголовком и параметром ?token=: браузер, открывая
|
||||
страницу по ссылке, заголовок не подставит.
|
||||
"""
|
||||
ip_guard(request)
|
||||
supplied = request.query_params.get("token")
|
||||
if check_token(request.headers.get("authorization"), settings.token):
|
||||
return supplied or ""
|
||||
if token_matches(supplied, settings.token):
|
||||
return supplied or ""
|
||||
raise HTTPException(status_code=401, detail="неверный или отсутствующий токен")
|
||||
|
||||
|
||||
def docs_guard(request: Request) -> str:
|
||||
"""Пускает к документации и возвращает токен для ссылки на схему.
|
||||
|
||||
@@ -238,13 +254,7 @@ def docs_guard(request: Request) -> str:
|
||||
if not settings.docs:
|
||||
# 404, а не 403: подтверждать существование страницы незачем.
|
||||
raise HTTPException(status_code=404, detail="Not Found")
|
||||
ip_guard(request)
|
||||
supplied = request.query_params.get("token")
|
||||
if check_token(request.headers.get("authorization"), settings.token):
|
||||
return supplied or ""
|
||||
if token_matches(supplied, settings.token):
|
||||
return supplied or ""
|
||||
raise HTTPException(status_code=401, detail="неверный или отсутствующий токен")
|
||||
return page_guard(request)
|
||||
|
||||
|
||||
@app.get("/docs", include_in_schema=False)
|
||||
@@ -270,6 +280,19 @@ def openapi_schema(request: Request) -> JSONResponse:
|
||||
return JSONResponse(schema)
|
||||
|
||||
|
||||
@app.get("/v1/jobs/{job_id}/view", include_in_schema=False)
|
||||
def job_view(job_id: str, request: Request):
|
||||
"""Расшифровка в браузере: видно, что отдал сервис и что дала разметка."""
|
||||
page_guard(request)
|
||||
job = store.get(job_id)
|
||||
if job is None or job["status"] != JobStatus.DONE:
|
||||
raise HTTPException(status_code=404, detail="задача не найдена или не готова")
|
||||
payload = dict(job.get("result") or {})
|
||||
payload["job_id"] = job_id
|
||||
payload["filename"] = job.get("filename", "")
|
||||
return HTMLResponse(render_job(payload))
|
||||
|
||||
|
||||
@app.get("/v1/logs", dependencies=[Depends(guard)], summary="Журнал сервиса",
|
||||
description="Последние записи журнала: удобно посмотреть причину сбоя, "
|
||||
"не заходя на машину. Уровень можно отфильтровать параметром level.")
|
||||
|
||||
Reference in New Issue
Block a user