diff --git a/.gitignore b/.gitignore index 0302e44e..0b6ecdde 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ go.work.sum # env file .env -xray-checker -xray_config.json +singbox-checker +singbox_config.json docker-compose.yml .DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 3e0ccad7..04f2f00f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Xray Checker +# Singbox Checker [![GitHub Release](https://img.shields.io/github/v/release/kutovoys/xray-checker?style=flat&color=blue)](https://github.com/kutovoys/xray-checker/releases/latest) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/kutovoys/xray-checker/build-publish.yml)](https://github.com/kutovoys/xray-checker/actions/workflows/build-publish.yml) @@ -8,7 +8,7 @@ [![ru](https://img.shields.io/badge/lang-ru-blue)](https://github.com/kutovoys/xray-checker/blob/main/README_RU.md) [![en](https://img.shields.io/badge/lang-en-red)](https://github.com/kutovoys/xray-checker/blob/main/README.md) -Xray Checker is a tool for monitoring proxy server availability with support for VLESS, VMess, Trojan, and Shadowsocks protocols. It automatically tests connections through Xray Core and provides metrics for Prometheus, as well as API endpoints for integration with monitoring systems. +Singbox Checker is a tool for monitoring proxy server availability with support for VLESS, VMess, Trojan, and Shadowsocks protocols. It automatically tests connections through Singbox and provides metrics for Prometheus, as well as API endpoints for integration with monitoring systems.
Dashboard Screenshot @@ -16,7 +16,7 @@ Xray Checker is a tool for monitoring proxy server availability with support for ## πŸš€ Key Features -- πŸ” Monitoring of Xray proxy servers (VLESS, VMess, Trojan, Shadowsocks) +- πŸ” Monitoring of Singbox proxy servers (VLESS, VMess, Trojan, Shadowsocks) - πŸ”„ Automatic configuration updates from subscription - πŸ“Š Prometheus metrics export - πŸŒ“ Web interface with dark/light theme @@ -68,7 +68,7 @@ Detailed installation and configuration documentation is available at [xray-chec ## 🀝 Contributing -We welcome any contributions to Xray Checker! If you want to help: +We welcome any contributions to Singbox Checker! If you want to help: 1. Fork the repository 2. Create a branch for your changes @@ -78,7 +78,7 @@ We welcome any contributions to Xray Checker! If you want to help: For more details on how to contribute, read the [contributor's guide](https://xray-checker.kutovoy.dev/contributing/development-guide).

-Thanks to the all contributors who have helped improve Xray Checker: +Thanks to the all contributors who have helped improve Singbox Checker:

diff --git a/README_RU.md b/README_RU.md index d86840b8..fde21707 100644 --- a/README_RU.md +++ b/README_RU.md @@ -1,4 +1,4 @@ -# Xray Checker +# Singbox Checker [![GitHub Release](https://img.shields.io/github/v/release/kutovoys/xray-checker?style=flat&color=blue)](https://github.com/kutovoys/xray-checker/releases/latest) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/kutovoys/xray-checker/build-publish.yml)](https://github.com/kutovoys/xray-checker/actions/workflows/build-publish.yml) @@ -8,7 +8,7 @@ [![ru](https://img.shields.io/badge/lang-ru-blue)](https://github.com/kutovoys/xray-checker/blob/main/README_RU.md) [![en](https://img.shields.io/badge/lang-en-red)](https://github.com/kutovoys/xray-checker/blob/main/README.md) -Xray Checker - это инструмСнт для ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π° доступности прокси-сСрвСров с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ² VLESS, VMess, Trojan ΠΈ Shadowsocks. Он автоматичСски тСстируСт соСдинСния Ρ‡Π΅Ρ€Π΅Π· Xray Core ΠΈ прСдоставляСт ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ для Prometheus, Π° Ρ‚Π°ΠΊΠΆΠ΅ API-эндпоинты для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с систСмами ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π°. +Singbox Checker - это инструмСнт для ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π° доступности прокси-сСрвСров с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ² VLESS, VMess, Trojan ΠΈ Shadowsocks. Он автоматичСски тСстируСт соСдинСния Ρ‡Π΅Ρ€Π΅Π· Singbox ΠΈ прСдоставляСт ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ для Prometheus, Π° Ρ‚Π°ΠΊΠΆΠ΅ API-эндпоинты для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с систСмами ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π°.

