Files
GovAI/docs/openapi.yaml
T
freedakgmail c949204662 feat(govai): 0617 优化首批 — 安全/私有化/深度研究/服务层/可观测性
借鉴 odysseus 的能力设计,全程净室实现、零 AGPL 代码、不引入 AGPL 依赖。

T1 提示注入防护: pkg/promptguard 包裹外部/知识库内容为不可信数据,buildMessages 移出 system 指令区。 T2 安全 CI: .github/workflows(ci+security: govulncheck/gitleaks/actionlint/hadolint/trivy)+dependabot+.hadolint.yaml;go.mod 加 toolchain go1.25.11 修复 20 个 stdlib CVE。 T3 管理员 2FA: 迁移 000016 + RFC6238 TOTP/备份码(pkg/auth, 零依赖) + 登录流程集成(后端)。 T4 本地模型: LLM/embedding 支持本地 vLLM/Ollama(OpenAI 兼容, 鉴权头条件发送, NoAuth) + docs/local-deploy.md。 T6 深度研究: 迁移 000017 + Python research-worker(净室多步流水线, 检索避开 SearXNG) + Go research 服务/handler/路由。 T7 service 层: 新增 internal/service/{research,twofa}, 2FA 业务逻辑从胖 handler 下沉, 接口注入可单测。 T10 缓存/可观测性: internal/cache(Redis+内存, 优雅降级) 接入 store 热点列表; Prometheus 指标+/metrics; docs/openapi.yaml。 验证: go build/vet/test ./... 全绿(8 包); research-worker 12 单测过; 真实 PG 应用迁移并烟测。
2026-06-17 17:52:47 +08:00

