Initial sanitized code sync
This commit is contained in:
121
internal/service/export_svc.go
Normal file
121
internal/service/export_svc.go
Normal file
@@ -0,0 +1,121 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user