Dashboard Screenshot @@ -68,7 +68,7 @@ services: ## 🀝 УчастиС Π² Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ -ΠœΡ‹ Ρ€Π°Π΄Ρ‹ Π»ΡŽΠ±ΠΎΠΌΡƒ Π²ΠΊΠ»Π°Π΄Ρƒ Π² Ρ€Π°Π·Π²ΠΈΡ‚ΠΈΠ΅ Xray Checker! Если Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΏΠΎΠΌΠΎΡ‡ΡŒ: +ΠœΡ‹ Ρ€Π°Π΄Ρ‹ Π»ΡŽΠ±ΠΎΠΌΡƒ Π²ΠΊΠ»Π°Π΄Ρƒ Π² Ρ€Π°Π·Π²ΠΈΡ‚ΠΈΠ΅ Singbox Checker! Если Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΏΠΎΠΌΠΎΡ‡ΡŒ: 1. Π‘Π΄Π΅Π»Π°ΠΉΡ‚Π΅ Ρ„ΠΎΡ€ΠΊ рСпозитория 2. Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ Π²Π΅Ρ‚ΠΊΡƒ для Π²Π°ΡˆΠΈΡ… ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ @@ -78,7 +78,7 @@ services: ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½Π΅Π΅ ΠΎ Ρ‚ΠΎΠΌ, ΠΊΠ°ΠΊ внСсти свой Π²ΠΊΠ»Π°Π΄, Ρ‡ΠΈΡ‚Π°ΠΉΡ‚Π΅ Π² [руководствС для ΠΊΠΎΠ½Ρ‚Ρ€ΠΈΠ±ΡŒΡŽΡ‚ΠΎΡ€ΠΎΠ²](https://xray-checker.kutovoy.dev/ru/contributing/development-guide).

-Бпасибо всСм ΠΊΠΎΠ½Ρ‚Ρ€ΠΈΠ±ΡŒΡŽΡ‚ΠΎΡ€Π°ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠΌΠΎΠ³Π»ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ Xray Checker: +Бпасибо всСм ΠΊΠΎΠ½Ρ‚Ρ€ΠΈΠ±ΡŒΡŽΡ‚ΠΎΡ€Π°ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠΌΠΎΠ³Π»ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ Singbox Checker:

