f7a3ef2e0b
- 新增内部端点 POST /internal/launch-confirm-sample(X-Internal-Secret 鉴权): pricebot 对没见过文案的启动确认窗用 LLM 兜底放行后,把样本(host 包 / 弹窗树 / LLM plan / 设备 locale 机型)回写落 launch_confirm_sample 表,供研发人工沉淀回 pricebot 规则 yaml - 配套: model LaunchConfirmSample + schema + repo(都上报不去重) + alembic 迁移 launch_confirm_sample_table(down_revision=cps_wx_user) + main 注册 router + models/__init__ 登记 - docs: 后端技术实现.md 加 §6.5 pricebot 回写内部端点说明 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
2.3 KiB
Python
64 lines
2.3 KiB
Python
"""启动确认窗兜底样本内部上报端点(pricebot → app-server)。
|
|
|
|
pricebot 的 launch_confirm_agent 每次靠 LLM 兜底放行一个"静态 PROFILES 没认出"的跨 App
|
|
启动确认窗时,把完整样本 POST 到这里落库。**不是给客户端的接口**:不走用户 JWT,靠
|
|
server 间共享密钥头 `X-Internal-Secret` 校验(== settings.INTERNAL_API_SECRET)。密钥未配置
|
|
(默认空)时直接 503,避免裸奔写端点。
|
|
|
|
研发定期人工把 exec_success=true 的样本沉淀进 pricebot 的 launch_confirm.PROFILES。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import hmac
|
|
import logging
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Header, HTTPException, status
|
|
|
|
from app.api.deps import DbSession
|
|
from app.core.config import settings
|
|
from app.repositories import launch_confirm_sample as repo
|
|
from app.schemas.launch_confirm_sample import (
|
|
LaunchConfirmSampleIn,
|
|
LaunchConfirmSampleOut,
|
|
)
|
|
|
|
logger = logging.getLogger("shagua.internal.launch_confirm")
|
|
|
|
router = APIRouter(prefix="/internal", tags=["internal"])
|
|
|
|
|
|
def _check_secret(x_internal_secret: str | None) -> None:
|
|
"""共享密钥校验。未配置 → 503(挡裸奔写端点);不匹配 → 401(常量时间比较)。"""
|
|
configured = settings.INTERNAL_API_SECRET
|
|
if not configured:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="internal api not configured",
|
|
)
|
|
if not x_internal_secret or not hmac.compare_digest(x_internal_secret, configured):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="invalid internal secret",
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/launch-confirm-sample",
|
|
response_model=LaunchConfirmSampleOut,
|
|
summary="启动确认窗兜底样本上报(pricebot→app-server,落 launch_confirm_sample)",
|
|
)
|
|
def report_launch_confirm_sample(
|
|
payload: LaunchConfirmSampleIn,
|
|
db: DbSession,
|
|
x_internal_secret: Annotated[str | None, Header()] = None,
|
|
) -> LaunchConfirmSampleOut:
|
|
_check_secret(x_internal_secret)
|
|
sid = repo.insert_sample(db, payload)
|
|
logger.info(
|
|
"launch_confirm_sample id=%d host=%s locale=%s success=%s trace=%s",
|
|
sid, payload.host_package, payload.system_locale,
|
|
payload.exec_success, payload.trace_id,
|
|
)
|
|
return LaunchConfirmSampleOut(id=sid)
|