109 lines
4.1 KiB
TypeScript
Executable File
109 lines
4.1 KiB
TypeScript
Executable File
import { CheckCircle2, Eye, KeyRound, PlugZap, ShieldCheck } from "lucide-react";
|
||
import { wallMuseApi } from "@wallmuse/api-client";
|
||
import { AppShell } from "../../../components/app-shell";
|
||
|
||
export default async function ApiKeysPage() {
|
||
const keys = await wallMuseApi.listApiKeys();
|
||
|
||
return (
|
||
<AppShell>
|
||
<div className="page-grid">
|
||
<section className="page-title">
|
||
<p className="eyebrow">
|
||
<KeyRound size={18} strokeWidth={1.75} />
|
||
User API Key
|
||
</p>
|
||
<h1>绑定自己的模型 API Key。</h1>
|
||
<p>前端只提交到本站 API。Key 的加密保存、连通性测试和调用日志由后端接口处理。</p>
|
||
</section>
|
||
|
||
<section className="settings-layout">
|
||
<form className="glass-panel panel-pad">
|
||
<div className="field">
|
||
<label htmlFor="provider">Provider</label>
|
||
<select id="provider" defaultValue="OpenAI Compatible">
|
||
<option>OpenAI Compatible</option>
|
||
<option>SiliconFlow</option>
|
||
<option>Qwen Image</option>
|
||
<option>Seedream</option>
|
||
</select>
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="baseUrl">Base URL</label>
|
||
<input id="baseUrl" defaultValue="https://api.example.com/v1" />
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="apiKey">API Key</label>
|
||
<input id="apiKey" type="password" defaultValue="sk-wallmuse-demo-key" />
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="model">Model ID</label>
|
||
<input id="model" defaultValue="gpt-image-1" />
|
||
</div>
|
||
|
||
<div className="switch-row">
|
||
<span>
|
||
<strong>保存到账户</strong>
|
||
<br />
|
||
<small>服务端加密保存,前端不读取明文。</small>
|
||
</span>
|
||
<input type="checkbox" defaultChecked aria-label="Save key to account" />
|
||
</div>
|
||
|
||
<div className="switch-row">
|
||
<span>
|
||
<strong>设为默认生成模型</strong>
|
||
<br />
|
||
<small>生成页默认选择此供应商和模型。</small>
|
||
</span>
|
||
<input type="checkbox" defaultChecked aria-label="Set as default key" />
|
||
</div>
|
||
|
||
<div className="quick-specs">
|
||
<button className="primary-button" type="button">
|
||
<ShieldCheck size={18} strokeWidth={1.75} />
|
||
Save key
|
||
</button>
|
||
<button className="glass-button" type="button">
|
||
<PlugZap size={18} strokeWidth={1.75} />
|
||
Test connection
|
||
</button>
|
||
<button className="glass-button" type="button" aria-label="Show key">
|
||
<Eye size={18} strokeWidth={1.75} />
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
<div className="glass-panel panel-pad">
|
||
<div className="section-heading">
|
||
<div>
|
||
<h2>已保存 Key</h2>
|
||
<p>只展示 masked key,不暴露完整密钥。</p>
|
||
</div>
|
||
</div>
|
||
<div className="task-list">
|
||
{keys.map((key) => (
|
||
<div className="key-row" key={key.id}>
|
||
<div className="status-line">
|
||
<strong>{key.provider}</strong>
|
||
<span className="score">
|
||
<CheckCircle2 size={16} strokeWidth={1.75} />
|
||
{key.status}
|
||
</span>
|
||
</div>
|
||
<span>{key.baseUrl}</span>
|
||
<span>{key.model} · {key.maskedKey}</span>
|
||
<div className="quick-specs">
|
||
<span className="pill">{key.isDefault ? "Default" : "Optional"}</span>
|
||
<span className="pill">{new Date(key.updatedAt).toLocaleString()}</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</AppShell>
|
||
);
|
||
}
|