package cli import ( "context" "errors" "fmt" "os" "path/filepath" "runtime" "git.pcux.cn/xuans/Scert/backend/internal/app" "git.pcux.cn/xuans/Scert/backend/internal/config" "git.pcux.cn/xuans/Scert/backend/internal/localreset" "github.com/spf13/cobra" ) type serveStage func(context.Context, config.Config) (bool, error) // loadServeConfig 只允许真正不存在的文件进入新初始化;损坏、无权限或符号链接均失败关闭。 func loadServeConfig(path string, defaults config.Config) (config.Config, error) { info, err := os.Lstat(path) if err == nil { if !info.Mode().IsRegular() { return config.Config{}, errors.New("服务配置必须是普通文件") } return config.LoadWithEnv(path) } if !errors.Is(err, os.ErrNotExist) { return config.Config{}, err } defaults.Paths.Config = filepath.Dir(path) if err := defaults.ApplyEnvironment(); err != nil { return config.Config{}, err } if err := defaults.Validate(); err != nil { return config.Config{}, err } return defaults, nil } func serveLifecycle(ctx context.Context, cfg config.Config, reload func() (config.Config, error), bootstrapStage, applicationStage serveStage) error { if !cfg.SetupReady { completed, err := bootstrapStage(ctx, cfg) if err != nil { return err } if !completed || ctx.Err() != nil { return nil } next, err := reload() if err != nil { return fmt.Errorf("重新读取正式配置失败:%w", err) } if !next.SetupReady { return errors.New("初始化服务已退出,但正式配置尚未完成;拒绝启动业务服务") } if filepath.Clean(next.Paths.Data) != filepath.Clean(cfg.Paths.Data) { return errors.New("初始化不能更改进程锁所在数据目录") } cfg = next } if ctx.Err() != nil { return nil } // 关闭 Web 静态页面不等于停用核心 API、桌面管理和后台续期服务。 _, err := applicationStage(ctx, cfg) return err } func bootstrapFirewallMode(command *cobra.Command, value string) string { if !command.Flags().Changed("bootstrap-firewall-mode") { if environment, ok := os.LookupEnv("SCERT_BOOTSTRAP_FIREWALL_MODE"); ok { return environment } } return value } func runApplication(ctx context.Context, cfg config.Config, path string) (bool, error) { if !cfg.SetupReady { return false, localreset.ErrUnavailable } service, err := app.NewWithConfigPath(ctx, cfg, path) if err != nil { return false, err } defer service.Close() var startRenewal func(context.Context) if service.Renewal != nil { startRenewal = service.Renewal.Start } return runApplicationServices(ctx, service.HTTP, startRenewal, service.HTTP.LocalResetFactory(), func() (applicationControl, error) { return localreset.Listen("") }) } func bootstrapURLPath(cfg config.Config) string { if runtime.GOOS == "linux" { return "/run/scert/bootstrap-url" } path, _ := filepath.Abs(filepath.Join(cfg.Paths.Data, "run", "bootstrap-url")) return path }