aurora / internal /bootstrap /bootstrap.go
qwen2api's picture
Upload 113 files
6bc074c verified
Raw
History Blame Contribute Delete
7.16 kB
package bootstrap
import (
"os"
"strings"
"time"
"aurora/internal/accounts"
"aurora/internal/browserfp"
"aurora/internal/chatgpt"
"aurora/internal/config"
"aurora/internal/handler"
"aurora/internal/proxy"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/joho/godotenv"
)
// App 封装应用启动所需的所有依赖
type App struct {
Router *gin.Engine
Config *config.Config
AccountPool *accounts.Pool
Cleanup func() // 服务退出时调用,停止后台协程
}
// Init 完成所有初始化逻辑,返回 App 实例
func Init() (*App, error) {
gin.SetMode(gin.ReleaseMode)
_ = godotenv.Load(".env")
browserfp.Init()
cfg := config.Load()
proxies := loadProxyList()
proxyPool := proxy.NewPool(proxies)
// ─── 加载账号 ────────────────────────────────────────────────
profiles := accounts.DefaultProfiles
var accs []*accounts.Account
// 去重 key: chatgpt_account_id (for free/puid) 或 token (for noauth/refresh)
seen := make(map[string]bool)
// 1. access_tokens.txt — 纯 access token,不可续期
for _, t := range accounts.LoadTokensFromFile("access_tokens.txt") {
// 从 access_token JWT 解析 chatgpt_account_id 用于去重
chatGPTID := accounts.ExtractChatGPTAccountID(t.Token)
key := "access:" + chatGPTID
if seen[key] {
continue
}
seen[key] = true
acct := accounts.CreateAccount(t.Token, accounts.TypeFree, profiles)
acct.TeamUserID = t.TeamID
acct.ChatGPTAccountID = chatGPTID
acct.Status = accounts.StatusActive
acct.Proxy = proxyPool.Allocate()
accs = append(accs, acct)
}
// 2. refresh_tokens.txt — 带 refresh_token,可续期
for _, t := range accounts.LoadTokensFromFile("refresh_tokens.txt") {
// 立即交换一次获取 access_token 才能解析 chatgpt_account_id
// 先用 refresh_token 本身做去重 key,避免重复交换
key := "refresh:" + t.Token
if seen[key] {
continue
}
seen[key] = true
acct := accounts.CreateAccount("", accounts.TypeFree, profiles)
acct.RefreshToken = t.Token
acct.TeamUserID = t.TeamID
acct.Proxy = proxyPool.Allocate()
// 立即交换一次获取 access_token
if exchangeRefreshToken(acct) {
// exchange 成功后从 access_token 解析 chatgpt_account_id
acct.ChatGPTAccountID = accounts.ExtractChatGPTAccountID(acct.Token)
acct.Status = accounts.StatusActive
} else {
acct.Status = accounts.StatusExpired
}
accs = append(accs, acct)
}
// 3. session_tokens.txt — ChatGPT session token,用于免费账号续期
for _, t := range accounts.LoadTokensFromFile("session_tokens.txt") {
key := "session:" + t.Token
if seen[key] {
continue
}
seen[key] = true
acct := accounts.CreateAccount("", accounts.TypeFree, profiles)
acct.SessionToken = t.Token
acct.Proxy = proxyPool.Allocate()
// 立即交换一次获取 access_token
if exchangeSessionToken(acct) {
// exchange 成功后从 access_token 解析 chatgpt_account_id
acct.ChatGPTAccountID = accounts.ExtractChatGPTAccountID(acct.Token)
acct.Status = accounts.StatusActive
} else {
acct.Status = accounts.StatusExpired
}
accs = append(accs, acct)
}
// 4. free_tokens.txt — 设备 UUID
for _, t := range accounts.LoadTokensFromFile("free_tokens.txt") {
key := "free:" + t.Token
if seen[key] {
continue
}
seen[key] = true
acct := accounts.CreateAccount(t.Token, accounts.TypeNoAuth, profiles)
acct.Proxy = proxyPool.Allocate()
acct.Status = accounts.StatusActive
accs = append(accs, acct)
}
// 5. FREE_ACCOUNTS — 自动生成 UUID 账号
if cfg.FreeAccounts {
for i := 0; i < cfg.FreeAccountsNum; i++ {
uid := uuid.NewString()
// UUID 设备号天然唯一,不需要去重
acct := accounts.CreateAccount(uid, accounts.TypeNoAuth, profiles)
acct.Proxy = proxyPool.Allocate()
acct.Status = accounts.StatusActive
accs = append(accs, acct)
}
}
// 初始化 TLS Client
for _, acct := range accs {
_ = acct.InitClient()
}
accountPool := accounts.NewPool(accs)
// 启动健康检查(每 10 分钟续期过期 token)
renewFn := func(acct *accounts.Account) bool {
if acct.RefreshToken != "" {
return exchangeRefreshToken(acct)
}
if acct.SessionToken != "" {
return exchangeSessionToken(acct)
}
return false
}
stopHealthCheck := accountPool.StartHealthCheck(10*time.Minute, renewFn)
stopTempGC := accountPool.StartTempAccountGC(10*time.Minute, 1*time.Minute)
// 注册路由
router := handler.RegisterRouter(accountPool, &cfg)
return &App{
Router: router,
Config: &cfg,
AccountPool: accountPool,
Cleanup: func() {
stopHealthCheck()
stopTempGC()
},
}, nil
}
// exchangeRefreshToken 用 refresh_token 换 access_token,使用账号自身的 Client(已绑定代理)
func exchangeRefreshToken(acct *accounts.Account) bool {
if acct.RefreshToken == "" {
return false
}
if acct.Client == nil {
_ = acct.InitClient()
}
result, _, err := chatgpt.GETTokenForRefreshToken(acct.Client, acct.RefreshToken, "")
if err != nil {
return false
}
// 优先尝试 struct 形式 (*OpenAIAccessTokenWithSession 等)
if r, ok := result.(interface{ GetAccessToken() string }); ok {
if at := r.GetAccessToken(); at != "" {
acct.Token = at
return true
}
}
// fallback: map[string]interface{} (旧 API 直接返回 map)
if data, ok := result.(map[string]interface{}); ok {
if accessToken, ok := data["access_token"].(string); ok && accessToken != "" {
acct.Token = accessToken
return true
}
}
return false
}
// exchangeSessionToken 用 session_token 换 access_token,使用账号自身的 Client(已绑定代理)
func exchangeSessionToken(acct *accounts.Account) bool {
if acct.SessionToken == "" {
return false
}
if acct.Client == nil {
_ = acct.InitClient()
}
result, _, err := chatgpt.GETTokenForSessionToken(acct.Client, acct.SessionToken, "")
if err != nil {
return false
}
// 优先尝试 struct 形式 (*OpenAIAccessTokenWithSession)
if r, ok := result.(interface{ GetAccessToken() string }); ok {
if at := r.GetAccessToken(); at != "" {
acct.Token = at
return true
}
}
// fallback: map[string]interface{}
if data, ok := result.(map[string]interface{}); ok {
if accessToken, ok := data["accessToken"].(string); ok && accessToken != "" {
acct.Token = accessToken
return true
}
}
return false
}
// loadProxyList 从 proxies.txt / PROXY_URL / http_proxy 加载代理列表
func loadProxyList() []string {
var proxies []string
proxyUrl := os.Getenv("PROXY_URL")
if proxyUrl != "" {
proxies = append(proxies, proxyUrl)
}
if _, err := os.Stat("proxies.txt"); err == nil {
data, err := os.ReadFile("proxies.txt")
if err == nil {
lines := string(data)
for _, line := range strings.Split(lines, "\n") {
line = strings.TrimSpace(line)
if line != "" {
proxies = append(proxies, line)
}
}
}
}
if len(proxies) == 0 {
proxy := os.Getenv("http_proxy")
if proxy != "" {
proxies = append(proxies, proxy)
}
}
return proxies
}