Skip to content

Commit 27844fe

Browse files
committed
feat(api-docs): 展示请求 body 格式
- 为 POST/PATCH 端点添加可展开的 body 示例
1 parent 47e77a8 commit 27844fe

1 file changed

Lines changed: 109 additions & 19 deletions

File tree

console/ai-proxy-dashboard/src/features/dashboard/components/api-docs-page.tsx

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,99 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
33
import { Badge } from "@/components/ui/badge"
44
import { Button } from "@/components/ui/button"
55
import { Input } from "@/components/ui/input"
6-
import { Copy, Check } from "lucide-react"
6+
import { Copy, Check, ChevronDown, ChevronRight } from "lucide-react"
77

88
interface Endpoint {
99
method: string
1010
path: string
1111
description: string
1212
auth: boolean
13+
body?: BodyExample
14+
}
15+
16+
interface BodyExample {
17+
description: string
18+
json: unknown
19+
}
20+
21+
const PROVIDER_BODY: BodyExample = {
22+
description: "创建/更新渠道",
23+
json: {
24+
channelName: "my-openai",
25+
type: "openai",
26+
targetBaseUrl: "https://api.openai.com/v1",
27+
systemPrompt: null,
28+
models: ["gpt-4o", "gpt-4o-mini"],
29+
priority: 100,
30+
auth: {
31+
header: "authorization",
32+
value: "sk-xxxx"
33+
},
34+
responsesMode: "native",
35+
extraFields: null
36+
}
37+
}
38+
39+
const PROVIDER_ENABLED_BODY: BodyExample = {
40+
description: "启用/禁用渠道",
41+
json: {
42+
enabled: true
43+
}
44+
}
45+
46+
const KEY_NAME_BODY: BodyExample = {
47+
description: "创建/重命名 API Key",
48+
json: {
49+
name: "my-key"
50+
}
51+
}
52+
53+
const KEY_MODELS_BODY: BodyExample = {
54+
description: "设置允许模型列表",
55+
json: {
56+
models: ["gpt-4o", "gpt-4o-mini"]
57+
}
58+
}
59+
60+
const ALIAS_BODY: BodyExample = {
61+
description: "创建/更新模型别名",
62+
json: {
63+
alias: "gpt-4o",
64+
provider: "my-openai",
65+
model: "gpt-4o-2024-08-06",
66+
description: "GPT-4o 主力模型",
67+
enabled: true
68+
}
69+
}
70+
71+
const ALIAS_ENABLED_BODY: BodyExample = {
72+
description: "启用/禁用模型别名",
73+
json: {
74+
enabled: true
75+
}
1376
}
1477

