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"
"strings"
"sync"
"time"
"unicode"
"codex-pool/internal/client"
"codex-pool/internal/database"
"codex-pool/internal/logger"
)
@@ -255,7 +255,10 @@ func normalizeOwner(rec accountRecord, index int) (database.TeamOwner, error) {
// fetchAccountID 通过 token 获取 account_id
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)
if err != nil {
@@ -264,9 +267,8 @@ func fetchAccountID(token string) (string, error) {
req.Header.Set("Authorization", "Bearer "+token)
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 {
return "", fmt.Errorf("请求失败: %v", err)
}
@@ -276,7 +278,7 @@ func fetchAccountID(token string) (string, error) {
return "", fmt.Errorf("API 返回状态码: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
body, err := client.ReadBody(resp)
if err != nil {
return "", fmt.Errorf("读取响应失败: %v", err)
}

View File

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