Files
codexautopool/backend/cmd/upload.go

274 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"unicode"
"codex-pool/internal/api"
"codex-pool/internal/database"
)
type uploadValidateRequest struct {
Content string `json:"content"`
Accounts []accountRecord `json:"accounts"`
}
type accountRecord struct {
Account string `json:"account"`
Email string `json:"email"`
Password string `json:"password"`
Token string `json:"token"`
AccessTok string `json:"access_token"`
AccountID string `json:"account_id"`
}
func handleUploadValidate(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
api.Error(w, http.StatusMethodNotAllowed, "仅支持 POST")
return
}
if database.Instance == nil {
api.Error(w, http.StatusInternalServerError, "数据库未初始化")
return
}
body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 10<<20))
if err != nil {
api.Error(w, http.StatusBadRequest, "读取请求失败")
return
}
var req uploadValidateRequest
if err := json.Unmarshal(body, &req); err != nil {
// 如果不是 JSON直接把 body 当作原始内容
req.Content = string(body)
}
var records []accountRecord
switch {
case len(req.Accounts) > 0:
records = req.Accounts
case strings.TrimSpace(req.Content) != "":
parsed, parseErr := parseAccountsFlexible(req.Content)
if parseErr != nil {
api.Error(w, http.StatusBadRequest, parseErr.Error())
return
}
records = parsed
default:
api.Error(w, http.StatusBadRequest, "未提供账号内容")
return
}
owners := make([]database.TeamOwner, 0, len(records))
for i, rec := range records {
owner, err := normalizeOwner(rec, i+1)
if err != nil {
api.Error(w, http.StatusBadRequest, err.Error())
return
}
owners = append(owners, owner)
}
if len(owners) == 0 {
api.Error(w, http.StatusBadRequest, "未解析到有效账号")
return
}
inserted, err := database.Instance.AddTeamOwners(owners)
if err != nil {
api.Error(w, http.StatusInternalServerError, fmt.Sprintf("写入数据库失败: %v", err))
return
}
stats := database.Instance.GetOwnerStats()
api.Success(w, map[string]interface{}{
"imported": inserted,
"total": len(owners),
"stats": stats,
})
}
func normalizeOwner(rec accountRecord, index int) (database.TeamOwner, error) {
email := strings.TrimSpace(rec.Email)
if email == "" {
email = strings.TrimSpace(rec.Account)
}
if email == "" {
return database.TeamOwner{}, fmt.Errorf("第 %d 条记录缺少 account/email 字段", index)
}
password := strings.TrimSpace(rec.Password)
if password == "" {
return database.TeamOwner{}, fmt.Errorf("第 %d 条记录缺少 password 字段", index)
}
token := strings.TrimSpace(rec.Token)
if token == "" {
token = strings.TrimSpace(rec.AccessTok)
}
if token == "" {
return database.TeamOwner{}, fmt.Errorf("第 %d 条记录缺少 token 字段", index)
}
accountID := strings.TrimSpace(rec.AccountID)
return database.TeamOwner{
Email: email,
Password: password,
Token: token,
AccountID: accountID,
}, nil
}
func parseAccountsFlexible(raw string) ([]accountRecord, error) {
raw = strings.TrimSpace(strings.TrimPrefix(raw, "\uFEFF"))
if raw == "" {
return nil, fmt.Errorf("内容为空")
}
cleaned := stripJSONComments(raw)
cleaned = removeTrailingCommas(cleaned)
trimmed := strings.TrimSpace(cleaned)
if trimmed == "" {
return nil, fmt.Errorf("内容为空")
}
if strings.HasPrefix(trimmed, "[") {
var arr []accountRecord
if err := json.Unmarshal([]byte(trimmed), &arr); err == nil {
return arr, nil
}
} else if strings.HasPrefix(trimmed, "{") {
var single accountRecord
if err := json.Unmarshal([]byte(trimmed), &single); err == nil {
return []accountRecord{single}, nil
}
}
// JSONL 回退
lines := strings.Split(raw, "\n")
records := make([]accountRecord, 0, len(lines))
for i, line := range lines {
line = strings.TrimSpace(stripJSONComments(line))
if line == "" {
continue
}
if strings.HasPrefix(line, "#") {
continue
}
line = strings.TrimSpace(removeTrailingCommas(line))
if line == "" {
continue
}
var rec accountRecord
if err := json.Unmarshal([]byte(line), &rec); err != nil {
return nil, fmt.Errorf("第 %d 行解析失败: %v", i+1, err)
}
records = append(records, rec)
}
if len(records) == 0 {
return nil, fmt.Errorf("未解析到有效账号")
}
return records, nil
}
func stripJSONComments(input string) string {
var b strings.Builder
b.Grow(len(input))
inString := false
escaped := false
for i := 0; i < len(input); i++ {
ch := input[i]
if inString {
b.WriteByte(ch)
if escaped {
escaped = false
continue
}
if ch == '\\' {
escaped = true
} else if ch == '"' {
inString = false
}
continue
}
if ch == '"' {
inString = true
b.WriteByte(ch)
continue
}
if ch == '/' && i+1 < len(input) && input[i+1] == '/' {
for i+1 < len(input) && input[i+1] != '\n' {
i++
}
continue
}
if ch == '/' && i+1 < len(input) && input[i+1] == '*' {
i += 2
for i+1 < len(input) {
if input[i] == '*' && input[i+1] == '/' {
i++
break
}
i++
}
continue
}
b.WriteByte(ch)
}
return b.String()
}
func removeTrailingCommas(input string) string {
var b strings.Builder
b.Grow(len(input))
inString := false
escaped := false
for i := 0; i < len(input); i++ {
ch := input[i]
if inString {
b.WriteByte(ch)
if escaped {
escaped = false
continue
}
if ch == '\\' {
escaped = true
} else if ch == '"' {
inString = false
}
continue
}
if ch == '"' {
inString = true
b.WriteByte(ch)
continue
}
if ch == ',' {
j := i + 1
for j < len(input) && unicode.IsSpace(rune(input[j])) {
j++
}
if j < len(input) && (input[j] == ']' || input[j] == '}') {
continue
}
}
b.WriteByte(ch)
}
return b.String()
}