1578
const ENDPOINTS: Endpoint[] = [
1679
{ method: "GET", path: "/api/v1/health", description: "健康检查", auth: false },
1780
{ method: "GET", path: "/api/v1/providers", description: "获取所有渠道", auth: true },
1881
{ method: "GET", path: "/api/v1/providers/:channelName", description: "获取单个渠道详情", auth: true },
19-
{ method: "POST", path: "/api/v1/providers", description: "创建渠道", auth: true },
20-
{ method: "PATCH", path: "/api/v1/providers/:channelName", description: "更新渠道", auth: true },
82+
{ method: "POST", path: "/api/v1/providers", description: "创建渠道", auth: true, body: PROVIDER_BODY },
83+
{ method: "PATCH", path: "/api/v1/providers/:channelName", description: "更新渠道", auth: true, body: PROVIDER_BODY },
2184
{ method: "DELETE", path: "/api/v1/providers/:channelName", description: "删除渠道", auth: true },
22-
{ method: "PATCH", path: "/api/v1/providers/:channelName/enabled", description: "启用/禁用渠道", auth: true },
85+
{ method: "PATCH", path: "/api/v1/providers/:channelName/enabled", description: "启用/禁用渠道", auth: true, body: PROVIDER_ENABLED_BODY },
2386
{ method: "GET", path: "/api/v1/requests", description: "获取请求日志列表", auth: true },
2487
{ method: "GET", path: "/api/v1/requests/:requestId", description: "获取单个请求详情", auth: true },
2588
{ method: "GET", path: "/api/v1/stats", description: "获取统计数据", auth: true },
2689
{ method: "GET", path: "/api/v1/keys", description: "获取所有 API Keys", auth: true },
2790
{ method: "GET", path: "/api/v1/keys/:id", description: "获取单个 API Key", auth: true },
28-
{ method: "POST", path: "/api/v1/keys", description: "创建 API Key", auth: true },
29-
{ method: "PATCH", path: "/api/v1/keys/:id", description: "重命名 API Key", auth: true },
91+
{ method: "POST", path: "/api/v1/keys", description: "创建 API Key", auth: true, body: KEY_NAME_BODY },
92+
{ method: "PATCH", path: "/api/v1/keys/:id", description: "重命名 API Key", auth: true, body: KEY_NAME_BODY },
3093
{ method: "DELETE", path: "/api/v1/keys/:id", description: "删除 API Key", auth: true },
31-
{ method: "PATCH", path: "/api/v1/keys/:id/allowed-models", description: "设置 API Key 允许模型", auth: true },
94+
{ method: "PATCH", path: "/api/v1/keys/:id/allowed-models", description: "设置 API Key 允许模型", auth: true, body: KEY_MODELS_BODY },
3295
{ method: "GET", path: "/api/v1/aliases", description: "获取所有模型别名", auth: true },
33-
{ method: "POST", path: "/api/v1/aliases", description: "创建模型别名", auth: true },
34-
{ method: "PATCH", path: "/api/v1/aliases/:id", description: "更新模型别名", auth: true },
35-
{ method: "PATCH", path: "/api/v1/aliases/:id/enabled", description: "启用/禁用模型别名", auth: true },
96+
{ method: "POST", path: "/api/v1/aliases", description: "创建模型别名", auth: true, body: ALIAS_BODY },
97+
{ method: "PATCH", path: "/api/v1/aliases/:id", description: "更新模型别名", auth: true, body: ALIAS_BODY },
98+
{ method: "PATCH", path: "/api/v1/aliases/:id/enabled", description: "启用/禁用模型别名", auth: true, body: ALIAS_ENABLED_BODY },
3699
{ method: "DELETE", path: "/api/v1/aliases/:id", description: "删除模型别名", auth: true },
37100
]
38101

@@ -64,6 +127,41 @@ function CopyButton({ text }: { text: string }) {
64127
)
65128
}
66129

130+
function EndpointItem({ ep }: { ep: Endpoint }) {
131+
const [expanded, setExpanded] = useState(false)
132+
133+
return (
134+
<div className="rounded-md border">
135+
<div
136+
className="flex items-center gap-3 p-3 hover:bg-muted/50 cursor-pointer select-none"
137+
onClick={() => ep.body && setExpanded((v) => !v)}
138+
>
139+
<MethodBadge method={ep.method} />
140+
<code className="flex-1 text-sm font-mono">{ep.path}</code>
141+
<span className="text-sm text-muted-foreground">{ep.description}</span>
142+
{ep.auth && <Badge variant="outline" className="text-xs">需认证</Badge>}
143+
{ep.body && (
144+
expanded
145+
? <ChevronDown className="h-4 w-4 text-muted-foreground" />
146+
: <ChevronRight className="h-4 w-4 text-muted-foreground" />
147+
)}
148+
</div>
149+
150+
{expanded && ep.body && (
151+
<div className="border-t px-3 py-3 space-y-2">
152+
<div className="flex items-center justify-between">
153+
<span className="text-xs text-muted-foreground">{ep.body.description} - Request Body</span>
154+
<CopyButton text={JSON.stringify(ep.body.json, null, 2)} />
155+
</div>
156+
<pre className="rounded-md bg-muted p-3 text-xs font-mono overflow-x-auto">
157+
<code>{JSON.stringify(ep.body.json, null, 2)}</code>
158+
</pre>
159+
</div>
160+
)}
161+
</div>
162+
)
163+
}
164+
67165
export function ApiDocsPage() {
68166
const [filter, setFilter] = useState("")
69167

@@ -117,15 +215,7 @@ export function ApiDocsPage() {
117215
<CardContent>
118216
<div className="flex flex-col gap-2">
119217
{filtered.map((ep) => (
120-
<div
121-
key={ep.path + ep.method}
122-
className="flex items-center gap-3 rounded-md border p-3 hover:bg-muted/50"
123-
>
124-
<MethodBadge method={ep.method} />
125-
<code className="flex-1 text-sm font-mono">{ep.path}</code>
126-
<span className="text-sm text-muted-foreground">{ep.description}</span>
127-
{ep.auth && <Badge variant="outline" className="text-xs">需认证</Badge>}
128-
</div>
218+
<EndpointItem key={ep.path + ep.method} ep={ep} />
129219
))}
130220
</div>
131221
</CardContent>

0 commit comments

Comments
 (0)