上传完整的WebUI前端仓库

This commit is contained in:
墨梓柒
2026-01-13 06:24:35 +08:00
parent a9187dc312
commit 812296590e
184 changed files with 47854 additions and 1 deletions

View File

@@ -0,0 +1,203 @@
import React, { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import { Plus, Trash2 } from 'lucide-react'
import type { MaimMessageConfig } from '../types'
interface MaimMessageSectionProps {
config: MaimMessageConfig
onChange: (config: MaimMessageConfig) => void
}
export const MaimMessageSection = React.memo(function MaimMessageSection({ config, onChange }: MaimMessageSectionProps) {
const [newToken, setNewToken] = useState('')
const [newApiKey, setNewApiKey] = useState('')
const addToken = () => {
if (newToken && !config.auth_token.includes(newToken)) {
onChange({ ...config, auth_token: [...config.auth_token, newToken] })
setNewToken('')
}
}
const removeToken = (index: number) => {
onChange({
...config,
auth_token: config.auth_token.filter((_, i) => i !== index),
})
}
const addApiKey = () => {
if (newApiKey && !config.api_server_allowed_api_keys.includes(newApiKey)) {
onChange({ ...config, api_server_allowed_api_keys: [...config.api_server_allowed_api_keys, newApiKey] })
setNewApiKey('')
}
}
const removeApiKey = (index: number) => {
onChange({
...config,
api_server_allowed_api_keys: config.api_server_allowed_api_keys.filter((_, i) => i !== index),
})
}
return (
<div className="rounded-lg border bg-card p-4 sm:p-6 space-y-6">
{/* 认证令牌 */}
<div>
<h3 className="text-lg font-semibold mb-2"> API </h3>
<p className="text-sm text-muted-foreground mb-3"> API </p>
<div className="flex gap-2 mb-2">
<Input
value={newToken}
onChange={(e) => setNewToken(e.target.value)}
placeholder="输入认证令牌"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
addToken()
}
}}
/>
<Button onClick={addToken} size="sm">
<Plus className="h-4 w-4" strokeWidth={2} fill="none" />
</Button>
</div>
<div className="space-y-2">
{config.auth_token.map((token, index) => (
<div
key={index}
className="flex items-center justify-between bg-secondary px-3 py-2 rounded-md"
>
<span className="text-sm font-mono">{token}</span>
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0"
onClick={() => removeToken(index)}
>
<Trash2 className="h-3 w-3" strokeWidth={2} fill="none" />
</Button>
</div>
))}
</div>
</div>
{/* 新版 API Server */}
<div>
<h3 className="text-lg font-semibold mb-4"> API Server </h3>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label> API Server</Label>
<p className="text-sm text-muted-foreground">
API Server
</p>
</div>
<Switch
checked={config.enable_api_server}
onCheckedChange={(checked) => onChange({ ...config, enable_api_server: checked })}
/>
</div>
{config.enable_api_server && (
<>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="grid gap-2">
<Label></Label>
<Input
value={config.api_server_host}
onChange={(e) => onChange({ ...config, api_server_host: e.target.value })}
placeholder="0.0.0.0"
/>
</div>
<div className="grid gap-2">
<Label></Label>
<Input
type="number"
value={config.api_server_port}
onChange={(e) => onChange({ ...config, api_server_port: parseInt(e.target.value) })}
placeholder="8090"
/>
</div>
</div>
<div className="flex items-center space-x-2">
<Switch
checked={config.api_server_use_wss}
onCheckedChange={(checked) => onChange({ ...config, api_server_use_wss: checked })}
/>
<Label> WSS </Label>
</div>
{config.api_server_use_wss && (
<div className="grid gap-4">
<div className="grid gap-2">
<Label>SSL </Label>
<Input
value={config.api_server_cert_file}
onChange={(e) => onChange({ ...config, api_server_cert_file: e.target.value })}
placeholder="cert.pem"
/>
</div>
<div className="grid gap-2">
<Label>SSL </Label>
<Input
value={config.api_server_key_file}
onChange={(e) => onChange({ ...config, api_server_key_file: e.target.value })}
placeholder="key.pem"
/>
</div>
</div>
)}
{/* API Keys */}
<div>
<Label className="mb-2 block"> API Key </Label>
<p className="text-sm text-muted-foreground mb-2"></p>
<div className="flex gap-2 mb-2">
<Input
value={newApiKey}
onChange={(e) => setNewApiKey(e.target.value)}
placeholder="输入 API Key"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
addApiKey()
}
}}
/>
<Button onClick={addApiKey} size="sm">
<Plus className="h-4 w-4" strokeWidth={2} fill="none" />
</Button>
</div>
<div className="space-y-2">
{config.api_server_allowed_api_keys.map((apiKey, index) => (
<div
key={index}
className="flex items-center justify-between bg-secondary px-3 py-2 rounded-md"
>
<span className="text-sm font-mono">{apiKey}</span>
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0"
onClick={() => removeApiKey(index)}
>
<Trash2 className="h-3 w-3" strokeWidth={2} fill="none" />
</Button>
</div>
))}
</div>
</div>
</>
)}
</div>
</div>
</div>
)
})