package service import ( "archive/zip" "bytes" "encoding/json" "fmt" "time" "gpt-plus/internal/db" "gorm.io/gorm" ) type authFile struct { OpenAIAPIKey string `json:"OPENAI_API_KEY"` AccessToken string `json:"access_token"` IDToken string `json:"id_token"` LastRefresh string `json:"last_refresh"` RefreshToken string `json:"refresh_token"` Tokens authTokens `json:"tokens"` } type authTokens struct { AccessToken string `json:"access_token"` AccountID string `json:"account_id"` IDToken string `json:"id_token"` LastRefresh string `json:"last_refresh"` RefreshToken string `json:"refresh_token"` } func buildAuthFile(acct *db.Account) *authFile { now := time.Now().UTC().Format("2006-01-02T15:04:05Z") token := acct.AccessToken if acct.WorkspaceToken != "" { token = acct.WorkspaceToken } accountID := acct.AccountID if acct.TeamWorkspaceID != "" { accountID = acct.TeamWorkspaceID } return &authFile{ AccessToken: token, IDToken: acct.IDToken, LastRefresh: now, RefreshToken: acct.RefreshToken, Tokens: authTokens{ AccessToken: token, AccountID: accountID, IDToken: acct.IDToken, LastRefresh: now, RefreshToken: acct.RefreshToken, }, } } // ExportAccounts exports accounts as auth.json files. // If multiple files, returns a zip archive. func ExportAccounts(d *gorm.DB, ids []uint, note string) ([]byte, string, error) { var accounts []db.Account d.Where("id IN ?", ids).Find(&accounts) if len(accounts) == 0 { return nil, "", fmt.Errorf("未找到账号") } // Collect all files to export type exportFile struct { Name string Data []byte } var files []exportFile for _, acct := range accounts { auth := buildAuthFile(&acct) data, _ := json.MarshalIndent(auth, "", " ") files = append(files, exportFile{ Name: acct.Email + ".auth.json", Data: data, }) // If team_owner, also export sub-accounts if acct.Plan == "team_owner" { var subs []db.Account d.Where("parent_id = ?", acct.ID).Find(&subs) for _, sub := range subs { subAuth := buildAuthFile(&sub) subData, _ := json.MarshalIndent(subAuth, "", " ") files = append(files, exportFile{ Name: sub.Email + ".auth.json", Data: subData, }) } } } // Single file — return directly if len(files) == 1 { return files[0].Data, files[0].Name, nil } // Multiple files — zip archive var buf bytes.Buffer zw := zip.NewWriter(&buf) for _, f := range files { w, err := zw.Create(f.Name) if err != nil { return nil, "", err } w.Write(f.Data) } zw.Close() ts := time.Now().Format("20060102_150405") filename := fmt.Sprintf("export_%s_%s.zip", note, ts) if note == "" { filename = fmt.Sprintf("export_%s.zip", ts) } return buf.Bytes(), filename, nil }