feat: Implement initial backend API server with various endpoints and background services, including an error account cleaner.

This commit is contained in:
2026-02-05 07:51:03 +08:00
parent ad1270b88d
commit db00d44a71
4 changed files with 22 additions and 14 deletions

View File

@@ -508,7 +508,7 @@ func handleS2ATest(w http.ResponseWriter, r *http.Request) {
} }
// 请求 S2A 仪表盘接口 // 请求 S2A 仪表盘接口
dashboardURL := config.Global.S2AApiBase + "/api/v1/admin/dashboard" dashboardURL := config.Global.S2AApiBase + "/api/v1/admin/dashboard/stats"
req, err := http.NewRequest("GET", dashboardURL, nil) req, err := http.NewRequest("GET", dashboardURL, nil)
if err != nil { if err != nil {
api.Error(w, http.StatusInternalServerError, "创建请求失败") api.Error(w, http.StatusInternalServerError, "创建请求失败")

View File

@@ -75,7 +75,7 @@ func getTotalAccountCount() (int, error) {
} }
client := &http.Client{Timeout: 30 * time.Second} client := &http.Client{Timeout: 30 * time.Second}
url := fmt.Sprintf("%s/api/v1/admin/dashboard", config.Global.S2AApiBase) url := fmt.Sprintf("%s/api/v1/admin/dashboard/stats", config.Global.S2AApiBase)
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { Users, CheckCircle, XCircle, AlertTriangle, Zap, Activity, TrendingUp } from 'lucide-react' import { Users, CheckCircle, XCircle, AlertTriangle, Zap, Activity } from 'lucide-react'
import type { DashboardStats } from '../../types' import type { DashboardStats } from '../../types'
import StatsCard from './StatsCard' import StatsCard from './StatsCard'
@@ -78,17 +78,15 @@ export default function PoolStatus({ stats, loading, error }: PoolStatusProps) {
color="slate" color="slate"
loading={loading} loading={loading}
/> />
{/* RPM 卡片 - 显示当前 RPM 和今日最高 */} <StatsCard
<div className="relative"> title="RPM"
<StatsCard title="RPM" value={stats?.rpm ?? 0} icon={Zap} color="blue" loading={loading} /> value={stats?.rpm ?? 0}
{/* 今日最高 RPM 显示在卡片下方 */} icon={Zap}
{maxRpm > 0 && ( color="blue"
<div className="absolute -bottom-5 left-0 right-0 flex items-center justify-center gap-1 text-xs text-slate-500 dark:text-slate-400"> loading={loading}
<TrendingUp className="h-3 w-3 text-orange-500" /> subtitle={maxRpm > 0 ? `📈 今日最高: ${maxRpm}` : undefined}
<span>: <span className="font-semibold text-orange-500">{maxRpm}</span></span> />
</div>
)}
</div>
</div> </div>
) )
} }

View File

@@ -10,6 +10,8 @@ interface StatsCardProps {
trendValue?: string trendValue?: string
color?: 'blue' | 'green' | 'yellow' | 'red' | 'slate' color?: 'blue' | 'green' | 'yellow' | 'red' | 'slate'
loading?: boolean loading?: boolean
subtitle?: string
subtitleColor?: string
} }
export default function StatsCard({ export default function StatsCard({
@@ -20,6 +22,8 @@ export default function StatsCard({
trendValue, trendValue,
color = 'blue', color = 'blue',
loading = false, loading = false,
subtitle,
subtitleColor = 'text-orange-500',
}: StatsCardProps) { }: StatsCardProps) {
const colorStyles = { const colorStyles = {
blue: { blue: {
@@ -80,6 +84,11 @@ export default function StatsCard({
</span> </span>
</div> </div>
)} )}
{subtitle && (
<p className={`mt-1 text-xs font-medium ${subtitleColor}`}>
{subtitle}
</p>
)}
{trend && trendValue && TrendIcon && ( {trend && trendValue && TrendIcon && (
<div className={`mt-2 flex items-center gap-1 text-sm ${trendStyles[trend].color}`}> <div className={`mt-2 flex items-center gap-1 text-sm ${trendStyles[trend].color}`}>
<TrendIcon className="h-4 w-4" /> <TrendIcon className="h-4 w-4" />
@@ -94,3 +103,4 @@ export default function StatsCard({
</Card> </Card>
) )
} }