integrate core codes
This commit is contained in:
+99
-39
@@ -4,23 +4,24 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"s-ui/config"
|
||||
"s-ui/core"
|
||||
"s-ui/database"
|
||||
"s-ui/database/model"
|
||||
"s-ui/logger"
|
||||
"s-ui/singbox"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ApiAddr string
|
||||
var LastUpdate int64
|
||||
var IsSystemd bool
|
||||
var (
|
||||
LastUpdate int64
|
||||
IsSystemd bool
|
||||
corePtr *core.Core
|
||||
)
|
||||
|
||||
type ConfigService struct {
|
||||
ClientService
|
||||
TlsService
|
||||
InDataService
|
||||
singbox.Controller
|
||||
SettingService
|
||||
}
|
||||
|
||||
@@ -34,7 +35,8 @@ type SingBoxConfig struct {
|
||||
Experimental json.RawMessage `json:"experimental"`
|
||||
}
|
||||
|
||||
func NewConfigService() *ConfigService {
|
||||
func NewConfigService(core *core.Core) *ConfigService {
|
||||
corePtr = core
|
||||
return &ConfigService{}
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ func (s *ConfigService) InitConfig() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.RefreshApiAddr(&singboxConfig)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ConfigService) GetConfig() (*SingBoxConfig, error) {
|
||||
@@ -149,6 +151,7 @@ func (s *ConfigService) SaveChanges(changes map[string]string, loginUser string)
|
||||
return err
|
||||
}
|
||||
}
|
||||
needRestart := false
|
||||
if len(configChanges) > 0 {
|
||||
singboxConfig, err := s.GetConfig()
|
||||
if err != nil {
|
||||
@@ -163,36 +166,114 @@ func (s *ConfigService) SaveChanges(changes map[string]string, loginUser string)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRestart = true
|
||||
case "log":
|
||||
newConfig.Log = rawObject
|
||||
needRestart = true
|
||||
case "dns":
|
||||
newConfig.Dns = rawObject
|
||||
needRestart = true
|
||||
case "ntp":
|
||||
newConfig.Ntp = rawObject
|
||||
needRestart = true
|
||||
case "route":
|
||||
newConfig.Route = rawObject
|
||||
needRestart = true
|
||||
case "experimental":
|
||||
newConfig.Experimental = rawObject
|
||||
needRestart = true
|
||||
case "inbounds":
|
||||
if change.Action == "edit" {
|
||||
var object map[string]interface{}
|
||||
err = json.Unmarshal(newConfig.Inbounds[change.Index], &object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tag, ok := object["tag"].(string); ok {
|
||||
err = corePtr.RemoveInbound(tag)
|
||||
if err == nil {
|
||||
err = corePtr.AddInbound(rawObject)
|
||||
if err != nil {
|
||||
needRestart = true
|
||||
}
|
||||
} else {
|
||||
needRestart = true
|
||||
}
|
||||
} else {
|
||||
needRestart = true
|
||||
}
|
||||
newConfig.Inbounds[change.Index] = rawObject
|
||||
} else if change.Action == "del" {
|
||||
var object map[string]interface{}
|
||||
err = json.Unmarshal(newConfig.Inbounds[change.Index], &object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tag, ok := object["tag"].(string); ok {
|
||||
err = corePtr.RemoveInbound(tag)
|
||||
if err != nil {
|
||||
needRestart = true
|
||||
}
|
||||
} else {
|
||||
needRestart = true
|
||||
}
|
||||
newConfig.Inbounds = append(newConfig.Inbounds[:change.Index], newConfig.Inbounds[change.Index+1:]...)
|
||||
} else {
|
||||
newConfig.Inbounds = append(newConfig.Inbounds, rawObject)
|
||||
err = corePtr.AddInbound(rawObject)
|
||||
if err != nil {
|
||||
logger.Debug(err)
|
||||
needRestart = true
|
||||
}
|
||||
}
|
||||
case "outbounds":
|
||||
if change.Action == "edit" {
|
||||
var object map[string]interface{}
|
||||
err = json.Unmarshal(newConfig.Outbounds[change.Index], &object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tag, ok := object["tag"].(string); ok {
|
||||
err = corePtr.RemoveOutbound(tag)
|
||||
if err == nil {
|
||||
err = corePtr.AddOutbound(rawObject)
|
||||
if err != nil {
|
||||
needRestart = true
|
||||
}
|
||||
} else {
|
||||
needRestart = true
|
||||
}
|
||||
} else {
|
||||
needRestart = true
|
||||
}
|
||||
newConfig.Outbounds[change.Index] = rawObject
|
||||
} else if change.Action == "del" {
|
||||
var object map[string]interface{}
|
||||
err = json.Unmarshal(newConfig.Outbounds[change.Index], &object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tag, ok := object["tag"].(string); ok {
|
||||
err = corePtr.RemoveOutbound(tag)
|
||||
if err != nil {
|
||||
needRestart = true
|
||||
}
|
||||
} else {
|
||||
needRestart = true
|
||||
}
|
||||
newConfig.Outbounds = append(newConfig.Outbounds[:change.Index], newConfig.Outbounds[change.Index+1:]...)
|
||||
} else {
|
||||
err = corePtr.AddOutbound(rawObject)
|
||||
if err != nil {
|
||||
logger.Debug(err)
|
||||
needRestart = true
|
||||
}
|
||||
newConfig.Outbounds = append(newConfig.Outbounds, rawObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = s.Save(&newConfig)
|
||||
err = s.Save(&newConfig, needRestart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -238,7 +319,7 @@ func (s *ConfigService) CheckChanges(lu string) (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConfigService) Save(singboxConfig *SingBoxConfig) error {
|
||||
func (s *ConfigService) Save(singboxConfig *SingBoxConfig, needRestart bool) error {
|
||||
configPath := config.GetBinFolderPath()
|
||||
_, err := os.Stat(configPath + "/config.json")
|
||||
if os.IsNotExist(err) {
|
||||
@@ -260,39 +341,14 @@ func (s *ConfigService) Save(singboxConfig *SingBoxConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
s.RefreshApiAddr(singboxConfig)
|
||||
s.Controller.Restart()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ConfigService) RefreshApiAddr(singboxConfig *SingBoxConfig) error {
|
||||
Env_API := config.GetEnvApi()
|
||||
if len(Env_API) > 0 {
|
||||
ApiAddr = Env_API
|
||||
} else {
|
||||
var err error
|
||||
if singboxConfig == nil {
|
||||
singboxConfig, err = s.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var experimental struct {
|
||||
V2rayApi struct {
|
||||
Listen string `json:"listen"`
|
||||
Stats interface{} `jaon:"stats"`
|
||||
} `json:"v2ray_api"`
|
||||
}
|
||||
err = json.Unmarshal(singboxConfig.Experimental, &experimental)
|
||||
if needRestart {
|
||||
err = corePtr.Restart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ApiAddr = experimental.V2rayApi.Listen
|
||||
}
|
||||
// s.Controller.Restart()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -348,7 +404,7 @@ func (s *ConfigService) DepleteClients() error {
|
||||
singboxConfig.Inbounds[inbound_index] = modifiedInbound
|
||||
}
|
||||
|
||||
err = s.Save(singboxConfig)
|
||||
err = s.Save(singboxConfig, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -381,3 +437,7 @@ func (s *ConfigService) GetChanges(actor string, chngKey string, count string) [
|
||||
}
|
||||
return chngs
|
||||
}
|
||||
|
||||
func (s *ConfigService) RestartCore() error {
|
||||
return corePtr.Restart()
|
||||
}
|
||||
|
||||
+57
-49
@@ -1,24 +1,24 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"s-ui/config"
|
||||
"s-ui/logger"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
type ServerService struct {
|
||||
SingBoxService
|
||||
}
|
||||
type ServerService struct{}
|
||||
|
||||
func (s *ServerService) GetStatus(request string) *map[string]interface{} {
|
||||
status := make(map[string]interface{}, 0)
|
||||
@@ -91,15 +91,21 @@ func (s *ServerService) GetNetInfo() map[string]interface{} {
|
||||
}
|
||||
|
||||
func (s *ServerService) GetSingboxInfo() map[string]interface{} {
|
||||
info := make(map[string]interface{}, 0)
|
||||
sysStats, err := s.SingBoxService.GetSysStats()
|
||||
if err == nil {
|
||||
info["running"] = true
|
||||
info["stats"] = sysStats
|
||||
} else {
|
||||
info["running"] = s.SingBoxService.IsRunning()
|
||||
var rtm runtime.MemStats
|
||||
runtime.ReadMemStats(&rtm)
|
||||
isRunning := corePtr.IsRunning()
|
||||
uptime := uint32(0)
|
||||
if isRunning {
|
||||
uptime = corePtr.GetInstance().Uptime()
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"running": isRunning,
|
||||
"stats": map[string]interface{}{
|
||||
"NumGoroutine": uint32(runtime.NumGoroutine()),
|
||||
"Alloc": rtm.Alloc,
|
||||
"Uptime": uptime,
|
||||
},
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
func (s *ServerService) GetSystemInfo() map[string]interface{} {
|
||||
@@ -139,48 +145,50 @@ func (s *ServerService) GetSystemInfo() map[string]interface{} {
|
||||
return info
|
||||
}
|
||||
|
||||
func (s *ServerService) GetLogs(service string, count string, level string) []string {
|
||||
func (s *ServerService) GetLogs(count string, level string) []string {
|
||||
c, _ := strconv.Atoi(count)
|
||||
|
||||
if service == "s-ui" {
|
||||
return logger.GetLogs(c, level)
|
||||
}
|
||||
var lines []string
|
||||
var cmdArgs []string
|
||||
if IsSystemd {
|
||||
cmdArgs = []string{"journalctl", "-u", service, "--no-pager", "-n", count, "-p", level}
|
||||
} else {
|
||||
cmdArgs = []string{"tail", "/logs/" + service + ".log", "-n", count}
|
||||
}
|
||||
// Run the command
|
||||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return []string{"Failed to get logs!", err.Error()}
|
||||
}
|
||||
lines = strings.Split(out.String(), "\n")
|
||||
|
||||
return lines
|
||||
return logger.GetLogs(c, level)
|
||||
}
|
||||
|
||||
func (s *ServerService) GenKeypair(keyType string, options string) []string {
|
||||
if len(keyType) == 0 {
|
||||
return []string{"No keypair to generate"}
|
||||
}
|
||||
sbExec := s.GetBinaryPath()
|
||||
cmdArgs := []string{"generate", keyType + "-keypair"}
|
||||
if keyType == "tls" || keyType == "ech" {
|
||||
cmdArgs = append(cmdArgs, options)
|
||||
|
||||
switch keyType {
|
||||
case "ech":
|
||||
return s.generateECHKeyPair(options)
|
||||
case "tls":
|
||||
return s.generateTLSKeyPair(options)
|
||||
case "reality":
|
||||
return s.generateRealityKeyPair()
|
||||
}
|
||||
// Run the command
|
||||
cmd := exec.Command(sbExec, cmdArgs...)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return []string{"Failed to generate keypair"}
|
||||
}
|
||||
return strings.Split(out.String(), "\n")
|
||||
|
||||
return []string{"Failed to generate keypair"}
|
||||
}
|
||||
|
||||
func (s *ServerService) generateECHKeyPair(options string) []string {
|
||||
parts := strings.Split(options, ",")
|
||||
configPem, keyPem, err := tls.ECHKeygenDefault(parts[0], parts[1] == "true")
|
||||
if err != nil {
|
||||
return []string{"Failed to generate ECH keypair: ", err.Error()}
|
||||
}
|
||||
return append(strings.Split(configPem, "\n"), strings.Split(keyPem, "\n")...)
|
||||
}
|
||||
|
||||
func (s *ServerService) generateTLSKeyPair(serverName string) []string {
|
||||
privateKeyPem, publicKeyPem, err := tls.GenerateKeyPair(time.Now, serverName, time.Now().AddDate(0, 12, 0))
|
||||
if err != nil {
|
||||
return []string{"Failed to generate TLS keypair: ", err.Error()}
|
||||
}
|
||||
return append(strings.Split(string(privateKeyPem), "\n"), strings.Split(string(publicKeyPem), "\n")...)
|
||||
}
|
||||
|
||||
func (s *ServerService) generateRealityKeyPair() []string {
|
||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return []string{"Failed to generate Reality keypair: ", err.Error()}
|
||||
}
|
||||
publicKey := privateKey.PublicKey()
|
||||
return []string{"PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey[:]), "PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey[:])}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"s-ui/singbox"
|
||||
)
|
||||
|
||||
type SingBoxService struct {
|
||||
singbox.V2rayAPI
|
||||
singbox.Controller
|
||||
StatsService
|
||||
}
|
||||
|
||||
func (s *SingBoxService) GetStats() error {
|
||||
s.V2rayAPI.Init(ApiAddr)
|
||||
defer s.V2rayAPI.Close()
|
||||
stats, err := s.V2rayAPI.GetStats(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.StatsService.SaveStats(stats)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SingBoxService) GetSysStats() (*map[string]interface{}, error) {
|
||||
err := s.V2rayAPI.Init(ApiAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.V2rayAPI.Close()
|
||||
resp, err := s.V2rayAPI.GetSysStats()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
result["NumGoroutine"] = resp.NumGoroutine
|
||||
result["Alloc"] = resp.Alloc
|
||||
result["Uptime"] = resp.Uptime
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
@@ -19,18 +19,22 @@ var onlineResources = &onlines{}
|
||||
type StatsService struct {
|
||||
}
|
||||
|
||||
func (s *StatsService) SaveStats(stats []*model.Stats) error {
|
||||
var err error
|
||||
func (s *StatsService) SaveStats() error {
|
||||
if !corePtr.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
stats := corePtr.GetInstance().ConnTracker().GetStats()
|
||||
|
||||
// Reset onlines
|
||||
onlineResources.Inbound = nil
|
||||
onlineResources.Outbound = nil
|
||||
onlineResources.User = nil
|
||||
|
||||
if len(stats) == 0 {
|
||||
if len(*stats) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
db := database.GetDB()
|
||||
tx := db.Begin()
|
||||
defer func() {
|
||||
@@ -41,7 +45,7 @@ func (s *StatsService) SaveStats(stats []*model.Stats) error {
|
||||
}
|
||||
}()
|
||||
|
||||
for _, stat := range stats {
|
||||
for _, stat := range *stats {
|
||||
if stat.Resource == "user" {
|
||||
if stat.Direction {
|
||||
err = tx.Model(model.Client{}).Where("name = ?", stat.Tag).
|
||||
|
||||
Reference in New Issue
Block a user