diff --git a/config/config.go b/config/config.go index 4ac1d247..a5acb245 100644 --- a/config/config.go +++ b/config/config.go @@ -10,8 +10,8 @@ var CLIConfig CLI func Parse(version string) { ctx := kong.Parse(&CLIConfig, - kong.Name("xray-checker"), - kong.Description("Xray Checker: A Prometheus exporter for monitoring Xray proxies"), + kong.Name("singbox-checker"), + kong.Description("Singbox Checker: A Prometheus exporter for monitoring Singbox proxies"), kong.Vars{ "version": version, }, @@ -35,9 +35,9 @@ type CLI struct { SimulateLatency bool `name:"simulate-latency" help:"Whether to add latency to the response" default:"true" env:"SIMULATE_LATENCY"` } `embed:"" prefix:""` - Xray struct { - StartPort int `name:"xray-start-port" help:"Start port for proxy configuration" default:"10000" env:"XRAY_START_PORT"` - LogLevel string `name:"xray-log-level" help:"Xray log level (debug|info|warning|error|none)" default:"none" env:"XRAY_LOG_LEVEL"` + Singbox struct { + StartPort int `name:"singbox-start-port" help:"Start port for proxy configuration" default:"10000" env:"SINGBOX_START_PORT"` + LogLevel string `name:"singbox-log-level" help:"Singbox log level (debug|info|warning|error|none)" default:"none" env:"SINGBOX_LOG_LEVEL"` } `embed:"" prefix:""` Metrics struct { @@ -48,7 +48,7 @@ type CLI struct { Password string `name:"metrics-password" help:"Password for metrics if protected by basic auth" default:"MetricsVeryHardPassword" env:"METRICS_PASSWORD"` Instance string `name:"metrics-instance" help:"Instance label for metrics" default:"" env:"METRICS_INSTANCE"` PushURL string `name:"metrics-push-url" help:"Prometheus pushgateway URL (e.g. https://user:pass@host:port)" default:"" env:"METRICS_PUSH_URL"` - BasePath string `name:"metrics-base-path" help:"URL path to metrics (e.g. /xray/metrics)" default:"" env:"METRICS_BASE_PATH"` + BasePath string `name:"metrics-base-path" help:"URL path to metrics (e.g. /singbox/metrics)" default:"" env:"METRICS_BASE_PATH"` } `embed:"" prefix:""` Version VersionFlag `name:"version" help:"Print version information and quit"` @@ -60,9 +60,9 @@ type VersionFlag string func (v VersionFlag) Decode(ctx *kong.DecodeContext) error { return nil } func (v VersionFlag) IsBool() bool { return true } func (v VersionFlag) BeforeApply(app *kong.Kong, vars kong.Vars) error { - fmt.Println("Xray Checker: A Prometheus exporter for monitoring Xray proxies") + fmt.Println("Singbox Checker: A Prometheus exporter for monitoring Singbox proxies") fmt.Printf("Version:\t %s\n", vars["version"]) - fmt.Printf("GitHub: https://github.com/kutovoys/xray-checker\n") + fmt.Printf("GitHub: https://github.com/kutovoys/singbox-checker\n") app.Exit(0) return nil } diff --git a/main.go b/main.go index 48311523..2e8d5b72 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,9 @@ import ( "xray-checker/config" "xray-checker/metrics" "xray-checker/runner" + singbox "xray-checker/singbox" "xray-checker/subscription" "xray-checker/web" - "xray-checker/xray" "github.com/go-co-op/gocron" "github.com/prometheus/client_golang/prometheus" @@ -24,22 +24,22 @@ var ( func main() { config.Parse(version) - log.Printf("Xray Checker %s starting...\n", version) + log.Printf("Singbox Checker %s starting...\n", version) - configFile := "xray_config.json" + configFile := "singbox_config.json" proxyConfigs, err := subscription.InitializeConfiguration(configFile) if err != nil { log.Fatalf("Error initializing configuration: %v", err) } - xrayRunner := runner.NewXrayRunner(configFile) - if err := xrayRunner.Start(); err != nil { - log.Fatalf("Error starting Xray: %v", err) + singboxRunner := runner.NewSingboxRunner(configFile) + if err := singboxRunner.Start(); err != nil { + log.Fatalf("Error starting Singbox: %v", err) } defer func() { - if err := xrayRunner.Stop(); err != nil { - log.Printf("Error stopping Xray: %v", err) + if err := singboxRunner.Stop(); err != nil { + log.Printf("Error stopping Singbox: %v", err) } }() @@ -51,7 +51,7 @@ func main() { proxyChecker := checker.NewProxyChecker( *proxyConfigs, - config.CLIConfig.Xray.StartPort, + config.CLIConfig.Singbox.StartPort, config.CLIConfig.Proxy.IpCheckUrl, config.CLIConfig.Proxy.Timeout, config.CLIConfig.Proxy.StatusCheckUrl, @@ -92,8 +92,8 @@ func main() { newConfigs, err := subscription.ReadFromSource(config.CLIConfig.Subscription.URL) if err != nil { log.Printf("Error checking subscription updates: %v", err) - } else if !xray.IsConfigsEqual(*proxyConfigs, newConfigs) { - if err := xray.UpdateConfiguration(newConfigs, proxyConfigs, xrayRunner, proxyChecker); err != nil { + } else if !singbox.IsConfigsEqual(*proxyConfigs, newConfigs) { + if err := singbox.UpdateConfiguration(newConfigs, proxyConfigs, singboxRunner, proxyChecker); err != nil { log.Printf("Error updating configuration: %v", err) } } @@ -120,7 +120,7 @@ func main() { protectedHandler.Handle("/", web.IndexHandler(version, proxyChecker)) protectedHandler.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) - web.RegisterConfigEndpoints(*proxyConfigs, proxyChecker, config.CLIConfig.Xray.StartPort) + web.RegisterConfigEndpoints(*proxyConfigs, proxyChecker, config.CLIConfig.Singbox.StartPort) protectedHandler.Handle("/config/", web.ConfigStatusHandler(proxyChecker)) if config.CLIConfig.Metrics.Protected { diff --git a/runner/singbox.go b/runner/singbox.go new file mode 100644 index 00000000..79333e1e --- /dev/null +++ b/runner/singbox.go @@ -0,0 +1,51 @@ +package runner + +import ( + "fmt" + "log" + "os" + "os/exec" +) + +type SingboxRunner struct { + cmd *exec.Cmd + configFile string +} + +func NewSingboxRunner(configFile string) *SingboxRunner { + return &SingboxRunner{configFile: configFile} +} + +func (r *SingboxRunner) Start() error { + r.cmd = exec.Command("sing-box", "run", "-c", r.configFile) + r.cmd.Stdout = os.Stdout + r.cmd.Stderr = os.Stderr + + if err := r.cmd.Start(); err != nil { + return fmt.Errorf("error starting sing-box: %v", err) + } + + go func() { + _ = r.cmd.Wait() + r.cmd = nil + }() + + log.Println("sing-box started successfully") + return nil +} + +func (r *SingboxRunner) Stop() error { + if r.cmd != nil && r.cmd.Process != nil { + if err := r.cmd.Process.Signal(os.Interrupt); err != nil { + return fmt.Errorf("error stopping sing-box: %v", err) + } + _, _ = r.cmd.Process.Wait() + r.cmd = nil + log.Println("sing-box stopped successfully") + } + return nil +} + +func (r *SingboxRunner) IsRunning() bool { + return r.cmd != nil && r.cmd.ProcessState == nil +} diff --git a/xray/xray.go b/singbox/singbox.go similarity index 74% rename from xray/xray.go rename to singbox/singbox.go index 0b30b296..ab4027e3 100644 --- a/xray/xray.go +++ b/singbox/singbox.go @@ -1,4 +1,4 @@ -package xray +package singbox import ( "bytes" @@ -15,20 +15,20 @@ import ( ) type TemplateData struct { - Proxies []*models.ProxyConfig - StartPort int - XrayLogLevel string + Proxies []*models.ProxyConfig + StartPort int + LogLevel string } -func generateConfig(proxies []*models.ProxyConfig, startPort int, xrayLogLevel string) ([]byte, error) { +func generateConfig(proxies []*models.ProxyConfig, startPort int, logLevel string) ([]byte, error) { if len(proxies) == 0 { return nil, fmt.Errorf("no valid proxy configurations found") } data := TemplateData{ - Proxies: proxies, - StartPort: startPort, - XrayLogLevel: xrayLogLevel, + Proxies: proxies, + StartPort: startPort, + LogLevel: logLevel, } funcMap := template.FuncMap{ @@ -42,9 +42,9 @@ func generateConfig(proxies []*models.ProxyConfig, startPort int, xrayLogLevel s }, } - tmpl, err := template.New("xray.json.tmpl"). + tmpl, err := template.New("singbox.json.tmpl"). Funcs(funcMap). - ParseFS(templates, "templates/xray.json.tmpl") + ParseFS(templates, "templates/singbox.json.tmpl") if err != nil { return nil, fmt.Errorf("error parsing template: %v", err) } @@ -80,8 +80,8 @@ func PrepareProxyConfigs(proxies []*models.ProxyConfig) { } } -func GenerateAndSaveConfig(proxies []*models.ProxyConfig, startPort int, filename string, xrayLogLevel string) error { - configBytes, err := generateConfig(proxies, startPort, xrayLogLevel) +func GenerateAndSaveConfig(proxies []*models.ProxyConfig, startPort int, filename string, logLevel string) error { + configBytes, err := generateConfig(proxies, startPort, logLevel) if err != nil { return fmt.Errorf("error generating config: %v", err) } @@ -94,30 +94,30 @@ func GenerateAndSaveConfig(proxies []*models.ProxyConfig, startPort int, filenam } func UpdateConfiguration(newConfigs []*models.ProxyConfig, currentConfigs *[]*models.ProxyConfig, - xrayRunner *runner.XrayRunner, proxyChecker *checker.ProxyChecker) error { + singboxRunner *runner.SingboxRunner, proxyChecker *checker.ProxyChecker) error { log.Println("Found changes in subscription, updating configuration...") PrepareProxyConfigs(newConfigs) - configFile := "xray_config.json" - if err := GenerateAndSaveConfig(newConfigs, config.CLIConfig.Xray.StartPort, configFile, config.CLIConfig.Xray.LogLevel); err != nil { - return fmt.Errorf("error generating new Xray config: %v", err) + configFile := "singbox_config.json" + if err := GenerateAndSaveConfig(newConfigs, config.CLIConfig.Singbox.StartPort, configFile, config.CLIConfig.Singbox.LogLevel); err != nil { + return fmt.Errorf("error generating new Singbox config: %v", err) } - if err := xrayRunner.Stop(); err != nil { - return fmt.Errorf("error stopping Xray: %v", err) + if err := singboxRunner.Stop(); err != nil { + return fmt.Errorf("error stopping Singbox: %v", err) } - if err := xrayRunner.Start(); err != nil { - return fmt.Errorf("error starting Xray with new config: %v", err) + if err := singboxRunner.Start(); err != nil { + return fmt.Errorf("error starting Singbox with new config: %v", err) } proxyChecker.UpdateProxies(newConfigs) *currentConfigs = newConfigs - web.RegisterConfigEndpoints(newConfigs, proxyChecker, config.CLIConfig.Xray.StartPort) + web.RegisterConfigEndpoints(newConfigs, proxyChecker, config.CLIConfig.Singbox.StartPort) log.Println("Configuration updated successfully") return nil diff --git a/singbox/template.go b/singbox/template.go new file mode 100644 index 00000000..6dceaa35 --- /dev/null +++ b/singbox/template.go @@ -0,0 +1,6 @@ +package singbox + +import "embed" + +//go:embed templates/singbox.json.tmpl +var templates embed.FS diff --git a/xray/templates/xray.json.tmpl b/singbox/templates/singbox.json.tmpl similarity index 99% rename from xray/templates/xray.json.tmpl rename to singbox/templates/singbox.json.tmpl index 20618fad..899dcb15 100644 --- a/xray/templates/xray.json.tmpl +++ b/singbox/templates/singbox.json.tmpl @@ -1,7 +1,7 @@ {{/* Base template structure */}} { "log": { - "loglevel": "{{.XrayLogLevel}}" + "loglevel": "{{.LogLevel}}" }, "inbounds": [ {{- range $index, $proxy := .Proxies }} diff --git a/subscription/subscription.go b/subscription/subscription.go index 6464fa1e..a12f2ed8 100644 --- a/subscription/subscription.go +++ b/subscription/subscription.go @@ -14,8 +14,8 @@ import ( "xray-checker/config" "xray-checker/models" "xray-checker/parser" + singbox "xray-checker/singbox" "xray-checker/utils" - "xray-checker/xray" ) func InitializeConfiguration(configFile string) (*[]*models.ProxyConfig, error) { @@ -25,9 +25,9 @@ func InitializeConfiguration(configFile string) (*[]*models.ProxyConfig, error) } proxyConfigs := &configs - xray.PrepareProxyConfigs(*proxyConfigs) - if err := xray.GenerateAndSaveConfig(*proxyConfigs, config.CLIConfig.Xray.StartPort, configFile, config.CLIConfig.Xray.LogLevel); err != nil { - return nil, fmt.Errorf("error generating Xray config: %v", err) + singbox.PrepareProxyConfigs(*proxyConfigs) + if err := singbox.GenerateAndSaveConfig(*proxyConfigs, config.CLIConfig.Singbox.StartPort, configFile, config.CLIConfig.Singbox.LogLevel); err != nil { + return nil, fmt.Errorf("error generating Singbox config: %v", err) } return proxyConfigs, nil diff --git a/web/handlers.go b/web/handlers.go index 35eacebf..5c007ae3 100644 --- a/web/handlers.go +++ b/web/handlers.go @@ -28,7 +28,7 @@ func IndexHandler(version string, proxyChecker *checker.ProxyChecker) http.Handl return } - RegisterConfigEndpoints(proxyChecker.GetProxies(), proxyChecker, config.CLIConfig.Xray.StartPort) + RegisterConfigEndpoints(proxyChecker.GetProxies(), proxyChecker, config.CLIConfig.Singbox.StartPort) data := PageData{ Version: version, @@ -42,7 +42,7 @@ func IndexHandler(version string, proxyChecker *checker.ProxyChecker) http.Handl Timeout: config.CLIConfig.Proxy.Timeout, SubscriptionUpdate: config.CLIConfig.Subscription.Update, SubscriptionUpdateInterval: config.CLIConfig.Subscription.UpdateInterval, - StartPort: config.CLIConfig.Xray.StartPort, + StartPort: config.CLIConfig.Singbox.StartPort, Instance: config.CLIConfig.Metrics.Instance, PushUrl: metrics.GetPushURL(config.CLIConfig.Metrics.PushURL), Endpoints: registeredEndpoints, diff --git a/xray/template.go b/xray/template.go deleted file mode 100644 index 13fd52e4..00000000 --- a/xray/template.go +++ /dev/null @@ -1,6 +0,0 @@ -package xray - -import "embed" - -//go:embed templates/xray.json.tmpl -var templates embed.FS