first commit
Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped

This commit is contained in:
Rogee
2025-09-28 10:05:07 +08:00
commit 7fcabe0225
481 changed files with 125127 additions and 0 deletions

565
internal/cli/command.go Normal file
View File

@@ -0,0 +1,565 @@
package cli
import (
"fmt"
"os"
"strings"
"time"
)
// CommandType 定义命令类型
type CommandType int
const (
SERVER CommandType = iota
CONFIG
TEST
VERSION
HELP
UNKNOWN
)
// Command 表示CLI命令的结构
// 该结构体包含命令的所有参数和配置
type Command struct {
// 基本命令信息
Type CommandType `json:"type"`
Name string `json:"name"`
Description string `json:"description"`
Aliases []string `json:"aliases"`
Usage string `json:"usage"`
// 位置参数
Args []string `json:"args"`
ArgsCount int `json:"args_count"`
// 标志参数
Flags map[string]string `json:"flags"`
Booleans map[string]bool `json:"booleans"`
Integers map[string]int64 `json:"integers"`
Floats map[string]float64 `json:"floats"`
// 选项参数
Options map[string][]string `json:"options"`
// 环境变量
EnvVars map[string]string `json:"env_vars"`
// 配置文件
ConfigFile string `json:"config_file"`
Config interface{} `json:"config"`
// 执行信息
Executed bool `json:"executed"`
ExecutedAt time.Time `json:"executed_at"`
Duration time.Duration `json:"duration"`
ExitCode int `json:"exit_code"`
Output string `json:"output"`
Error error `json:"error"`
// 上下文信息
WorkingDir string `json:"working_dir"`
UserID string `json:"user_id,omitempty"`
SessionID string `json:"session_id,omitempty"`
// 调试信息
Debug bool `json:"debug"`
Verbose bool `json:"verbose"`
Quiet bool `json:"quiet"`
DryRun bool `json:"dry_run"`
// 自定义数据
Custom map[string]interface{} `json:"custom,omitempty"`
}
// NewCommand 创建新的命令实例
// 返回包含基本信息的Command结构体
func NewCommand(cmdType CommandType, name, description string) *Command {
return &Command{
Type: cmdType,
Name: name,
Description: description,
Args: make([]string, 0),
Flags: make(map[string]string),
Booleans: make(map[string]bool),
Integers: make(map[string]int64),
Floats: make(map[string]float64),
Options: make(map[string][]string),
EnvVars: make(map[string]string),
WorkingDir: getCurrentWorkingDir(),
Custom: make(map[string]interface{}),
}
}
// NewServerCommand 创建服务器命令
// 返回配置好的服务器命令实例
func NewServerCommand() *Command {
cmd := NewCommand(SERVER, "server", "Start the subconverter server")
cmd.Usage = "server [flags]"
cmd.Aliases = []string{"serve", "start"}
// 默认标志
cmd.Booleans["debug"] = false
cmd.Booleans["verbose"] = false
cmd.Integers["port"] = 25500
cmd.Flags["host"] = "0.0.0.0"
cmd.Flags["config"] = ""
return cmd
}
// NewConfigCommand 创建配置命令
// 返回配置好的配置命令实例
func NewConfigCommand() *Command {
cmd := NewCommand(CONFIG, "config", "Manage configuration")
cmd.Usage = "config <subcommand> [flags]"
cmd.Aliases = []string{"conf", "cfg"}
return cmd
}
// NewTestCommand 创建测试命令
// 返回配置好的测试命令实例
func NewTestCommand() *Command {
cmd := NewCommand(TEST, "test", "Run tests")
cmd.Usage = "test <subcommand> [flags]"
cmd.Aliases = []string{"t"}
cmd.Booleans["verbose"] = false
cmd.Booleans["cover"] = false
cmd.Flags["run"] = ""
cmd.Flags["output"] = ""
return cmd
}
// NewVersionCommand 创建版本命令
// 返回配置好的版本命令实例
func NewVersionCommand() *Command {
cmd := NewCommand(VERSION, "version", "Show version information")
cmd.Usage = "version [flags]"
cmd.Aliases = []string{"v", "ver"}
cmd.Booleans["verbose"] = false
cmd.Booleans["short"] = false
return cmd
}
// NewHelpCommand 创建帮助命令
// 返回配置好的帮助命令实例
func NewHelpCommand() *Command {
cmd := NewCommand(HELP, "help", "Show help information")
cmd.Usage = "help [command]"
cmd.Aliases = []string{"h", "?"}
return cmd
}
// AddArg 添加位置参数
func (c *Command) AddArg(arg string) {
c.Args = append(c.Args, arg)
c.ArgsCount = len(c.Args)
}
// AddArgs 添加多个位置参数
func (c *Command) AddArgs(args ...string) {
c.Args = append(c.Args, args...)
c.ArgsCount = len(c.Args)
}
// SetFlag 设置字符串标志
func (c *Command) SetFlag(key, value string) {
c.Flags[key] = value
}
// SetBoolean 设置布尔标志
func (c *Command) SetBoolean(key string, value bool) {
c.Booleans[key] = value
}
// SetInteger 设置整数标志
func (c *Command) SetInteger(key string, value int64) {
c.Integers[key] = value
}
// SetFloat 设置浮点数标志
func (c *Command) SetFloat(key string, value float64) {
c.Floats[key] = value
}
// AddOption 添加选项参数
func (c *Command) AddOption(key string, values ...string) {
if c.Options == nil {
c.Options = make(map[string][]string)
}
c.Options[key] = append(c.Options[key], values...)
}
// SetEnvVar 设置环境变量
func (c *Command) SetEnvVar(key, value string) {
if c.EnvVars == nil {
c.EnvVars = make(map[string]string)
}
c.EnvVars[key] = value
}
// GetArg 获取位置参数
func (c *Command) GetArg(index int) (string, bool) {
if index < 0 || index >= len(c.Args) {
return "", false
}
return c.Args[index], true
}
// GetFlag 获取字符串标志
func (c *Command) GetFlag(key string) (string, bool) {
value, exists := c.Flags[key]
return value, exists
}
// GetBoolean 获取布尔标志
func (c *Command) GetBoolean(key string) (bool, bool) {
value, exists := c.Booleans[key]
return value, exists
}
// GetInteger 获取整数标志
func (c *Command) GetInteger(key string) (int64, bool) {
value, exists := c.Integers[key]
return value, exists
}
// GetFloat 获取浮点数标志
func (c *Command) GetFloat(key string) (float64, bool) {
value, exists := c.Floats[key]
return value, exists
}
// GetOptions 获取选项参数
func (c *Command) GetOptions(key string) ([]string, bool) {
value, exists := c.Options[key]
return value, exists
}
// GetEnvVar 获取环境变量
func (c *Command) GetEnvVar(key string) (string, bool) {
value, exists := c.EnvVars[key]
return value, exists
}
// HasFlag 检查是否有指定标志
func (c *Command) HasFlag(key string) bool {
_, exists := c.Flags[key]
return exists
}
// HasBoolean 检查是否有指定布尔标志
func (c *Command) HasBoolean(key string) bool {
_, exists := c.Booleans[key]
return exists
}
// HasOption 检查是否有指定选项
func (c *Command) HasOption(key string) bool {
_, exists := c.Options[key]
return exists
}
// ArgCount 返回位置参数数量
func (c *Command) ArgCount() int {
return len(c.Args)
}
// IsExecuted 检查命令是否已执行
func (c *Command) IsExecuted() bool {
return c.Executed
}
// IsSuccessful 检查命令是否成功执行
func (c *Command) IsSuccessful() bool {
return c.Executed && c.ExitCode == 0
}
// SetConfig 设置配置信息
func (c *Command) SetConfig(config interface{}) {
c.Config = config
}
// SetConfigFile 设置配置文件路径
func (c *Command) SetConfigFile(path string) {
c.ConfigFile = path
}
// SetOutput 设置输出信息
func (c *Command) SetOutput(output string) {
c.Output = output
}
// SetError 设置错误信息
func (c *Command) SetError(err error) {
c.Error = err
c.ExitCode = 1
}
// MarkExecuted 标记命令为已执行
func (c *Command) MarkExecuted() {
c.Executed = true
c.ExecutedAt = time.Now()
}
// MarkSuccessful 标记命令为成功执行
func (c *Command) MarkSuccessful() {
c.MarkExecuted()
c.ExitCode = 0
}
// MarkFailed 标记命令为执行失败
func (c *Command) MarkFailed(exitCode int) {
c.MarkExecuted()
c.ExitCode = exitCode
}
// SetDuration 设置执行持续时间
func (c *Command) SetDuration(duration time.Duration) {
c.Duration = duration
}
// AddCustom 添加自定义数据
func (c *Command) AddCustom(key string, value interface{}) {
if c.Custom == nil {
c.Custom = make(map[string]interface{})
}
c.Custom[key] = value
}
// GetCustom 获取自定义数据
func (c *Command) GetCustom(key string) (interface{}, bool) {
value, exists := c.Custom[key]
return value, exists
}
// Validate 验证命令的有效性
func (c *Command) Validate() error {
if c.Name == "" {
return fmt.Errorf("command name cannot be empty")
}
if c.Type < SERVER || c.Type > UNKNOWN {
return fmt.Errorf("invalid command type: %d", c.Type)
}
// 验证必需的参数
switch c.Type {
case SERVER:
if port, exists := c.Integers["port"]; exists && (port < 1 || port > 65535) {
return fmt.Errorf("invalid port number: %d", port)
}
case CONFIG:
if len(c.Args) == 0 {
return fmt.Errorf("config command requires a subcommand")
}
case TEST:
if len(c.Args) == 0 {
return fmt.Errorf("test command requires a subcommand")
}
}
return nil
}
// GetUsage 返回使用说明
func (c *Command) GetUsage() string {
if c.Usage != "" {
return c.Usage
}
return c.Name
}
// GetFullUsage 返回完整的使用说明
func (c *Command) GetFullUsage() string {
var builder strings.Builder
builder.WriteString(fmt.Sprintf("Usage: %s", c.GetUsage()))
if c.Description != "" {
builder.WriteString(fmt.Sprintf("\n\n%s", c.Description))
}
if len(c.Aliases) > 0 {
builder.WriteString(fmt.Sprintf("\n\nAliases: %s", strings.Join(c.Aliases, ", ")))
}
// 添加位置参数说明
if len(c.Args) > 0 {
builder.WriteString("\n\nArguments:")
for i, arg := range c.Args {
builder.WriteString(fmt.Sprintf("\n %d: %s", i+1, arg))
}
}
// 添加标志说明
if len(c.Flags) > 0 || len(c.Booleans) > 0 || len(c.Integers) > 0 || len(c.Floats) > 0 {
builder.WriteString("\n\nFlags:")
for key, value := range c.Flags {
builder.WriteString(fmt.Sprintf("\n --%s=%s", key, value))
}
for key, value := range c.Booleans {
builder.WriteString(fmt.Sprintf("\n --%s (default: %t)", key, value))
}
for key, value := range c.Integers {
builder.WriteString(fmt.Sprintf("\n --%s=%d", key, value))
}
for key, value := range c.Floats {
builder.WriteString(fmt.Sprintf("\n --%s=%f", key, value))
}
}
return builder.String()
}
// Clone 创建命令的副本
func (c *Command) Clone() *Command {
clone := &Command{
Type: c.Type,
Name: c.Name,
Description: c.Description,
Usage: c.Usage,
ArgsCount: c.ArgsCount,
ConfigFile: c.ConfigFile,
Config: c.Config,
Executed: c.Executed,
ExecutedAt: c.ExecutedAt,
Duration: c.Duration,
ExitCode: c.ExitCode,
Output: c.Output,
Error: c.Error,
WorkingDir: c.WorkingDir,
UserID: c.UserID,
SessionID: c.SessionID,
Debug: c.Debug,
Verbose: c.Verbose,
Quiet: c.Quiet,
DryRun: c.DryRun,
}
// 深拷贝切片
if len(c.Aliases) > 0 {
clone.Aliases = make([]string, len(c.Aliases))
copy(clone.Aliases, c.Aliases)
}
if len(c.Args) > 0 {
clone.Args = make([]string, len(c.Args))
copy(clone.Args, c.Args)
}
// 深拷贝映射
if len(c.Flags) > 0 {
clone.Flags = make(map[string]string)
for k, v := range c.Flags {
clone.Flags[k] = v
}
}
if len(c.Booleans) > 0 {
clone.Booleans = make(map[string]bool)
for k, v := range c.Booleans {
clone.Booleans[k] = v
}
}
if len(c.Integers) > 0 {
clone.Integers = make(map[string]int64)
for k, v := range c.Integers {
clone.Integers[k] = v
}
}
if len(c.Floats) > 0 {
clone.Floats = make(map[string]float64)
for k, v := range c.Floats {
clone.Floats[k] = v
}
}
if len(c.Options) > 0 {
clone.Options = make(map[string][]string)
for k, v := range c.Options {
clone.Options[k] = make([]string, len(v))
copy(clone.Options[k], v)
}
}
if len(c.EnvVars) > 0 {
clone.EnvVars = make(map[string]string)
for k, v := range c.EnvVars {
clone.EnvVars[k] = v
}
}
if len(c.Custom) > 0 {
clone.Custom = make(map[string]interface{})
for k, v := range c.Custom {
clone.Custom[k] = v
}
}
return clone
}
// String 返回命令的字符串表示
func (c *Command) String() string {
if c.Executed {
return fmt.Sprintf("%s (executed in %v, exit code: %d)", c.Name, c.Duration, c.ExitCode)
}
return fmt.Sprintf("%s (not executed)", c.Name)
}
// GetCurrentWorkingDir 获取当前工作目录
func getCurrentWorkingDir() string {
if dir, err := os.Getwd(); err == nil {
return dir
}
return "."
}
// CommandTypeToString 将命令类型转换为字符串
func CommandTypeToString(cmdType CommandType) string {
switch cmdType {
case SERVER:
return "server"
case CONFIG:
return "config"
case TEST:
return "test"
case VERSION:
return "version"
case HELP:
return "help"
default:
return "unknown"
}
}
// StringToCommandType 将字符串转换为命令类型
func StringToCommandType(s string) CommandType {
switch strings.ToLower(s) {
case "server", "serve", "start":
return SERVER
case "config", "conf", "cfg":
return CONFIG
case "test", "t":
return TEST
case "version", "v", "ver":
return VERSION
case "help", "h", "?":
return HELP
default:
return UNKNOWN
}
}

