Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -8,15 +8,15 @@
[![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.

<div align="center">
<img src=".github/screen/xray-checker.png" alt="Dashboard Screenshot">
</div>

## 🚀 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
Expand Down Expand Up @@ -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
Expand All @@ -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).

<p align="center">
Thanks to the all contributors who have helped improve Xray Checker:
Thanks to the all contributors who have helped improve Singbox Checker:
</p>
<p align="center">
<a href="https://github.com/kutovoys/xray-checker/graphs/contributors">
Expand Down
8 changes: 4 additions & 4 deletions README_RU.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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-эндпоинты для интеграции с системами мониторинга.

<div align="center">
<img src=".github/screen/xray-checker.png" alt="Dashboard Screenshot">
Expand Down Expand Up @@ -68,7 +68,7 @@ services:

## 🤝 Участие в разработке

Мы рады любому вкладу в развитие Xray Checker! Если вы хотите помочь:
Мы рады любому вкладу в развитие Singbox Checker! Если вы хотите помочь:

1. Сделайте форк репозитория
2. Создайте ветку для ваших изменений
Expand All @@ -78,7 +78,7 @@ services:
Подробнее о том, как внести свой вклад, читайте в [руководстве для контрибьюторов](https://xray-checker.kutovoy.dev/ru/contributing/development-guide).

<p align="center">
Спасибо всем контрибьюторам, которые помогли улучшить Xray Checker:
Спасибо всем контрибьюторам, которые помогли улучшить Singbox Checker:
</p>
<p align="center">
<a href="https://github.com/kutovoys/xray-checker/graphs/contributors">
Expand Down
16 changes: 8 additions & 8 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -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 {
Expand All @@ -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"`
Expand All @@ -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
}
24 changes: 12 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
}
}()

Expand All @@ -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,
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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 {
Expand Down
51 changes: 51 additions & 0 deletions runner/singbox.go
Original file line number Diff line number Diff line change
@@ -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
}
42 changes: 21 additions & 21 deletions xray/xray.go → singbox/singbox.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package xray
package singbox

import (
"bytes"
Expand All @@ -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{
Expand All @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions singbox/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package singbox

import "embed"

//go:embed templates/singbox.json.tmpl
var templates embed.FS
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{/* Base template structure */}}
{
"log": {
"loglevel": "{{.XrayLogLevel}}"
"loglevel": "{{.LogLevel}}"
},
"inbounds": [
{{- range $index, $proxy := .Proxies }}
Expand Down
8 changes: 4 additions & 4 deletions subscription/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand Down
Loading