Files

218 lines
6.7 KiB
Go

package backend
import (
"ant-chrome/backend/internal/apppath"
"ant-chrome/backend/internal/automation"
"ant-chrome/backend/internal/browser"
"ant-chrome/backend/internal/config"
"ant-chrome/backend/internal/database"
"ant-chrome/backend/internal/launchcode"
"ant-chrome/backend/internal/logger"
"ant-chrome/backend/internal/proxy"
"context"
"fmt"
"os"
"time"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// startup 应用启动时调用
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
if err := apppath.EnsureWritableLayout(a.appRoot); err != nil {
runtime.LogFatal(ctx, fmt.Sprintf("初始化 Linux 用户数据目录失败: %v", err))
return
}
cfg := a.startupLoadConfig()
a.config = cfg
a.applyRuntimeConfig(cfg.Runtime)
log := a.startupInitLogger(ctx, cfg)
a.startupLogEnvironment(log, cfg)
if err := os.MkdirAll(a.resolveAppPath("data"), 0o755); err != nil {
log.Error("创建 data 目录失败", logger.F("error", err))
}
a.ensureDefaultCores()
a.startupInitInterceptor(log, cfg)
db, err := a.startupInitDatabase(cfg)
if err != nil {
log.Error("初始化数据库失败", logger.F("error", err))
runtime.LogFatal(ctx, fmt.Sprintf("初始化数据库失败: %v", err))
return
}
a.db = db
if err := db.Migrate(); err != nil {
log.Error("数据库迁移失败", logger.F("error", err))
}
a.startupInitManagers(cfg, db)
a.startupInitLaunchCode(log)
a.startupInitLaunchServer(log)
a.startupInitAutomation()
a.startupInitBridgeHooks()
a.startupInitSpeedScheduler()
log.Info("应用启动成功")
}
func (a *App) startupLoadConfig() *config.Config {
cfg, err := LoadConfig(a.resolveAppPath("config.yaml"))
if err != nil {
return config.DefaultConfig()
}
return cfg
}
func (a *App) startupInitLogger(ctx context.Context, cfg *config.Config) *logger.Logger {
logConfig := logger.LoggerConfig{
Level: cfg.Logging.Level,
FileEnabled: cfg.Logging.FileEnabled,
FilePath: a.resolveAppPath(cfg.Logging.FilePath),
Format: cfg.Logging.Format,
BufferSize: cfg.Logging.BufferSize,
AsyncQueueSize: cfg.Logging.AsyncQueueSize,
FlushIntervalMs: cfg.Logging.FlushIntervalMs,
Rotation: logger.RotationConfig{
Enabled: cfg.Logging.Rotation.Enabled,
MaxSizeMB: cfg.Logging.Rotation.MaxSizeMB,
MaxAge: cfg.Logging.Rotation.MaxAge,
MaxBackups: cfg.Logging.Rotation.MaxBackups,
TimeInterval: cfg.Logging.Rotation.TimeInterval,
},
}
logger.InitWithConfig(ctx, logConfig)
return logger.New("App")
}
func (a *App) startupLogEnvironment(log *logger.Logger, cfg *config.Config) {
log.Info("应用启动中...",
logger.F("version", a.appVersion()),
logger.F("max_memory_mb", cfg.Runtime.MaxMemoryMB),
logger.F("gc_percent", cfg.Runtime.GCPercent),
)
if apppath.IsDetached(a.appRoot) {
log.Info("检测到安装目录需要只读运行,已切换到用户数据目录",
logger.F("install_root", apppath.InstallRoot(a.appRoot)),
logger.F("state_root", apppath.StateRoot(a.appRoot)),
)
}
}
func (a *App) startupInitInterceptor(log *logger.Logger, cfg *config.Config) {
if !cfg.Logging.Interceptor.Enabled {
return
}
interceptorConfig := logger.InterceptorConfig{
Enabled: cfg.Logging.Interceptor.Enabled,
LogParameters: cfg.Logging.Interceptor.LogParameters,
LogResults: cfg.Logging.Interceptor.LogResults,
SensitiveFields: cfg.Logging.Interceptor.SensitiveFields,
}
a.interceptor = logger.NewMethodInterceptor(log, interceptorConfig)
}
func (a *App) startupInitDatabase(cfg *config.Config) (*database.DB, error) {
return database.NewDB(a.resolveAppPath(cfg.Database.SQLite.Path))
}
func (a *App) startupInitManagers(cfg *config.Config, db *database.DB) {
a.browserMgr = browser.NewManager(cfg, a.appRoot)
a.xrayMgr = proxy.NewXrayManager(cfg, a.appRoot)
a.clashMgr = proxy.NewClashManager(cfg, a.appRoot)
a.singboxMgr = proxy.NewSingBoxManager(cfg, a.appRoot)
conn := db.GetConn()
a.browserMgr.ProfileDAO = browser.NewSQLiteProfileDAO(conn)
a.browserMgr.ProxyDAO = browser.NewSQLiteProxyDAO(conn)
a.browserMgr.CoreDAO = browser.NewSQLiteCoreDAO(conn)
a.browserMgr.BookmarkDAO = browser.NewSQLiteBookmarkDAO(conn)
a.browserMgr.GroupDAO = browser.NewSQLiteGroupDAO(conn)
a.browserMgr.ExtensionDAO = browser.NewSQLiteExtensionDAO(conn)
a.migrateToSQLite()
a.browserMgr.InitData()
if err := a.browserMgr.CleanupExpiredTrash(); err != nil {
logger.New("Browser").Error("启动清理回收站失败", logger.F("error", err))
}
a.autoDetectCores()
a.loadProxies()
a.reconcileProfileProxyBindings()
}
func (a *App) startupInitLaunchCode(log *logger.Logger) {
launchCodeDAO := launchcode.NewSQLiteLaunchCodeDAO(a.db.GetConn())
a.launchCodeSvc = launchcode.NewLaunchCodeService(launchCodeDAO)
if err := a.launchCodeSvc.LoadAll(); err != nil {
log.Error("LaunchCode 加载失败", logger.F("error", err))
}
a.browserMgr.CodeProvider = a.launchCodeSvc
}
func (a *App) startupInitLaunchServer(log *logger.Logger) {
port := a.config.LaunchServer.Port
a.launchServer = launchcode.NewLaunchServer(a.launchCodeSvc, a, a.browserMgr, port)
a.launchServer.SetAPIAuthConfig(launchcode.APIAuthConfig{
Enabled: a.config.LaunchServer.Auth.Enabled,
APIKey: a.config.LaunchServer.Auth.APIKey,
Header: a.config.LaunchServer.Auth.Header,
})
if err := a.launchServer.Start(); err != nil {
log.Error("LaunchServer 启动失败", logger.F("error", err))
return
}
log.Info("LaunchServer 监听地址",
logger.F("url", fmt.Sprintf("http://127.0.0.1:%d", a.launchServer.Port())),
logger.F("preferred_port", port),
)
}
func (a *App) startupInitAutomation() {
a.automationMgr = automation.NewManager(a.appRoot, a.config, func(event string, payload any) {
if a.ctx == nil {
return
}
runtime.EventsEmit(a.ctx, event, payload)
}, automation.Options{})
}
func (a *App) startupInitBridgeHooks() {
a.xrayMgr.OnBridgeDied = func(key string, err error) {
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "proxy:bridge:died", map[string]interface{}{
"engine": "xray",
"key": key[:8],
"error": err.Error(),
})
}
}
a.singboxMgr.OnBridgeDied = func(key string, err error) {
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "proxy:bridge:died", map[string]interface{}{
"engine": "singbox",
"key": key[:8],
"error": err.Error(),
})
}
}
}
func (a *App) startupInitSpeedScheduler() {
a.speedScheduler = browser.NewProxySpeedScheduler(
a.browserMgr.ProxyDAO,
func(proxyId string) (bool, int64, string) {
connectorType := config.NormalizeBrowserConnectorType(a.config.Browser.DefaultConnectorType)
r := a.testProxySpeedWithConnector(proxyId, a.getLatestProxies(), connectorType)
return r.Ok, r.LatencyMs, r.Error
},
5*time.Minute,
5,
)
a.speedScheduler.Start()
}