448 lines
13 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.0.3
info:
title: 政智通 GovAI Portal API
description: >
政务 AI 应用平台后端 API。所有业务响应使用统一信封 `{code,message,data}`
`code=0` 表示成功。认证使用 JWTBearer 头或 access_token Cookie)。
本规范覆盖主要端点,随实现演进补充。
version: "1.0.0"
servers:
- url: http://localhost:8080/api/v1
description: 本地开发
tags:
- name: auth
description: 认证与两步验证
- name: store
description: 应用商店(公开只读)
- name: apps
description: 应用使用(对话/补全)
- name: research
description: 深度研究(综合研判)
- name: ppt
description: PPT 生成
- name: knowledge
description: 知识库
- name: ops
description: 运维(健康检查 / 指标)
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
cookieAuth:
type: apiKey
in: cookie
name: access_token
schemas:
ApiResponse:
type: object
properties:
code:
type: integer
description: 0 为成功,其它为业务错误码
example: 0
message:
type: string
example: success
data:
nullable: true
description: 业务数据,结构随接口而定
LoginRequest:
type: object
required: [email, password]
properties:
email: { type: string, format: email }
password: { type: string, format: password }
org_id: { type: string, description: 机构 ID(可选) }
totp_code: { type: string, description: 已开启 2FA 时提供 }
backup_code: { type: string, description: 备份码(TOTP 不可用时) }
ResearchCreateRequest:
type: object
required: [topic]
properties:
topic: { type: string, description: 研究题目/问题 }
app_id: { type: string }
config:
type: object
properties:
max_steps: { type: integer, default: 4 }
max_sources: { type: integer, default: 6 }
language: { type: string, default: zh }
ResearchTask:
type: object
properties:
task_id: { type: string }
topic: { type: string }
status:
type: string
enum: [pending, planning, searching, reading, synthesizing, completed, failed, canceled]
progress: { type: integer, minimum: 0, maximum: 100 }
report: { type: string, nullable: true, description: 完成后的 Markdown 报告 }
sources:
type: array
items:
type: object
properties:
title: { type: string }
url: { type: string }
snippet: { type: string }
tokens_used: { type: integer }
created_at: { type: string, format: date-time }
security:
- bearerAuth: []
- cookieAuth: []
paths:
/auth/register:
post:
tags: [auth]
summary: 注册
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name, email, password]
properties:
name: { type: string }
email: { type: string, format: email }
password: { type: string, minLength: 6 }
responses:
"201": { description: 注册成功, content: { application/json: { schema: { $ref: "#/components/schemas/ApiResponse" } } } }
"409": { description: 邮箱已注册 }
/auth/login:
post:
tags: [auth]
summary: 登录(支持 2FA
description: 已开启 2FA 的账号在密码正确但未带验证码时返回 `code=40110`,前端据此引导输入验证码后重试。
security: []
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/LoginRequest" }
responses:
"200": { description: 登录成功, content: { application/json: { schema: { $ref: "#/components/schemas/ApiResponse" } } } }
"401": { description: 凭据错误 / 需要或验证码错误(40110 需要 2FA、40111 验证码错误) }
/auth/refresh:
post:
tags: [auth]
summary: 刷新 access token
security: []
responses:
"200": { description: 成功 }
/auth/logout:
post:
tags: [auth]
summary: 登出
responses:
"200": { description: 成功 }
/auth/me:
get:
tags: [auth]
summary: 当前用户信息
responses:
"200": { description: 成功, content: { application/json: { schema: { $ref: "#/components/schemas/ApiResponse" } } } }
/auth/2fa/status:
get:
tags: [auth]
summary: 查询 2FA 状态
responses:
"200":
description: 成功
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/ApiResponse"
- type: object
properties:
data:
type: object
properties:
enabled: { type: boolean }
backup_codes_remaining: { type: integer }
/auth/2fa/enroll:
post:
tags: [auth]
summary: 开始 2FA 设置(返回密钥/二维码 URI/一次性备份码)
responses:
"200":
description: 成功
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/ApiResponse"
- type: object
properties:
data:
type: object
properties:
secret: { type: string }
otpauth_uri: { type: string }
backup_codes: { type: array, items: { type: string } }
"409": { description: 已启用(40902 }
/auth/2fa/verify:
post:
tags: [auth]
summary: 校验验证码并启用 2FA
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [code]
properties:
code: { type: string }
responses:
"200": { description: 已启用 }
"401": { description: 验证码错误(40111 }
/auth/2fa/disable:
post:
tags: [auth]
summary: 关闭 2FA(需 TOTP 或备份码)
requestBody:
content:
application/json:
schema:
type: object
properties:
code: { type: string }
backup_code: { type: string }
responses:
"200": { description: 已关闭 }
"401": { description: 验证码或备份码错误(40111 }
/organizations:
get:
tags: [auth]
summary: 机构列表(公开)
security: []
responses:
"200": { description: 成功 }
/store/categories:
get:
tags: [store]
summary: 应用分类(缓存 60s
security: []
parameters:
- { name: org_id, in: query, schema: { type: string } }
responses:
"200": { description: 成功 }
/store/apps:
get:
tags: [store]
summary: 应用列表(分页/搜索/排序)
security: []
parameters:
- { name: page, in: query, schema: { type: integer, default: 1 } }
- { name: page_size, in: query, schema: { type: integer, default: 20, maximum: 50 } }
- { name: q, in: query, schema: { type: string } }
- { name: category, in: query, schema: { type: string } }
- { name: sort, in: query, schema: { type: string, enum: [popular, rating, latest] } }
- { name: org_id, in: query, schema: { type: string } }
responses:
"200": { description: 成功 }
/store/apps/{slug}:
get:
tags: [store]
summary: 应用详情
security: []
parameters:
- { name: slug, in: path, required: true, schema: { type: string } }
responses:
"200": { description: 成功 }
"404": { description: 不存在 }
/store/featured:
get:
tags: [store]
summary: 精选应用(缓存 60s
security: []
parameters:
- { name: org_id, in: query, schema: { type: string } }
responses:
"200": { description: 成功 }
/store/rankings:
get:
tags: [store]
summary: 应用排行榜(缓存 60s
security: []
parameters:
- { name: org_id, in: query, schema: { type: string } }
responses:
"200": { description: 成功 }
/apps/{id}/chat:
post:
tags: [apps]
summary: 对话(SSE 流式)
parameters:
- { name: id, in: path, required: true, schema: { type: string } }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [message]
properties:
message: { type: string }
conversation_id: { type: string }
responses:
"200": { description: SSE 事件流(text/event-stream }
/apps/{id}/completion:
post:
tags: [apps]
summary: 补全(SSE 流式)
parameters:
- { name: id, in: path, required: true, schema: { type: string } }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [message]
properties:
message: { type: string }
responses:
"200": { description: SSE 事件流 }
/research/tasks:
post:
tags: [research]
summary: 创建深度研究任务
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/ResearchCreateRequest" }
responses:
"201": { description: 已创建(返回 task_id, status=pending }
"400": { description: 题目为空 }
get:
tags: [research]
summary: 我的研究任务列表
responses:
"200": { description: 成功 }
/research/tasks/{taskId}:
get:
tags: [research]
summary: 查询研究任务状态/结果
parameters:
- { name: taskId, in: path, required: true, schema: { type: string } }
responses:
"200":
description: 成功
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/ApiResponse"
- type: object
properties:
data: { $ref: "#/components/schemas/ResearchTask" }
"404": { description: 不存在 }
/research/tasks/{taskId}/cancel:
post:
tags: [research]
summary: 取消进行中的研究任务
parameters:
- { name: taskId, in: path, required: true, schema: { type: string } }
responses:
"200": { description: 已取消 }
"404": { description: 不存在或无法取消 }
/ppt/tasks:
post:
tags: [ppt]
summary: 创建 PPT 生成任务
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [title, source_content]
properties:
title: { type: string }
source_type: { type: string, enum: [text, url] }
source_content: { type: string }
config: { type: object }
responses:
"201": { description: 已创建 }
/ppt/tasks/{taskId}:
get:
tags: [ppt]
summary: 查询 PPT 任务状态
parameters:
- { name: taskId, in: path, required: true, schema: { type: string } }
responses:
"200": { description: 成功 }
"404": { description: 不存在 }
/knowledge:
get:
tags: [knowledge]
summary: 知识库列表
responses:
"200": { description: 成功 }
post:
tags: [knowledge]
summary: 创建知识库
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name: { type: string }
description: { type: string }
responses:
"201": { description: 已创建 }
/health:
get:
tags: [ops]
summary: 健康检查
security: []
responses:
"200": { description: ok }
/metrics:
get:
tags: [ops]
summary: Prometheus 指标(文本格式,供监控抓取)
description: 注意此端点位于 `/metrics`(非 `/api/v1` 前缀下)。
security: []
responses:
"200": { description: Prometheus 文本格式指标 }