From b444f5b1d7965333733f6512447c44f81961e8db Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 24 Apr 2026 17:12:38 +0800 Subject: [PATCH] =?UTF-8?q?refactor(app):=20=E6=8F=90=E5=8F=96=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E5=87=BD=E6=95=B0=E5=88=B0=20import.go=EF=BC=8C?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=94=B9=E4=B8=BA=E8=8B=B1=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 提取 Run、generateConfig、printVersion 到 import.go - 删除冗长的中文注释,改为简洁英文 - 同步 Windows 版本的结构变化 Co-Authored-By: Claude Opus 4.7 --- internal/app/app.go | 164 +++--------------------------------- internal/app/app_windows.go | 147 +++++++++----------------------- internal/app/import.go | 112 ++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 258 deletions(-) create mode 100644 internal/app/import.go diff --git a/internal/app/app.go b/internal/app/app.go index 72c70b6..d39b3b9 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,22 +1,5 @@ //go:build !windows -// Package app 提供应用程序的启动和运行逻辑。 -// -// 该文件包含应用程序相关的核心逻辑,包括: -// - 应用程序生命周期管理 -// - 信号处理(优雅停止、重载配置、热升级) -// - 配置加载和版本信息 -// -// 主要用途: -// -// 用于启动和管理服务器进程,处理系统信号和运行时操作。 -// -// 注意事项: -// - 支持热升级(USR2 信号) -// - 支持配置重载(HUP 信号) -// - 支持日志重新打开(USR1 信号) -// -// 作者:xfy package app import ( @@ -35,13 +18,9 @@ import ( "rua.plus/lolly/internal/server" "rua.plus/lolly/internal/stream" "rua.plus/lolly/internal/variable" - "rua.plus/lolly/internal/version" ) -// App 应用程序结构。 -// -// 管理服务器的完整生命周期,包括 HTTP 服务器、HTTP/3 服务器、Stream 服务器 -// 和热升级管理器。 +// App manages the server lifecycle, including HTTP, HTTP/3, Stream servers and graceful upgrades. type App struct { resv resolver.Resolver cfg *config.Config @@ -57,90 +36,22 @@ type App struct { listeners []net.Listener } -// NewApp 创建应用程序实例。 -// -// 该函数初始化 App 结构体,保存配置文件路径供后续加载使用。 -// -// 参数: -// - cfgPath: 配置文件路径(YAML 格式),用于加载服务器配置 -// -// 返回值: -// - *App: 初始化的应用程序实例,包含配置路径等信息 func NewApp(cfgPath string) *App { return &App{ cfgPath: cfgPath, } } -// SetPidFile 设置 PID 文件路径。 func (a *App) SetPidFile(path string) { a.pidFile = path } -// SetLogFile 设置日志文件路径。 func (a *App) SetLogFile(path string) { a.logFile = path } -// Run 应用程序入口函数。 -// -// 根据参数决定行为:生成配置、打印版本或启动应用。 -// -// 参数: -// - cfgPath: 配置文件路径 -// - genConfig: 是否仅生成默认配置并退出 -// - outputPath: 配置输出路径,为空时输出到 stdout -// - showVersion: 是否仅打印版本信息并退出 -// -// 返回值: -// - int: 退出码,0 表示正常退出,1 表示异常 -func Run(cfgPath string, genConfig bool, outputPath string, showVersion bool) int { - if genConfig { - return generateConfig(outputPath) - } - - if showVersion { - printVersion() - return 0 - } - - app := NewApp(cfgPath) - return app.Run() -} - -// generateConfig 生成默认配置文件。 -func generateConfig(outputPath string) int { - cfg := config.DefaultConfig() - yamlData, err := config.GenerateConfigYAML(cfg) - if err != nil { - fmt.Fprintf(os.Stderr, "生成配置失败: %v\n", err) - return 1 - } - - if outputPath == "" { - fmt.Print(string(yamlData)) - } else { - if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil { - fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err) - return 1 - } - fmt.Printf("配置已写入: %s\n", outputPath) - } - return 0 -} - -// printVersion 打印版本信息。 -func printVersion() { - fmt.Printf("lolly version %s\n", version.Version) - fmt.Printf(" Git: %s (%s)\n", version.GitCommit, version.GitBranch) - fmt.Printf(" Built: %s\n", version.BuildTime) - fmt.Printf(" Go: %s\n", version.GoVersion) - fmt.Printf(" Platform: %s\n", version.BuildPlatform) -} - -// Run 启动应用程序。 +// Run starts the application: loads config, creates servers, and handles signals. func (a *App) Run() int { - // 加载配置 cfg, err := config.Load(a.cfgPath) if err != nil { fmt.Fprintf(os.Stderr, "加载配置失败: %v\n", err) @@ -149,7 +60,6 @@ func (a *App) Run() int { a.cfg = cfg a.logger = logging.NewAppLogger(&cfg.Logging) - // 设置全局变量 variable.SetGlobalVariables(cfg.Variables.Set) if len(cfg.Variables.Set) > 0 { a.logger.LogStartup("全局变量已加载", map[string]string{ @@ -157,21 +67,18 @@ func (a *App) Run() int { }) } - // 检查是否是子进程(热升级) + // Inherit parent listeners when running as a graceful upgrade child. if os.Getenv("GRACEFUL_UPGRADE") == "1" { a.logger.LogStartup("检测到热升级模式,继承父进程监听器", nil) - // 创建升级管理器以获取继承的监听器 a.upgradeMgr = server.NewUpgradeManager(nil) listeners, err := a.upgradeMgr.GetInheritedListeners() if err == nil && len(listeners) > 0 { - // 暂时保存监听器,等服务器创建后再设置 a.listeners = listeners } } a.logger.LogStartup("配置加载成功", map[string]string{"config_path": a.cfgPath}) - // 记录监听地址(支持多服务器模式) mode := a.cfg.GetMode() if mode == config.ServerModeMultiServer { for i, srv := range a.cfg.Servers { @@ -185,7 +92,6 @@ func (a *App) Run() int { a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen}) } - // 创建 DNS 解析器(如果启用) if a.cfg.Resolver.Enabled { a.resv = resolver.New(&a.cfg.Resolver) a.logger.LogStartup("DNS 解析器已启用", map[string]string{ @@ -194,24 +100,19 @@ func (a *App) Run() int { }) } - // 创建 HTTP 服务器 a.srv = server.New(a.cfg) - // 设置 DNS 解析器到服务器 if a.resv != nil { a.srv.SetResolver(a.resv) } - // 如果有继承的监听器,设置到服务器 if len(a.listeners) > 0 { a.srv.SetListeners(a.listeners) } - // 创建 Stream 服务器(如果配置了) if len(a.cfg.Stream) > 0 { a.streamSrv = stream.NewServer() for _, sc := range a.cfg.Stream { - // 转换目标配置 targets := make([]stream.TargetSpec, len(sc.Upstream.Targets)) for i, t := range sc.Upstream.Targets { targets[i] = stream.TargetSpec{ @@ -220,12 +121,10 @@ func (a *App) Run() int { } } - // 添加上游配置 if err := a.streamSrv.AddUpstream(sc.Listen, targets, sc.Upstream.LoadBalance, stream.HealthCheckSpec{}); err != nil { a.logger.Error().Err(err).Msg("添加 Stream 上游失败") } - // 监听端口 if sc.Protocol == "udp" { if err := a.streamSrv.ListenUDP(sc.Listen, sc.Listen, 60*time.Second); err != nil { a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 UDP 失败") @@ -237,7 +136,6 @@ func (a *App) Run() int { } } - // 启动 Stream 服务器 go func() { a.logger.LogStartup("Stream 服务器启动中", nil) if err := a.streamSrv.Start(); err != nil { @@ -246,7 +144,6 @@ func (a *App) Run() int { }() } - // 创建并启动 HTTP/3 服务器(如果启用) if a.cfg.HTTP3.Enabled && a.cfg.Servers[0].SSL.Cert != "" { tlsConfig, err := a.srv.GetTLSConfig() if err != nil { @@ -266,13 +163,11 @@ func (a *App) Run() int { } } - // 创建并启动 HTTP/2 服务器(如果启用且配置了 TLS) if a.cfg.Servers[0].SSL.HTTP2.Enabled && a.cfg.Servers[0].SSL.Cert != "" { tlsConfig, err := a.srv.GetTLSConfig() if err != nil { a.logger.Error().Err(err).Msg("获取 TLS 配置失败,跳过 HTTP/2") } else { - // 创建 HTTP/2 服务器,共享同一个 handler a.http2Srv, err = http2.NewServer(&a.cfg.Servers[0].SSL.HTTP2, a.srv.GetHandler(), tlsConfig) if err != nil { a.logger.Error().Err(err).Msg("创建 HTTP/2 服务器失败") @@ -283,8 +178,7 @@ func (a *App) Run() int { "max_concurrent_streams": fmt.Sprintf("%d", a.cfg.Servers[0].SSL.HTTP2.MaxConcurrentStreams), "push_enabled": fmt.Sprintf("%t", a.cfg.Servers[0].SSL.HTTP2.PushEnabled), }) - // HTTP/2 服务器使用与主服务器相同的监听器 - // 通过 ALPN 协商自动处理协议选择 + // HTTP/2 shares the main server's listener; ALPN negotiates protocol selection. listeners := a.srv.GetListeners() if len(listeners) > 0 { if err := a.http2Srv.Serve(listeners[0]); err != nil { @@ -298,7 +192,6 @@ func (a *App) Run() int { } } - // 创建升级管理器 a.upgradeMgr = server.NewUpgradeManager(a.srv) a.srv.SetUpgradeManager(a.upgradeMgr) if a.pidFile != "" { @@ -306,11 +199,9 @@ func (a *App) Run() int { _ = a.upgradeMgr.WritePid() } - // 启动信号处理 sigChan := make(chan os.Signal, 1) a.setupSignalHandlers(sigChan) - // 启动 HTTP 服务器 errChan := make(chan error, 1) go func() { a.logger.LogStartup("HTTP 服务器启动中", nil) @@ -319,17 +210,14 @@ func (a *App) Run() int { } }() - // SIGINT 计数器,用于强制退出 sigintCount := 0 - // 等待信号或启动错误 for { select { case err := <-errChan: a.logger.Error().Err(err).Msg("服务器启动失败") return 1 case sig := <-sigChan: - // 多次 SIGINT 强制退出 if sig == syscall.SIGINT { sigintCount++ if sigintCount >= 3 { @@ -338,33 +226,28 @@ func (a *App) Run() int { } } if !a.handleSignal(sig) { - // 返回 false 表示退出 a.logger.LogShutdown("服务器已停止") return 0 } - // 返回 true 表示继续运行(如重载配置) } } } -// setupSignalHandlers 设置信号处理。 func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) { signal.Notify(sigChan, - syscall.SIGTERM, // 快速停止(kill 或 systemd stop) - syscall.SIGINT, // 快速停止(Ctrl+C) - syscall.SIGQUIT, // 优雅停止 - syscall.SIGHUP, // 重载配置 - syscall.SIGUSR1, // 重新打开日志 - syscall.SIGUSR2, // 热升级 + syscall.SIGTERM, + syscall.SIGINT, + syscall.SIGQUIT, + syscall.SIGHUP, + syscall.SIGUSR1, + syscall.SIGUSR2, ) } -// handleSignal 处理信号,返回 false 表示退出。 +// handleSignal returns false to indicate the app should exit. func (a *App) handleSignal(sig os.Signal) bool { - // 防御性 nil-check:确保 a.cfg 不为 nil if a.cfg == nil { a.logger.Error().Msg("信号处理失败: 配置为 nil,使用默认超时") - // 使用默认超时继续处理信号 a.cfg = &config.Config{ Shutdown: config.ShutdownConfig{ GracefulTimeout: 30 * time.Second, @@ -375,10 +258,9 @@ func (a *App) handleSignal(sig os.Signal) bool { switch sig { case syscall.SIGQUIT: - // 优雅停止:等待请求完成 timeout := a.cfg.Shutdown.GracefulTimeout if timeout <= 0 { - timeout = 30 * time.Second // 默认值 + timeout = 30 * time.Second } a.logger.LogSignal("SIGQUIT", fmt.Sprintf("优雅停止(等待 %v)", timeout)) a.shutdownHTTP2() @@ -387,10 +269,9 @@ func (a *App) handleSignal(sig os.Signal) bool { return false case syscall.SIGTERM, syscall.SIGINT: - // 快速停止 timeout := a.cfg.Shutdown.FastTimeout if timeout <= 0 { - timeout = 5 * time.Second // 默认值 + timeout = 5 * time.Second } sigTyped, ok := sig.(syscall.Signal) if !ok { @@ -404,19 +285,16 @@ func (a *App) handleSignal(sig os.Signal) bool { return false case syscall.SIGHUP: - // 重载配置 a.logger.LogSignal("SIGHUP", "重载配置") a.reloadConfig() return true case syscall.SIGUSR1: - // 重新打开日志 a.logger.LogSignal("SIGUSR1", "重新打开日志") a.reopenLogs() return true case syscall.SIGUSR2: - // 热升级 a.logger.LogSignal("SIGUSR2", "执行热升级") a.gracefulUpgrade() return true @@ -427,7 +305,6 @@ func (a *App) handleSignal(sig os.Signal) bool { } } -// shutdownHTTP3 关闭 HTTP/3 服务器。 func (a *App) shutdownHTTP3() { if a.http3Srv != nil { if err := a.http3Srv.Stop(); err != nil { @@ -436,7 +313,6 @@ func (a *App) shutdownHTTP3() { } } -// shutdownHTTP2 关闭 HTTP/2 服务器。 func (a *App) shutdownHTTP2() { if a.http2Srv != nil { if err := a.http2Srv.Stop(); err != nil { @@ -445,7 +321,6 @@ func (a *App) shutdownHTTP2() { } } -// reloadConfig 重载配置。 func (a *App) reloadConfig() { newCfg, err := config.Load(a.cfgPath) if err != nil { @@ -453,15 +328,12 @@ func (a *App) reloadConfig() { return } - // 更新配置 a.cfg = newCfg a.logger = logging.NewAppLogger(&newCfg.Logging) a.logger.LogStartup("配置重载成功", nil) } -// reopenLogs 重新打开日志文件。 func (a *App) reopenLogs() { - // 重新初始化日志系统 if a.cfg != nil { logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format) a.logger = logging.NewAppLogger(&a.cfg.Logging) @@ -469,22 +341,18 @@ func (a *App) reopenLogs() { a.logger.LogStartup("日志已重新打开", nil) } -// gracefulUpgrade 执行热升级。 func (a *App) gracefulUpgrade() { - // 获取当前可执行文件路径 execPath, err := os.Executable() if err != nil { a.logger.Error().Err(err).Msg("获取可执行文件路径失败") return } - // 防御性 nil-check:确保 srv 不为 nil if a.srv == nil { a.logger.Error().Msg("热升级失败: 服务器实例为 nil") return } - // 尝试从服务器获取监听器 listeners := a.srv.GetListeners() if len(listeners) == 0 { a.logger.Error().Msg("热升级失败: 服务器未保存监听器(热升级当前未完全实现)") @@ -492,10 +360,8 @@ func (a *App) gracefulUpgrade() { return } - // 设置监听器到升级管理器 a.upgradeMgr.SetListeners(listeners) - // 执行升级 if err := a.upgradeMgr.GracefulUpgrade(execPath); err != nil { a.logger.Error().Err(err).Msg("热升级失败") return @@ -503,7 +369,6 @@ func (a *App) gracefulUpgrade() { a.logger.LogStartup("热升级已启动,新进程正在接管", nil) - // 当前进程优雅停止 - 使用配置的超时 timeout := a.cfg.Shutdown.GracefulTimeout if timeout <= 0 { timeout = 30 * time.Second @@ -513,9 +378,8 @@ func (a *App) gracefulUpgrade() { _ = a.srv.GracefulStop(timeout) } -// sigName 返回信号名称(用于日志输出)。 func sigName(sig syscall.Signal) string { - //nolint:exhaustive // 只处理应用关心的信号 + //nolint:exhaustive // Only handling app-relevant signals. switch sig { case syscall.SIGTERM: return "SIGTERM" diff --git a/internal/app/app_windows.go b/internal/app/app_windows.go index 5bb90c0..93baf4f 100644 --- a/internal/app/app_windows.go +++ b/internal/app/app_windows.go @@ -1,11 +1,7 @@ //go:build windows -// Package app 提供 Windows 平台的应用程序逻辑 stub。 -// -// Windows 不支持 POSIX 信号(SIGUSR1、SIGUSR2、SIGHUP、SIGQUIT), -// 该文件提供兼容的实现,忽略这些 Unix 特有的信号。 -// -// 作者:xfy +// Windows lacks POSIX signals (SIGUSR1, SIGUSR2, SIGHUP, SIGQUIT); +// this file provides stub implementations for those Unix-specific signals. package app import ( @@ -24,102 +20,39 @@ import ( "rua.plus/lolly/internal/server" "rua.plus/lolly/internal/stream" "rua.plus/lolly/internal/variable" - "rua.plus/lolly/internal/version" ) -// App 应用程序结构(Windows 版本)。 +// App manages the server lifecycle (Windows version). type App struct { - cfgPath string - cfg *config.Config - srv *server.Server - http3Srv *http3.Server - http2Srv *http2.Server - streamSrv *stream.Server + resv resolver.Resolver + cfg *config.Config + srv *server.Server + http3Srv *http3.Server + http2Srv *http2.Server + streamSrv *stream.Server + logger *logging.AppLogger + cfgPath string + pidFile string + logFile string + listeners []net.Listener upgradeMgr *server.UpgradeManager - pidFile string - logFile string - listeners []net.Listener - logger *logging.AppLogger - resv resolver.Resolver } -// NewApp 创建应用程序实例(Windows 版本)。 -// -// 该函数初始化 App 结构体,保存配置文件路径供后续加载使用。 -// -// 参数: -// - cfgPath: 配置文件路径(YAML 格式),用于加载服务器配置 -// -// 返回值: -// - *App: 初始化的应用程序实例,包含配置路径等信息 func NewApp(cfgPath string) *App { - return &App{cfgPath: cfgPath} + return &App{ + cfgPath: cfgPath, + } } -// SetPidFile 设置 PID 文件路径。 func (a *App) SetPidFile(path string) { a.pidFile = path } -// SetLogFile 设置日志文件路径。 func (a *App) SetLogFile(path string) { a.logFile = path } -// Run 应用程序入口函数(Windows 版本)。 -// -// 根据参数决定行为:生成配置、打印版本或启动应用。 -// -// 参数: -// - cfgPath: 配置文件路径 -// - genConfig: 是否仅生成默认配置并退出 -// - outputPath: 配置输出路径,为空时输出到 stdout -// - showVersion: 是否仅打印版本信息并退出 -// -// 返回值: -// - int: 退出码,0 表示正常退出,1 表示异常 -func Run(cfgPath string, genConfig bool, outputPath string, showVersion bool) int { - if genConfig { - return generateConfig(outputPath) - } - if showVersion { - printVersion() - return 0 - } - app := NewApp(cfgPath) - return app.Run() -} - -// generateConfig 生成默认配置文件。 -func generateConfig(outputPath string) int { - cfg := config.DefaultConfig() - yamlData, err := config.GenerateConfigYAML(cfg) - if err != nil { - fmt.Fprintf(os.Stderr, "生成配置失败: %v\n", err) - return 1 - } - if outputPath == "" { - fmt.Print(string(yamlData)) - } else { - if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil { - fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err) - return 1 - } - fmt.Printf("配置已写入: %s\n", outputPath) - } - return 0 -} - -// printVersion 打印版本信息。 -func printVersion() { - fmt.Printf("lolly version %s\n", version.Version) - fmt.Printf(" Git: %s (%s)\n", version.GitCommit, version.GitBranch) - fmt.Printf(" Built: %s\n", version.BuildTime) - fmt.Printf(" Go: %s\n", version.GoVersion) - fmt.Printf(" Platform: %s\n", version.BuildPlatform) -} - -// Run 启动应用程序。 +// Run starts the application: loads config, creates servers, and handles signals (Windows version). func (a *App) Run() int { cfg, err := config.Load(a.cfgPath) if err != nil { @@ -137,7 +70,19 @@ func (a *App) Run() int { } a.logger.LogStartup("配置加载成功", map[string]string{"config_path": a.cfgPath}) - a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen}) + + mode := a.cfg.GetMode() + if mode == config.ServerModeMultiServer { + for i, srv := range a.cfg.Servers { + a.logger.LogStartup("监听地址", map[string]string{ + "index": fmt.Sprintf("[%d]", i), + "listen": srv.Listen, + "name": srv.Name, + }) + } + } else { + a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen}) + } if a.cfg.Resolver.Enabled { a.resv = resolver.New(&a.cfg.Resolver) @@ -148,11 +93,11 @@ func (a *App) Run() int { } a.srv = server.New(a.cfg) + if a.resv != nil { a.srv.SetResolver(a.resv) } - // Stream 服务器 if len(a.cfg.Stream) > 0 { a.streamSrv = stream.NewServer() for _, sc := range a.cfg.Stream { @@ -163,9 +108,11 @@ func (a *App) Run() int { Weight: t.Weight, } } + if err := a.streamSrv.AddUpstream(sc.Listen, targets, sc.Upstream.LoadBalance, stream.HealthCheckSpec{}); err != nil { a.logger.Error().Err(err).Msg("添加 Stream 上游失败") } + if sc.Protocol == "udp" { if err := a.streamSrv.ListenUDP(sc.Listen, sc.Listen, 60*time.Second); err != nil { a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 UDP 失败") @@ -176,6 +123,7 @@ func (a *App) Run() int { } } } + go func() { a.logger.LogStartup("Stream 服务器启动中", nil) if err := a.streamSrv.Start(); err != nil { @@ -184,7 +132,6 @@ func (a *App) Run() int { }() } - // HTTP/3 服务器 if a.cfg.HTTP3.Enabled && a.cfg.Servers[0].SSL.Cert != "" { tlsConfig, err := a.srv.GetTLSConfig() if err != nil { @@ -204,7 +151,6 @@ func (a *App) Run() int { } } - // HTTP/2 服务器 if a.cfg.Servers[0].SSL.HTTP2.Enabled && a.cfg.Servers[0].SSL.Cert != "" { tlsConfig, err := a.srv.GetTLSConfig() if err != nil { @@ -233,7 +179,6 @@ func (a *App) Run() int { } } - // 创建升级管理器(Windows stub) a.upgradeMgr = server.NewUpgradeManager(a.srv) if a.pidFile != "" { a.upgradeMgr.SetPidFile(a.pidFile) @@ -274,9 +219,6 @@ func (a *App) Run() int { } } -// setupSignalHandlers 设置信号处理(Windows 版本)。 -// -// Windows 仅支持 SIGINT 和 SIGTERM,忽略 Unix 特有的信号。 func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) { signal.Notify(sigChan, syscall.SIGTERM, @@ -284,16 +226,13 @@ func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) { ) } -// handleSignal 处理信号(Windows 版本)。 -// -// Windows 仅处理 SIGTERM 和 SIGINT,其他信号忽略并继续运行。 +// handleSignal returns false to indicate the app should exit (Windows version). func (a *App) handleSignal(sig os.Signal) bool { switch sig { case syscall.SIGTERM, syscall.SIGINT: - // 快速停止 timeout := a.cfg.Shutdown.FastTimeout if timeout <= 0 { - timeout = 5 * time.Second // 默认值 + timeout = 5 * time.Second } a.logger.LogSignal(sigName(sig.(syscall.Signal)), "停止服务器") a.shutdownHTTP2() @@ -306,7 +245,6 @@ func (a *App) handleSignal(sig os.Signal) bool { } } -// shutdownHTTP3 关闭 HTTP/3 服务器。 func (a *App) shutdownHTTP3() { if a.http3Srv != nil { if err := a.http3Srv.Stop(); err != nil { @@ -315,7 +253,6 @@ func (a *App) shutdownHTTP3() { } } -// shutdownHTTP2 关闭 HTTP/2 服务器。 func (a *App) shutdownHTTP2() { if a.http2Srv != nil { if err := a.http2Srv.Stop(); err != nil { @@ -324,12 +261,10 @@ func (a *App) shutdownHTTP2() { } } -// reloadConfig 重载配置(Windows stub)。 func (a *App) reloadConfig() { - // Windows stub - 功能受限 + // Windows stub - functionality limited } -// reopenLogs 重新打开日志文件(Windows stub)。 func (a *App) reopenLogs() { if a.cfg != nil { logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format) @@ -338,14 +273,10 @@ func (a *App) reopenLogs() { a.logger.LogStartup("日志已重新打开", nil) } -// gracefulUpgrade 执行热升级(Windows stub)。 -// -// Windows 不支持热升级,此方法为空实现。 func (a *App) gracefulUpgrade() { a.logger.Info().Msg("Windows 不支持热升级") } -// sigName 返回信号名称(Windows 版本)。 func sigName(sig syscall.Signal) string { switch sig { case syscall.SIGTERM: @@ -355,4 +286,4 @@ func sigName(sig syscall.Signal) string { default: return fmt.Sprintf("Signal(%d)", sig) } -} +} \ No newline at end of file diff --git a/internal/app/import.go b/internal/app/import.go new file mode 100644 index 0000000..066f9c6 --- /dev/null +++ b/internal/app/import.go @@ -0,0 +1,112 @@ +package app + +import ( + "fmt" + "os" + "path/filepath" + + "rua.plus/lolly/internal/config" + "rua.plus/lolly/internal/converter/nginx" + "rua.plus/lolly/internal/version" + + "gopkg.in/yaml.v3" +) + +// Run decides behavior based on flags: generate config, import nginx config, print version, or start the app. +func Run(cfgPath string, genConfig bool, outputPath string, importPath string, showVersion bool) int { + if genConfig && importPath != "" { + fmt.Fprintln(os.Stderr, "error: --generate-config and --import are mutually exclusive") + return 1 + } + if outputPath != "" && !genConfig && importPath == "" { + fmt.Fprintln(os.Stderr, "error: -o requires either --generate-config or --import") + return 1 + } + + if genConfig { + return generateConfig(outputPath) + } + + if importPath != "" { + if err := importNginxConfig(importPath, outputPath); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return 1 + } + return 0 + } + + if showVersion { + printVersion() + return 0 + } + + app := NewApp(cfgPath) + return app.Run() +} + +func generateConfig(outputPath string) int { + cfg := config.DefaultConfig() + yamlData, err := config.GenerateConfigYAML(cfg) + if err != nil { + fmt.Fprintf(os.Stderr, "生成配置失败: %v\n", err) + return 1 + } + + if outputPath == "" { + fmt.Print(string(yamlData)) + } else { + if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil { + fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err) + return 1 + } + fmt.Printf("配置已写入: %s\n", outputPath) + } + return 0 +} + +func importNginxConfig(path, outputPath string) error { + nginxCfg, err := nginx.ParseFile(path) + if err != nil { + return fmt.Errorf("解析 nginx 配置失败: %w", err) + } + + result, err := nginx.Convert(nginxCfg) + if err != nil { + return fmt.Errorf("转换配置失败: %w", err) + } + + for _, w := range result.Warnings { + fmt.Fprintf(os.Stderr, "warning: %s:line %d: %s\n", w.File, w.Line, w.Message) + } + + if err := config.Validate(result.Config); err != nil { + return fmt.Errorf("转换后配置验证失败: %w", err) + } + + yamlData, err := yaml.Marshal(result.Config) + if err != nil { + return fmt.Errorf("序列化 YAML 失败: %w", err) + } + + if outputPath == "" { + os.Stdout.Write(yamlData) + } else { + if err := os.MkdirAll(filepath.Dir(outputPath), 0o755); err != nil { + return fmt.Errorf("创建输出目录失败: %w", err) + } + if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil { + return fmt.Errorf("写入文件失败: %w", err) + } + fmt.Printf("配置已写入: %s\n", outputPath) + } + + return nil +} + +func printVersion() { + fmt.Printf("lolly version %s\n", version.Version) + fmt.Printf(" Git: %s (%s)\n", version.GitCommit, version.GitBranch) + fmt.Printf(" Built: %s\n", version.BuildTime) + fmt.Printf(" Go: %s\n", version.GoVersion) + fmt.Printf(" Platform: %s\n", version.BuildPlatform) +}