feat: add backend API for flexible account record upload and validation, including concurrent account_id fetching, and a new frontend upload page.

This commit is contained in:
2026-01-30 13:14:12 +08:00
parent 2b9831539c
commit 2a0a8941c2
2 changed files with 15 additions and 7 deletions

View File

@@ -7,9 +7,9 @@ import (
"net/http" "net/http"
"strings" "strings"
"sync" "sync"
"time"
"unicode" "unicode"
"codex-pool/internal/client"
"codex-pool/internal/database" "codex-pool/internal/database"
"codex-pool/internal/logger" "codex-pool/internal/logger"
) )
@@ -255,7 +255,10 @@ func normalizeOwner(rec accountRecord, index int) (database.TeamOwner, error) {
// fetchAccountID 通过 token 获取 account_id // fetchAccountID 通过 token 获取 account_id
func fetchAccountID(token string) (string, error) { func fetchAccountID(token string) (string, error) {
client := &http.Client{Timeout: 15 * time.Second} tlsClient, err := client.New("")
if err != nil {
return "", fmt.Errorf("创建 TLS 客户端失败: %v", err)
}
req, err := http.NewRequest("GET", "https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27", nil) req, err := http.NewRequest("GET", "https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27", nil)
if err != nil { if err != nil {
@@ -264,9 +267,8 @@ func fetchAccountID(token string) (string, error) {
req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
resp, err := client.Do(req) resp, err := tlsClient.Do(req)
if err != nil { if err != nil {
return "", fmt.Errorf("请求失败: %v", err) return "", fmt.Errorf("请求失败: %v", err)
} }
@@ -276,7 +278,7 @@ func fetchAccountID(token string) (string, error) {
return "", fmt.Errorf("API 返回状态码: %d", resp.StatusCode) return "", fmt.Errorf("API 返回状态码: %d", resp.StatusCode)
} }
body, err := io.ReadAll(resp.Body) body, err := client.ReadBody(resp)
if err != nil { if err != nil {
return "", fmt.Errorf("读取响应失败: %v", err) return "", fmt.Errorf("读取响应失败: %v", err)
} }

View File

@@ -58,6 +58,7 @@ export default function Upload() {
const [status, setStatus] = useState<ProcessStatus | null>(null) const [status, setStatus] = useState<ProcessStatus | null>(null)
const [polling, setPolling] = useState(false) const [polling, setPolling] = useState(false)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [refreshing, setRefreshing] = useState(false)
const [importResult, setImportResult] = useState<{ const [importResult, setImportResult] = useState<{
imported: number imported: number
total: number total: number
@@ -232,8 +233,13 @@ export default function Upload() {
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => { loadStats(); fetchStatus(); }} disabled={refreshing}
icon={<RefreshCw className={`h-4 w-4 ${polling ? 'animate-spin' : ''}`} />} onClick={async () => {
setRefreshing(true)
await Promise.all([loadStats(), fetchStatus()])
setTimeout(() => setRefreshing(false), 500)
}}
icon={<RefreshCw className={`h-4 w-4 ${refreshing || polling ? 'animate-spin' : ''}`} />}
> >
</Button> </Button>