3x-ui/node/xray/manager.go

127 lines
3 KiB
Go
Raw Normal View History

2026-01-05 21:12:53 +00:00
// Package xray provides XRAY Core management for the node service.
package xray
import (
"encoding/json"
"errors"
"fmt"
"sync"
"github.com/mhsanaei/3x-ui/v2/logger"
"github.com/mhsanaei/3x-ui/v2/xray"
)
// Manager manages the XRAY Core process lifecycle.
type Manager struct {
process *xray.Process
lock sync.Mutex
config *xray.Config
}
// NewManager creates a new XRAY manager instance.
func NewManager() *Manager {
return &Manager{}
}
// IsRunning returns true if XRAY is currently running.
func (m *Manager) IsRunning() bool {
m.lock.Lock()
defer m.lock.Unlock()
return m.process != nil && m.process.IsRunning()
}
// GetStatus returns the current status of XRAY.
func (m *Manager) GetStatus() map[string]interface{} {
m.lock.Lock()
defer m.lock.Unlock()
status := map[string]interface{}{
"running": m.process != nil && m.process.IsRunning(),
"version": "Unknown",
"uptime": 0,
}
if m.process != nil && m.process.IsRunning() {
status["version"] = m.process.GetVersion()
status["uptime"] = m.process.GetUptime()
}
return status
}
// ApplyConfig applies a new XRAY configuration and restarts if needed.
func (m *Manager) ApplyConfig(configJSON []byte) error {
m.lock.Lock()
defer m.lock.Unlock()
var newConfig xray.Config
if err := json.Unmarshal(configJSON, &newConfig); err != nil {
return fmt.Errorf("failed to parse config: %w", err)
}
// If XRAY is running and config is the same, skip restart
if m.process != nil && m.process.IsRunning() {
oldConfig := m.process.GetConfig()
if oldConfig != nil && oldConfig.Equals(&newConfig) {
logger.Info("Config unchanged, skipping restart")
return nil
}
// Stop existing process
if err := m.process.Stop(); err != nil {
logger.Warningf("Failed to stop existing XRAY: %v", err)
}
}
// Start new process with new config
m.config = &newConfig
m.process = xray.NewProcess(&newConfig)
if err := m.process.Start(); err != nil {
return fmt.Errorf("failed to start XRAY: %w", err)
}
logger.Info("XRAY configuration applied successfully")
return nil
}
// Reload reloads XRAY configuration without full restart (if supported).
// Falls back to restart if reload is not available.
func (m *Manager) Reload() error {
m.lock.Lock()
defer m.lock.Unlock()
if m.process == nil || !m.process.IsRunning() {
return errors.New("XRAY is not running")
}
// XRAY doesn't support hot reload, so we need to restart
// Save current config
if m.config == nil {
return errors.New("no config to reload")
}
// Stop and restart
if err := m.process.Stop(); err != nil {
return fmt.Errorf("failed to stop XRAY: %w", err)
}
m.process = xray.NewProcess(m.config)
if err := m.process.Start(); err != nil {
return fmt.Errorf("failed to restart XRAY: %w", err)
}
logger.Info("XRAY reloaded successfully")
return nil
}
// Stop stops the XRAY process.
func (m *Manager) Stop() error {
m.lock.Lock()
defer m.lock.Unlock()
if m.process == nil || !m.process.IsRunning() {
return nil
}
return m.process.Stop()
}