New: 加了个Http服务器 现在可以用接口获取分数了 //apidoc稍后补上(100%AI 无人工添加 事实上是主播不会写Service没找了哈哈)

This commit is contained in:
NanGua-QWQ
2026-02-05 22:07:12 +08:00
parent 08d58bbf11
commit fa75b88f11
14 changed files with 927 additions and 324 deletions
+186
View File
@@ -62,6 +62,17 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
const [settleDialogVisible, setSettleDialogVisible] = useState(false)
const [urlRegisterLoading, setUrlRegisterLoading] = useState(false)
// HTTP Server状态
const [httpServerStatus, setHttpServerStatus] = useState<{
isRunning: boolean
config: { port: number; host: string; corsOrigin?: string }
url: string | null
} | null>(null)
const [httpLoading, setHttpLoading] = useState(false)
const [httpPort, setHttpPort] = useState('3000')
const [httpHost, setHttpHost] = useState('localhost')
const [httpCorsOrigin, setHttpCorsOrigin] = useState('*')
const canAdmin = permission === 'admin'
@@ -92,6 +103,7 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
useEffect(() => {
loadAll()
refreshHttpServerStatus()
if (!(window as any).api) return
const unsubscribe = (window as any).api.onSettingChanged((change: any) => {
setSettings((prev) => {
@@ -247,6 +259,72 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
if (!(window as any).api) return
setSettleDialogVisible(true)
}
// HTTP Server函数
const refreshHttpServerStatus = async () => {
if (!(window as any).api) return
try {
const res = await (window as any).api.httpServerStatus()
if (res.success && res.data) {
setHttpServerStatus(res.data)
setHttpPort(res.data.config.port.toString())
setHttpHost(res.data.config.host)
setHttpCorsOrigin(res.data.config.corsOrigin || '*')
}
} catch (error) {
console.error('Failed to refresh HTTP server status:', error)
}
}
const handleHttpServerStart = async () => {
if (!(window as any).api) return
setHttpLoading(true)
try {
const config: any = {}
const portNum = parseInt(httpPort)
if (!isNaN(portNum) && portNum > 0 && portNum <= 65535) {
config.port = portNum
}
if (httpHost.trim()) {
config.host = httpHost.trim()
}
if (httpCorsOrigin.trim()) {
config.corsOrigin = httpCorsOrigin.trim()
}
const res = await (window as any).api.httpServerStart(config)
if (res.success) {
MessagePlugin.success(`HTTP服务器已启动: ${res.data.url}`)
await refreshHttpServerStatus()
} else {
MessagePlugin.error(res.message || '启动失败')
}
} catch (error) {
MessagePlugin.error('启动HTTP服务器时发生错误')
console.error(error)
} finally {
setHttpLoading(false)
}
}
const handleHttpServerStop = async () => {
if (!(window as any).api) return
setHttpLoading(true)
try {
const res = await (window as any).api.httpServerStop()
if (res.success) {
MessagePlugin.success('HTTP服务器已停止')
await refreshHttpServerStatus()
} else {
MessagePlugin.error(res.message || '停止失败')
}
} catch (error) {
MessagePlugin.error('停止HTTP服务器时发生错误')
console.error(error)
} finally {
setHttpLoading(false)
}
}
return (
<div style={{ padding: '24px', maxWidth: '900px', margin: '0 auto' }}>
@@ -539,6 +617,114 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
</Card>
</Tabs.TabPanel>
<Tabs.TabPanel value="http" label="HTTP服务器">
<Card style={{ backgroundColor: 'var(--ss-card-bg)', color: 'var(--ss-text-main)' }}>
<div style={{ fontSize: '16px', fontWeight: 600, marginBottom: '8px' }}>
HTTP服务器 API
</div>
<Divider />
<div
style={{
marginBottom: '12px',
fontSize: '13px',
color: 'var(--ss-text-secondary)'
}}
>
HTTP API提供学生分数查询服务访
</div>
<Form labelWidth={120}>
<Form.FormItem label="服务器状态">
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Tag
theme={httpServerStatus?.isRunning ? 'success' : 'default'}
variant="light"
>
{httpServerStatus?.isRunning ? '运行中' : '已停止'}
</Tag>
{httpServerStatus?.isRunning && (
<div style={{ fontSize: '12px', color: 'var(--ss-text-secondary)' }}>
访: {httpServerStatus.url}
</div>
)}
</div>
</Form.FormItem>
<Form.FormItem label="端口设置">
<Input
value={httpPort}
onChange={(v) => setHttpPort(v)}
placeholder="端口号 (默认: 3000)"
style={{ width: '200px' }}
disabled={!canAdmin || httpLoading}
/>
</Form.FormItem>
<Form.FormItem label="主机地址">
<Input
value={httpHost}
onChange={(v) => setHttpHost(v)}
placeholder="主机地址 (默认: localhost)"
style={{ width: '200px' }}
disabled={!canAdmin || httpLoading}
/>
</Form.FormItem>
<Form.FormItem label="CORS来源">
<Input
value={httpCorsOrigin}
onChange={(v) => setHttpCorsOrigin(v)}
placeholder="CORS来源 (默认: *)"
style={{ width: '200px' }}
disabled={!canAdmin || httpLoading}
/>
<div style={{ marginTop: '4px', fontSize: '12px', color: 'var(--ss-text-secondary)' }}>
*
</div>
</Form.FormItem>
<Form.FormItem label="操作">
<Space>
<Button
theme="primary"
loading={httpLoading}
disabled={!canAdmin}
onClick={handleHttpServerStart}
>
{httpServerStatus?.isRunning ? '重启服务器' : '启动服务器'}
</Button>
<Button
theme="danger"
variant="outline"
loading={httpLoading}
disabled={!canAdmin || !httpServerStatus?.isRunning}
onClick={handleHttpServerStop}
>
</Button>
<Button
variant="outline"
disabled={!canAdmin || httpLoading}
onClick={refreshHttpServerStatus}
>
</Button>
</Space>
</Form.FormItem>
<Form.FormItem label="API端点">
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px', fontSize: '12px' }}>
<div> GET /health - </div>
<div> GET /api/students/scores - </div>
<div> GET /api/students/:id/score - </div>
<div> GET /api/students/search?name= - </div>
<div> GET /api/leaderboard?limit=10 - </div>
</div>
</Form.FormItem>
</Form>
</Card>
</Tabs.TabPanel>
<Tabs.TabPanel value="url" label="URL 链接">
<Card style={{ backgroundColor: 'var(--ss-card-bg)', color: 'var(--ss-text-main)' }}>
<div style={{ fontSize: '16px', fontWeight: 600, marginBottom: '8px' }}>
+2 -2
View File
@@ -6,7 +6,7 @@ import {
RootListIcon,
ViewListIcon,
HomeIcon,
MoneyIcon
ReplayIcon
} from 'tdesign-icons-react'
import appLogo from '../assets/logoHD.svg'
@@ -78,7 +78,7 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
</Menu.MenuItem>
<Menu.MenuItem value="leaderboard" icon={<ViewListIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="auto-score" icon={<MoneyIcon />}>
<Menu.MenuItem value="auto-score" icon={<ReplayIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="settlements" icon={<HistoryIcon />}>
</Menu.MenuItem>