354
internal/cli/root.go Normal file
View File

@@ -0,0 +1,354 @@
package cli
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/subconverter-go/internal/config"
"github.com/subconverter-go/internal/logging"
httpserver "github.com/subconverter-go/internal/http"
)
// RootCmd CLI根命令
// 包含所有子命令和全局配置
type RootCmd struct {
cmd *cobra.Command
configPath string
logger *logging.Logger
configMgr *config.ConfigManager
server *httpserver.Server
}
// NewRootCmd 创建新的根命令
// 返回初始化好的RootCmd实例
func NewRootCmd() *RootCmd {
root := &RootCmd{
cmd: &cobra.Command{
Use: "subconverter-go",
Short: "SubConverter Go版本 - 代理订阅转换服务",
Long: `SubConverter Go版本是一个高性能的代理订阅转换服务
支持在Clash、Surge、Quantumult X等格式之间转换。
这是一个Go语言重写版本与原C++版本保持功能兼容。`,
Version: "1.0.0",
},
}
// 设置命令
root.setupCommands()
return root
}
// setupCommands 设置命令结构
func (r *RootCmd) setupCommands() {
// 添加全局标志
r.cmd.PersistentFlags().StringVarP(&r.configPath, "config", "c", "", "配置文件路径")
r.cmd.PersistentFlags().BoolP("verbose", "v", false, "详细输出")
r.cmd.PersistentFlags().BoolP("quiet", "q", false, "静默模式")
// 添加子命令
r.cmd.AddCommand(r.newServerCommand())
r.cmd.AddCommand(r.newConvertCommand())
r.cmd.AddCommand(r.newConfigCommand())
r.cmd.AddCommand(r.newVersionCommand())
// 设置前执行函数
r.cmd.PersistentPreRunE = r.persistentPreRun
}
// persistentPreRun 持久化预运行函数
// 在所有命令执行前运行,用于初始化共享资源
func (r *RootCmd) persistentPreRun(cmd *cobra.Command, args []string) error {
// 初始化日志
if err := r.initLogging(); err != nil {
return fmt.Errorf("failed to initialize logging: %v", err)
}
// 初始化配置
if err := r.initConfig(); err != nil {
return fmt.Errorf("failed to initialize config: %v", err)
}
return nil
}
// initLogging 初始化日志系统
func (r *RootCmd) initLogging() error {
// 根据命令行参数设置日志级别
level := "info"
if r.cmd.Flag("verbose").Changed {
level = "debug"
} else if r.cmd.Flag("quiet").Changed {
level = "error"
}
// 创建日志记录器
logger, err := logging.NewLogger(&logging.LoggingConfig{
Level: level,
Format: "text",
Output: "stdout",
MaxSize: 100,
MaxBackups: 3,
MaxAge: 30,
Compress: true,
ReportCaller: false,
EnableColor: true,
})
if err != nil {
return err
}
r.logger = logger
return nil
}
// initConfig 初始化配置管理器
func (r *RootCmd) initConfig() error {
// 创建配置管理器
configMgr, err := config.NewConfigManager(r.configPath)
if err != nil {
return err
}
r.configMgr = configMgr
return nil
}
// newServerCommand 创建服务器命令
func (r *RootCmd) newServerCommand() *cobra.Command {
var (
host string
port int
)
cmd := &cobra.Command{
Use: "server",
Short: "启动HTTP服务器",
Long: "启动HTTP服务器提供代理订阅转换API服务",
RunE: func(cmd *cobra.Command, args []string) error {
return r.runServer(host, port)
},
}
// 添加命令特定标志
cmd.Flags().StringVarP(&host, "host", "H", "", "服务器监听地址")
cmd.Flags().IntVarP(&port, "port", "p", 0, "服务器监听端口")
return cmd
}
// newConvertCommand 创建转换命令
func (r *RootCmd) newConvertCommand() *cobra.Command {
var (
target string
inputFile string
outputFile string
url string
)
cmd := &cobra.Command{
Use: "convert",
Short: "转换代理订阅",
Long: "将代理订阅从一个格式转换为另一个格式",
RunE: func(cmd *cobra.Command, args []string) error {
return r.runConvert(target, inputFile, outputFile, url)
},
}
// 添加命令特定标志
cmd.Flags().StringVarP(&target, "target", "t", "clash", "目标格式 (clash, surge, quanx, etc.)")
cmd.Flags().StringVarP(&inputFile, "input", "i", "", "输入文件路径")
cmd.Flags().StringVarP(&outputFile, "output", "o", "", "输出文件路径")
cmd.Flags().StringVarP(&url, "url", "u", "", "订阅URL")
return cmd
}
// newConfigCommand 创建配置命令
func (r *RootCmd) newConfigCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "配置管理",
Long: "管理SubConverter配置",
}
// 添加子命令
cmd.AddCommand(r.newConfigShowCommand())
cmd.AddCommand(r.newConfigValidateCommand())
return cmd
}
// newConfigShowCommand 创建配置显示命令
func (r *RootCmd) newConfigShowCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "show",
Short: "显示当前配置",
Long: "显示当前配置信息",
RunE: func(cmd *cobra.Command, args []string) error {
return r.showConfig()
},
}
return cmd
}
// newConfigValidateCommand 创建配置验证命令
func (r *RootCmd) newConfigValidateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "validate",
Short: "验证配置文件",
Long: "验证配置文件的有效性",
RunE: func(cmd *cobra.Command, args []string) error {
return r.validateConfig()
},
}
return cmd
}
// newVersionCommand 创建版本命令
func (r *RootCmd) newVersionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "显示版本信息",
Long: "显示SubConverter Go版本的详细信息",
Run: func(cmd *cobra.Command, args []string) {
r.showVersion()
},
}
return cmd
}
// runServer 运行服务器
func (r *RootCmd) runServer(host string, port int) error {
r.logger.Info("Starting SubConverter Go server...")
// 获取服务器配置
serverConfig := r.configMgr.GetServerConfig()
// 应用命令行参数覆盖
if host != "" {
serverConfig.Host = host
}
if port != 0 {
serverConfig.Port = port
}
// 更新配置
if err := r.configMgr.UpdateConfig(r.configMgr.GetConfig()); err != nil {
return fmt.Errorf("failed to update server config: %v", err)
}
// 创建HTTP服务器
server, err := httpserver.NewServer(r.configMgr)
if err != nil {
return fmt.Errorf("failed to create HTTP server: %v", err)
}
r.server = server
// 启动服务器
if err := server.Start(); err != nil {
return fmt.Errorf("failed to start server: %v", err)
}
r.logger.Infof("Server started on %s:%d", serverConfig.Host, serverConfig.Port)
// 等待关闭信号
r.server.WaitForShutdown()
return nil
}
// runConvert 运行转换命令
func (r *RootCmd) runConvert(target, inputFile, outputFile, url string) error {
r.logger.Info("Starting conversion...")
r.logger.Infof("Target: %s, Input: %s, Output: %s, URL: %s", target, inputFile, outputFile, url)
// 这里实现转换逻辑
// 目前只是占位符
r.logger.Warn("Conversion functionality not implemented yet")
return nil
}
// showConfig 显示配置
func (r *RootCmd) showConfig() error {
config := r.configMgr.GetConfig()
r.logger.Info("Current configuration:")
r.logger.Infof("Server: %s:%d", config.Server.Host, config.Server.Port)
r.logger.Infof("Log Level: %s", config.Logging.Level)
r.logger.Infof("Rate Limit: %d", config.Security.RateLimit)
r.logger.Infof("Default Target: %s", config.Conversion.DefaultTarget)
return nil
}
// validateConfig 验证配置
func (r *RootCmd) validateConfig() error {
config := r.configMgr.GetConfig()
if err := config.Validate(); err != nil {
return fmt.Errorf("configuration validation failed: %v", err)
}
r.logger.Info("Configuration is valid")
return nil
}
// showVersion 显示版本信息
func (r *RootCmd) showVersion() {
fmt.Printf("SubConverter Go v%s\n", r.cmd.Version)
fmt.Printf("Build: %s\n", "development")
fmt.Printf("Go Version: %s\n", "1.21+")
fmt.Println("A high-performance proxy subscription converter")
}
// Execute 执行命令
func (r *RootCmd) Execute() error {
return r.cmd.Execute()
}
// Close 清理资源
func (r *RootCmd) Close() error {
// 停止服务器
if r.server != nil {
if err := r.server.Stop(); err != nil {
r.logger.WithError(err).Error("Failed to stop server")
}
}
// 关闭配置管理器
if r.configMgr != nil {
if err := r.configMgr.Close(); err != nil {
r.logger.WithError(err).Error("Failed to close config manager")
}
}
return nil
}
// ExecuteMain 主执行函数
// 用于main.go调用
func ExecuteMain() {
rootCmd := NewRootCmd()
// 设置退出时的清理函数
defer func() {
if err := rootCmd.Close(); err != nil {
fmt.Fprintf(os.Stderr, "Error during cleanup: %v\n", err)
os.Exit(1)
}
}()
// 执行命令
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}