integrate core codes

This commit is contained in:
Alireza Ahmadi
2024-12-16 00:12:09 +01:00
parent f1b6c8a131
commit ecd9348a0f
22 changed files with 1528 additions and 424 deletions
+99 -39
View File
@@ -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
View File
@@ -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[:])}
}
-45
View File
@@ -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
}
+8 -4
View File
@@ -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).