all adjustments

This commit is contained in:
Alireza Ahmadi
2025-01-03 23:32:03 +01:00
parent ed48cdca33
commit fe428ed412
62 changed files with 2352 additions and 1975 deletions
+108 -10
View File
@@ -1,9 +1,11 @@
package api
import (
"encoding/json"
"s-ui/logger"
"s-ui/service"
"s-ui/util"
"s-ui/util/common"
"strconv"
"strings"
@@ -17,6 +19,8 @@ type APIHandler struct {
service.ClientService
service.TlsService
service.InboundService
service.OutboundService
service.EndpointService
service.PanelService
service.StatsService
service.ServerService
@@ -43,6 +47,7 @@ func (a *APIHandler) postHandler(c *gin.Context) {
var err error
action := c.Param("postAction")
remoteIP := getRemoteIp(c)
loginUser := GetLoginUser(c)
switch action {
case "login":
@@ -79,13 +84,21 @@ func (a *APIHandler) postHandler(c *gin.Context) {
jsonMsg(c, "", err)
}
case "save":
loginUser := GetLoginUser(c)
data := map[string]string{}
err = c.ShouldBind(&data)
if err == nil {
err = a.ConfigService.SaveChanges(data, loginUser)
obj := c.Request.FormValue("object")
act := c.Request.FormValue("action")
data := c.Request.FormValue("data")
userLinks := c.Request.FormValue("userLinks")
outJsons := c.Request.FormValue("outJsons")
err = a.ConfigService.Save(obj, act, json.RawMessage(data), json.RawMessage(userLinks), json.RawMessage(outJsons), loginUser)
if err != nil {
jsonMsg(c, "save", err)
return
}
jsonMsg(c, "save", err)
err = a.loadPartialData(c, obj, len(outJsons) > 5, len(userLinks) > 5)
if err != nil {
jsonMsg(c, obj, err)
}
return
case "restartApp":
err = a.PanelService.RestartPanel(3)
jsonMsg(c, "restartApp", err)
@@ -97,7 +110,7 @@ func (a *APIHandler) postHandler(c *gin.Context) {
result, _, err := util.GetOutbound(link, 0)
jsonObj(c, result, err)
default:
jsonMsg(c, "API call", nil)
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
}
}
@@ -119,6 +132,12 @@ func (a *APIHandler) getHandler(c *gin.Context) {
return
}
jsonObj(c, data, nil)
case "inbounds", "outbounds", "endpoints", "tls", "clients", "config":
err := a.loadPartialData(c, action, false, false)
if err != nil {
jsonMsg(c, action, err)
}
return
case "users":
users, err := a.UserService.GetUsers()
if err != nil {
@@ -170,7 +189,7 @@ func (a *APIHandler) getHandler(c *gin.Context) {
keypair := a.ServerService.GenKeypair(kType, options)
jsonObj(c, keypair, nil)
default:
jsonMsg(c, "API call", nil)
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
}
}
@@ -195,7 +214,7 @@ func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) {
return "", err
}
if isUpdated {
config, err := a.ConfigService.GetConfig()
config, err := a.SettingService.GetConfig()
if err != nil {
return "", err
}
@@ -211,14 +230,24 @@ func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) {
if err != nil {
return "", err
}
outbounds, err := a.OutboundService.GetAll()
if err != nil {
return "", err
}
endpoints, err := a.EndpointService.GetAll()
if err != nil {
return "", err
}
subURI, err := a.SettingService.GetFinalSubURI(strings.Split(c.Request.Host, ":")[0])
if err != nil {
return "", err
}
data["config"] = *config
data["config"] = json.RawMessage(config)
data["clients"] = clients
data["tls"] = tlsConfigs
data["inbounds"] = inbounds
data["outbounds"] = outbounds
data["endpoints"] = endpoints
data["subURI"] = subURI
data["onlines"] = onlines
} else {
@@ -227,3 +256,72 @@ func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) {
return data, nil
}
func (a *APIHandler) loadPartialData(c *gin.Context, obj string, plusInbounds bool, plusClients bool) error {
data := make(map[string]interface{}, 0)
switch obj {
case "inbounds":
id := c.Query("id")
inbounds, err := a.InboundService.Get(id)
if err != nil {
return err
}
data[obj] = inbounds
case "outbounds":
outbounds, err := a.OutboundService.GetAll()
if err != nil {
return err
}
data[obj] = outbounds
case "endpoints":
endpoints, err := a.EndpointService.GetAll()
if err != nil {
return err
}
data[obj] = endpoints
case "tls":
tlsConfigs, err := a.TlsService.GetAll()
if err != nil {
return err
}
data[obj] = tlsConfigs
case "clients":
clients, err := a.ClientService.GetAll()
if err != nil {
return err
}
data[obj] = clients
case "config":
config, err := a.SettingService.GetConfig()
if err != nil {
return err
}
data[obj] = json.RawMessage(config)
}
if plusInbounds {
inbounds, err := a.InboundService.GetAll()
if err != nil {
return err
}
data["inbounds"] = inbounds
}
if plusClients {
clients, err := a.ClientService.GetAll()
if err != nil {
return err
}
data["clients"] = clients
}
jsonObj(c, data, nil)
return nil
}
func (a *APIHandler) postActions(c *gin.Context) (string, json.RawMessage, error) {
var data map[string]json.RawMessage
err := c.ShouldBind(&data)
if err != nil {
return "", nil, err
}
return string(data["action"]), data["data"], nil
}
+1 -1
View File
@@ -46,7 +46,7 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) {
}
} else {
m.Success = false
m.Msg = msg + err.Error()
m.Msg = msg + ": " + err.Error()
logger.Warning("failed :", err)
}
c.JSON(http.StatusOK, m)
+2 -2
View File
@@ -43,7 +43,7 @@ func (a *APP) Init() error {
a.core = core.NewCore()
a.cronJob = cronjob.NewCronJob(a.core)
a.cronJob = cronjob.NewCronJob()
a.webServer = web.NewServer()
a.subServer = sub.NewServer()
@@ -81,7 +81,7 @@ func (a *APP) Start() error {
return err
}
err = a.configService.StartCore()
err = a.configService.StartCore("")
if err != nil {
logger.Error(err)
}
+15 -2
View File
@@ -60,9 +60,22 @@ func moveJsonToDb(db *gorm.DB) error {
} else {
tls_server, _ := json.MarshalIndent(tlsObj, "", " ")
if len(tls_server) > 5 {
tlsObject := tlsObj.(map[string]interface{})
tlsClientObj := map[string]interface{}{}
if enabled, ok := tlsObject["enabled"]; ok {
tlsClientObj["enabled"] = enabled
}
if alpn, ok := tlsObject["alpn"]; ok {
tlsClientObj["alpn"] = alpn
}
if sni, ok := tlsObject["server_name"]; ok {
tlsClientObj["server_name"] = sni
}
tls_client, _ := json.MarshalIndent(tlsClientObj, "", " ")
newTls := &model.Tls{
Name: tag,
Server: tls_server,
Client: tls_client,
}
err = db.Create(newTls).Error
if err != nil {
@@ -76,10 +89,10 @@ func moveJsonToDb(db *gorm.DB) error {
var inbData InboundData
db.Raw("select id,addrs,out_json from inbound_data where tag = ?", tag).Find(&inbData)
if inbData.Id > 0 {
inbObj["outJson"] = inbData.OutJson
inbObj["out_json"] = inbData.OutJson
inbObj["addrs"] = inbData.Addrs
} else {
inbObj["outJson"] = json.RawMessage("{}")
inbObj["out_json"] = json.RawMessage("{}")
inbObj["addrs"] = json.RawMessage("[]")
}
inbJson, _ := json.Marshal(inbObj)
+12 -16
View File
@@ -109,20 +109,16 @@ func NewBox(options Options) (*Box, error) {
defaultLogWriter = io.Discard
}
var logFactory log.Factory
if factory == nil {
logFactory, err = NewFactory(log.Options{
Context: ctx,
Options: sbCommon.PtrValueOrDefault(options.Log),
DefaultWriter: defaultLogWriter,
BaseTime: createdAt,
})
if err != nil {
return nil, common.NewError("create log factory", err)
}
factory = logFactory
} else {
logFactory = factory
logFactory, err = NewFactory(log.Options{
Context: ctx,
Options: sbCommon.PtrValueOrDefault(options.Log),
DefaultWriter: defaultLogWriter,
BaseTime: createdAt,
})
if err != nil {
return nil, common.NewError("create log factory", err)
}
factory = logFactory
routeOptions := sbCommon.PtrValueOrDefault(options.Route)
endpointManager := endpoint.NewManager(logFactory.NewLogger("endpoint"), endpointRegistry)
@@ -158,7 +154,7 @@ func NewBox(options Options) (*Box, error) {
endpointOptions.Options,
)
if err != nil {
return nil, common.NewError("initialize endpoint["+string(i)+"] "+tag, err)
return nil, common.NewError("initialize endpoint["+F.ToString(i)+"] "+tag, err)
}
}
for i, inboundOptions := range options.Inbounds {
@@ -202,7 +198,7 @@ func NewBox(options Options) (*Box, error) {
outboundOptions.Options,
)
if err != nil {
return nil, common.NewError("initialize outbound["+string(i)+"] "+tag, err)
return nil, common.NewError("initialize outbound["+F.ToString(i)+"] "+tag, err)
}
}
outboundManager.Initialize(sbCommon.Must1(
@@ -368,7 +364,7 @@ func (s *Box) Close() error {
close(s.done)
}
err := sbCommon.Close(
s.inbound, s.outbound, s.router, s.connection, s.network,
s.endpoint, s.inbound, s.outbound, s.router, s.connection, s.network,
)
for _, lifecycleService := range s.services {
err1 := lifecycleService.Close()
+2 -2
View File
@@ -58,7 +58,7 @@ func (c *Core) AddOutbound(config []byte) error {
factory.NewLogger("outbound/"+outbound_config.Type+"["+outbound_config.Tag+"]"),
outbound_config.Tag,
outbound_config.Type,
outbound_config)
outbound_config.Options)
if err != nil {
return err
}
@@ -92,7 +92,7 @@ func (c *Core) AddEndpoint(config []byte) error {
factory.NewLogger("endpoint/"+endpoint_config.Type+"["+endpoint_config.Tag+"]"),
endpoint_config.Tag,
endpoint_config.Type,
endpoint_config)
endpoint_config.Options)
if err != nil {
return err
}
+17
View File
@@ -0,0 +1,17 @@
package cronjob
import (
"s-ui/service"
)
type CheckCoreJob struct {
service.ConfigService
}
func NewCheckCoreJob() *CheckCoreJob {
return &CheckCoreJob{}
}
func (s *CheckCoreJob) Run() {
s.ConfigService.StartCore("")
}
+4 -6
View File
@@ -1,7 +1,6 @@
package cronjob
import (
"s-ui/core"
"time"
"github.com/robfig/cron/v3"
@@ -9,13 +8,10 @@ import (
type CronJob struct {
cron *cron.Cron
Core *core.Core
}
func NewCronJob(c *core.Core) *CronJob {
return &CronJob{
Core: c,
}
func NewCronJob() *CronJob {
return &CronJob{}
}
func (c *CronJob) Start(loc *time.Location, trafficAge int) error {
@@ -29,6 +25,8 @@ func (c *CronJob) Start(loc *time.Location, trafficAge int) error {
c.cron.AddJob("@every 1m", NewDepleteJob())
// Start deleting old stats
c.cron.AddJob("@daily", NewDelStatsJob(trafficAge))
// Start core if it is not running
c.cron.AddJob("@every 5s", NewCheckCoreJob())
}()
return nil
+2 -2
View File
@@ -6,7 +6,7 @@ import (
)
type DepleteJob struct {
service.ConfigService
service.ClientService
}
func NewDepleteJob() *DepleteJob {
@@ -14,7 +14,7 @@ func NewDepleteJob() *DepleteJob {
}
func (s *DepleteJob) Run() {
err := s.ConfigService.DepleteClients()
err := s.ClientService.DepleteClients()
if err != nil {
logger.Warning("Disable depleted users failed: ", err)
return
+7 -5
View File
@@ -1,11 +1,13 @@
package model
import "encoding/json"
import (
"encoding/json"
)
type Endpoint struct {
Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
Type string `json:"type" form:"type"`
Tag string `json:"tag" form:"tag"`
Tag string `json:"tag" form:"tag" gorm:"unique"`
Options json.RawMessage `json:"-" form:"-"`
}
@@ -17,10 +19,10 @@ func (o *Endpoint) UnmarshalJSON(data []byte) error {
}
// Extract fixed fields and store the rest in Options
if val, exists := raw["id"]; exists {
o.Id = val.(uint)
delete(raw, "id")
if val, exists := raw["id"].(float64); exists {
o.Id = uint(val)
}
delete(raw, "id")
o.Type, _ = raw["type"].(string)
delete(raw, "type")
o.Tag = raw["tag"].(string)
+29 -7
View File
@@ -7,14 +7,14 @@ import (
type Inbound struct {
Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
Type string `json:"type" form:"type"`
Tag string `json:"tag" form:"tag"`
Tag string `json:"tag" form:"tag" gorm:"unique"`
// Foreign key to tls table
TlsId uint `json:"tls_id" form:"tls_id"`
Tls *Tls `json:"tls" form:"tls" gorm:"foreignKey:TlsId;references:Id"`
Addrs json.RawMessage `json:"addrs" form:"addrs"`
OutJson json.RawMessage `json:"outJson" form:"outJson"`
OutJson json.RawMessage `json:"out_json" form:"out_json"`
Options json.RawMessage `json:"-" form:"-"`
}
@@ -26,10 +26,10 @@ func (i *Inbound) UnmarshalJSON(data []byte) error {
}
// Extract fixed fields and store the rest in Options
if val, exists := raw["id"].(uint); exists {
i.Id = val
delete(raw, "id")
if val, exists := raw["id"].(float64); exists {
i.Id = uint(val)
}
delete(raw, "id")
i.Type, _ = raw["type"].(string)
delete(raw, "type")
i.Tag, _ = raw["tag"].(string)
@@ -48,8 +48,8 @@ func (i *Inbound) UnmarshalJSON(data []byte) error {
delete(raw, "addrs")
// OutJson
i.OutJson, _ = json.MarshalIndent(raw["outJson"], "", " ")
delete(raw, "outJson")
i.OutJson, _ = json.MarshalIndent(raw["out_json"], "", " ")
delete(raw, "out_json")
// Remaining fields
i.Options, err = json.MarshalIndent(raw, "", " ")
@@ -79,3 +79,25 @@ func (i Inbound) MarshalJSON() ([]byte, error) {
return json.Marshal(combined)
}
func (i Inbound) MarshalFull() (*map[string]interface{}, error) {
combined := make(map[string]interface{})
combined["id"] = i.Id
combined["type"] = i.Type
combined["tag"] = i.Tag
combined["tls_id"] = i.TlsId
combined["addrs"] = i.Addrs
combined["out_json"] = i.OutJson
if i.Options != nil {
var restFields map[string]json.RawMessage
if err := json.Unmarshal(i.Options, &restFields); err != nil {
return nil, err
}
for k, v := range restFields {
combined[k] = v
}
}
return &combined, nil
}
+4 -4
View File
@@ -5,7 +5,7 @@ import "encoding/json"
type Outbound struct {
Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
Type string `json:"type" form:"type"`
Tag string `json:"tag" form:"tag"`
Tag string `json:"tag" form:"tag" gorm:"unique"`
Options json.RawMessage `json:"-" form:"-"`
}
@@ -17,10 +17,10 @@ func (o *Outbound) UnmarshalJSON(data []byte) error {
}
// Extract fixed fields and store the rest in Options
if val, exists := raw["id"]; exists {
o.Id = val.(uint)
delete(raw, "id")
if val, exists := raw["id"].(float64); exists {
o.Id = uint(val)
}
delete(raw, "id")
o.Type, _ = raw["type"].(string)
delete(raw, "type")
o.Tag = raw["tag"].(string)
+8 -8
View File
@@ -7,8 +7,8 @@ require (
github.com/gin-gonic/gin v1.10.0
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/robfig/cron/v3 v3.0.1
github.com/sagernet/sing v0.6.0-beta.8
github.com/sagernet/sing-box v1.11.0-beta.11
github.com/sagernet/sing v0.6.0-beta.9
github.com/sagernet/sing-box v1.11.0-beta.19
github.com/sagernet/sing-dns v0.4.0-beta.1
github.com/shirou/gopsutil/v3 v3.24.5
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
@@ -88,11 +88,11 @@ require (
github.com/sagernet/quic-go v0.48.2-beta.1 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-mux v0.3.0-alpha.1 // indirect
github.com/sagernet/sing-quic v0.4.0-alpha.4 // indirect
github.com/sagernet/sing-quic v0.4.0-beta.2 // indirect
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 // indirect
github.com/sagernet/sing-tun v0.6.0-beta.6 // indirect
github.com/sagernet/sing-tun v0.6.0-beta.7.0.20241229131914-aa9d9c62966f // indirect
github.com/sagernet/sing-vmess v0.2.0-beta.1 // indirect
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/utls v1.6.7 // indirect
@@ -110,13 +110,13 @@ require (
go.uber.org/zap v1.27.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
+21
View File
@@ -174,16 +174,24 @@ github.com/sagernet/sing v0.6.0-beta.5 h1:RD2j8WmJsvAbbBkAlJWaiYmnd+v/JohBiweoew
github.com/sagernet/sing v0.6.0-beta.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.6.0-beta.8 h1:PoxDdN7y8D4oImT3cQ05Sq1ZYnYsJberkUkIEHIGwWE=
github.com/sagernet/sing v0.6.0-beta.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.6.0-beta.9 h1:P8lKa5hN53fRNAVCIKy5cWd6/kLO5c4slhdsfehSmHs=
github.com/sagernet/sing v0.6.0-beta.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-box v1.11.0-beta.6 h1:MPdL2Yem/xM0RhejCO7krYvl1Zbd1zkSjKluKpHnHPQ=
github.com/sagernet/sing-box v1.11.0-beta.6/go.mod h1:6dO5V0A37cLlhvKnxCmZinSpZXz7ZSk11x3rgI+xH1I=
github.com/sagernet/sing-box v1.11.0-beta.11 h1:bVR0n3oQ3hGcuc/CSS7axsOeRNCRlCGkYVOKl0wxbsw=
github.com/sagernet/sing-box v1.11.0-beta.11/go.mod h1:GZnZUzUHZ6Bgm7D/i8unNORv3537u1s03tLXFdxCRpg=
github.com/sagernet/sing-box v1.11.0-beta.15 h1:oWcs/PHgKaeWKbTfgz/020KEVvDqQv/tQWe7zpyktkc=
github.com/sagernet/sing-box v1.11.0-beta.15/go.mod h1:+QZDsF4HkdiGcMfz+JNOfONLh9CnZjIwJJQNWEzhiaQ=
github.com/sagernet/sing-box v1.11.0-beta.19 h1:uL2xlXpz4t7BduLbXiLe5QqpyiMhvNNRThBzhTJ4p00=
github.com/sagernet/sing-box v1.11.0-beta.19/go.mod h1:UXUN/lwRT9mAM8PK7upPOwgqooOV2vU+CcjBfwT1rYg=
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
github.com/sagernet/sing-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
github.com/sagernet/sing-quic v0.4.0-alpha.4/go.mod h1:h5RkKTmUhudJKzK7c87FPXD5w1bJjVyxMN9+opZcctA=
github.com/sagernet/sing-quic v0.4.0-beta.2 h1:ikoQ7zTR0o/2rlI5H5FeNC0j5bQJJHb1uoyXFRu3yGk=
github.com/sagernet/sing-quic v0.4.0-beta.2/go.mod h1:1UNObFodd8CnS3aCT53x9cigjPSCl3P//8dfBMCwBDM=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
@@ -194,6 +202,10 @@ github.com/sagernet/sing-tun v0.6.0-beta.2 h1:GK7r2jWKm7RhlJGTq4QadgFcebQia1c3BO
github.com/sagernet/sing-tun v0.6.0-beta.2/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-tun v0.6.0-beta.6 h1:xaIHoH78MqTSvZqQ4SQto8pC1A+X4qXReDRNaC8DQeI=
github.com/sagernet/sing-tun v0.6.0-beta.6/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-tun v0.6.0-beta.7 h1:FCSX8oGBqb0H57AAvfGeeH/jMGYWCOg6XWkN/oeES+0=
github.com/sagernet/sing-tun v0.6.0-beta.7/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-tun v0.6.0-beta.7.0.20241229131914-aa9d9c62966f h1:dTnXP0e3LbSa4EpUmuOGhllanKPei4vPKfzlLvk76Pc=
github.com/sagernet/sing-tun v0.6.0-beta.7.0.20241229131914-aa9d9c62966f/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4=
github.com/sagernet/sing-vmess v0.2.0-beta.1/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
@@ -254,6 +266,8 @@ golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
@@ -264,6 +278,8 @@ golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -274,13 +290,18 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+1 -1
View File
@@ -26,7 +26,7 @@ func InitLogger(level logging.Level) {
backend, err = logging.NewSyslogBackend("")
if err != nil {
println("Unable to use syslog: " + err.Error())
fmt.Println("Unable to use syslog: " + err.Error())
backend = logging.NewLogBackend(os.Stderr, "", 0)
}
if config.IsSystemd() && err != nil {
+109 -23
View File
@@ -11,6 +11,7 @@ import (
)
type ClientService struct {
InboundService
}
func (s *ClientService) GetAll() ([]model.Client, error) {
@@ -23,48 +24,117 @@ func (s *ClientService) GetAll() ([]model.Client, error) {
return clients, nil
}
func (s *ClientService) Save(tx *gorm.DB, changes []model.Changes) error {
func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage) ([]uint, error) {
var err error
for _, change := range changes {
client := model.Client{}
err = json.Unmarshal(change.Obj, &client)
var inboundIds []uint
switch act {
case "new", "edit":
var client model.Client
err = json.Unmarshal(data, &client)
if err != nil {
return nil, err
}
err = json.Unmarshal(client.Inbounds, &inboundIds)
if err != nil {
return nil, err
}
err = tx.Save(&client).Error
if err != nil {
return nil, err
}
case "del":
var id uint
err = json.Unmarshal(data, &id)
if err != nil {
return nil, err
}
var client model.Client
err = tx.Where("id = ?", id).First(&client).Error
if err != nil {
return nil, err
}
err = json.Unmarshal(client.Inbounds, &inboundIds)
if err != nil {
return nil, err
}
err = tx.Where("id = ?", id).Delete(model.Client{}).Error
if err != nil {
return nil, err
}
}
return inboundIds, nil
}
func (s *ClientService) UpdateLinks(tx *gorm.DB, links json.RawMessage) error {
var userLinks []interface{}
err := json.Unmarshal(links, &userLinks)
if err != nil {
return err
}
for _, userLink := range userLinks {
userLinkData, _ := userLink.(map[string]interface{})
userId, _ := userLinkData["id"].(float64)
links, err := json.MarshalIndent(userLinkData["links"], "", " ")
if err != nil {
return err
}
switch change.Action {
case "new":
err = tx.Create(&client).Error
case "del":
err = tx.Where("id = ?", change.Index).Delete(model.Client{}).Error
default:
err = tx.Save(client).Error
if inbounds, ok := userLinkData["inbounds"]; ok {
inbounds, err := json.MarshalIndent(inbounds, "", " ")
if err != nil {
return err
}
err = tx.Model(model.Client{}).Where("id = ?", uint(userId)).Update("inbounds", inbounds).Error
if err != nil {
return err
}
}
err = tx.Model(model.Client{}).Where("id = ?", uint(userId)).Update("links", links).Error
if err != nil {
return err
}
}
return err
return nil
}
func (s *ClientService) DepleteClients() ([]string, []string, error) {
func (s *ClientService) DepleteClients() error {
var err error
var clients []model.Client
var changes []model.Changes
var users []string
var inboundIds []uint
now := time.Now().Unix()
db := database.GetDB()
err = db.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Scan(&clients).Error
tx := db.Begin()
defer func() {
if err == nil {
tx.Commit()
if len(inboundIds) > 0 && corePtr.IsRunning() {
err1 := s.InboundService.RestartInbounds(tx, inboundIds)
if err1 != nil {
logger.Error("unable to restart inbounds: ", err1)
}
}
} else {
tx.Rollback()
}
}()
err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Scan(&clients).Error
if err != nil {
return nil, nil, err
return err
}
dt := time.Now().Unix()
var users, inbounds []string
for _, client := range clients {
logger.Debug("Client ", client.Name, " is going to be disabled")
users = append(users, client.Name)
var userInbounds []string
var userInbounds []uint
json.Unmarshal(client.Inbounds, &userInbounds)
inbounds = append(inbounds, userInbounds...)
inboundIds = s.uniqueAppendInboundIds(inboundIds, userInbounds)
changes = append(changes, model.Changes{
DateTime: dt,
Actor: "DepleteJob",
@@ -76,16 +146,32 @@ func (s *ClientService) DepleteClients() ([]string, []string, error) {
// Save changes
if len(changes) > 0 {
err = db.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Update("enable", false).Error
err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Update("enable", false).Error
if err != nil {
return nil, nil, err
return err
}
err = db.Model(model.Changes{}).Create(&changes).Error
err = tx.Model(model.Changes{}).Create(&changes).Error
if err != nil {
return nil, nil, err
return err
}
LastUpdate = dt
}
return users, inbounds, nil
return nil
}
// avoid duplicate inboundIds
func (s *ClientService) uniqueAppendInboundIds(a []uint, b []uint) []uint {
m := make(map[uint]bool)
for _, v := range a {
m[v] = true
}
for _, v := range b {
m[v] = true
}
var res []uint
for k := range m {
res = append(res, k)
}
return res
}
+72 -297
View File
@@ -2,12 +2,12 @@ package service
import (
"encoding/json"
"os"
"s-ui/config"
"s-ui/core"
"s-ui/database"
"s-ui/database/model"
"s-ui/logger"
"s-ui/util/common"
"strconv"
"time"
)
@@ -48,10 +48,13 @@ func (s *ConfigService) InitConfig() error {
return nil
}
func (s *ConfigService) GetConfig() (*SingBoxConfig, error) {
data, err := s.SettingService.GetConfig()
if err != nil {
return nil, err
func (s *ConfigService) GetConfig(data string) (*SingBoxConfig, error) {
var err error
if len(data) == 0 {
data, err = s.SettingService.GetConfig()
if err != nil {
return nil, err
}
}
singboxConfig := SingBoxConfig{}
err = json.Unmarshal([]byte(data), &singboxConfig)
@@ -74,8 +77,11 @@ func (s *ConfigService) GetConfig() (*SingBoxConfig, error) {
return &singboxConfig, nil
}
func (s *ConfigService) StartCore() error {
singboxConfig, err := s.GetConfig()
func (s *ConfigService) StartCore(defaultConfig string) error {
if corePtr.IsRunning() {
return nil
}
singboxConfig, err := s.GetConfig(defaultConfig)
if err != nil {
return err
}
@@ -93,11 +99,19 @@ func (s *ConfigService) StartCore() error {
}
func (s *ConfigService) RestartCore() error {
err := s.StartCore()
err := s.StopCore()
if err != nil {
return err
}
return s.StartCore()
return s.StartCore("")
}
func (s *ConfigService) restartCoreWithConfig(config json.RawMessage) error {
err := s.StopCore()
if err != nil {
return err
}
return s.StartCore(string(config))
}
func (s *ConfigService) StopCore() error {
@@ -109,220 +123,80 @@ func (s *ConfigService) StopCore() error {
return nil
}
func (s *ConfigService) SaveChanges(changes map[string]string, loginUser string) error {
func (s *ConfigService) Save(obj string, act string, data json.RawMessage, userLinks json.RawMessage, outJsons json.RawMessage, loginUser string) error {
var err error
var clientChanges, tlsChanges, inChanges, settingChanges, configChanges []model.Changes
if _, ok := changes["clients"]; ok {
err = json.Unmarshal([]byte(changes["clients"]), &clientChanges)
if err != nil {
return err
}
}
if _, ok := changes["tls"]; ok {
err = json.Unmarshal([]byte(changes["tls"]), &tlsChanges)
if err != nil {
return err
}
}
if _, ok := changes["inData"]; ok {
err = json.Unmarshal([]byte(changes["inData"]), &inChanges)
if err != nil {
return err
}
}
if _, ok := changes["settings"]; ok {
err = json.Unmarshal([]byte(changes["settings"]), &settingChanges)
if err != nil {
return err
}
}
if _, ok := changes["config"]; ok {
err = json.Unmarshal([]byte(changes["config"]), &configChanges)
if err != nil {
return err
}
}
var inboundIds []uint
db := database.GetDB()
tx := db.Begin()
defer func() {
if err == nil {
tx.Commit()
if len(inboundIds) > 0 && corePtr.IsRunning() {
err1 := s.InboundService.RestartInbounds(tx, inboundIds)
if err1 != nil {
logger.Error("unable to restart inbounds: ", err1)
}
}
// Try to start core if it is not running
if !corePtr.IsRunning() {
s.StartCore("")
}
} else {
tx.Rollback()
}
}()
if len(clientChanges) > 0 {
err = s.ClientService.Save(tx, clientChanges)
switch obj {
case "clients":
inboundIds, err = s.ClientService.Save(tx, act, data)
case "tls":
inboundIds, err = s.TlsService.Save(tx, act, data)
case "inbounds":
err = s.InboundService.Save(tx, act, data)
case "outbounds":
err = s.OutboundService.Save(tx, act, data)
case "endpoints":
err = s.EndpointService.Save(tx, act, data)
case "config":
err = s.SettingService.SaveConfig(tx, data)
if err != nil {
return err
}
err = s.restartCoreWithConfig(data)
default:
return common.NewError("unknown object: ", obj)
}
if len(tlsChanges) > 0 {
err = s.TlsService.Save(tx, tlsChanges)
if err != nil {
return err
}
if err != nil {
return err
}
// if len(inChanges) > 0 {
// err = s.InDataService.Save(tx, inChanges)
// if err != nil {
// return err
// }
// }
if len(settingChanges) > 0 {
err = s.SettingService.Save(tx, settingChanges)
if err != nil {
return err
}
}
needRestart := false
if len(configChanges) > 0 {
singboxConfig, err := s.GetConfig()
if err != nil {
return err
}
newConfig := *singboxConfig
for _, change := range configChanges {
rawObject := change.Obj
switch change.Key {
case "all":
err = json.Unmarshal(rawObject, &newConfig)
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, needRestart)
if len(userLinks) > 0 {
err = s.ClientService.UpdateLinks(tx, userLinks)
if err != nil {
return err
}
}
if len(outJsons) > 0 {
err = s.InboundService.UpdateOutJsons(tx, outJsons)
if err != nil {
return err
}
}
// Log changes
dt := time.Now().Unix()
allChanges := append(clientChanges, settingChanges...)
allChanges = append(allChanges, configChanges...)
allChanges = append(allChanges, tlsChanges...)
allChanges = append(allChanges, inChanges...)
if len(allChanges) > 0 {
for index := range allChanges {
allChanges[index].DateTime = dt
allChanges[index].Actor = loginUser
}
err = tx.Model(model.Changes{}).Create(&allChanges).Error
if err != nil {
return err
}
err = tx.Create(&model.Changes{
DateTime: dt,
Actor: loginUser,
Key: obj,
Action: act,
Obj: data,
}).Error
if err != nil {
return err
}
LastUpdate = dt
LastUpdate = time.Now().Unix()
return nil
}
@@ -345,105 +219,6 @@ func (s *ConfigService) CheckChanges(lu string) (bool, error) {
}
}
func (s *ConfigService) Save(singboxConfig *SingBoxConfig, needRestart bool) error {
configPath := config.GetBinFolderPath()
_, err := os.Stat(configPath + "/config.json")
if os.IsNotExist(err) {
err = os.MkdirAll(configPath, 01764)
if err != nil {
return err
}
} else if err != nil {
return err
}
data, err := json.MarshalIndent(singboxConfig, "", " ")
if err != nil {
return err
}
err = os.WriteFile(configPath+"/config.json", data, 0764)
if err != nil {
return err
}
if needRestart {
err = s.RestartCore()
if err != nil {
return err
}
}
// s.Controller.Restart()
return nil
}
func (s *ConfigService) DepleteClients() error {
users, inboundIds, err := s.ClientService.DepleteClients()
if err != nil || len(users) == 0 || len(inboundIds) == 0 {
return err
}
// inbounds, err := s.InboundService.FromIds(inboundIds)
// if err != nil {
// return err
// }
// for inbound_index, inbound := range inbounds {
// var inboundJson map[string]interface{}
// json.Unmarshal(inbound.Options, &inboundJson)
// inbound_users, ok := inboundJson["users"].([]interface{})
// if ok {
// var updatedUsers []interface{}
// for _, user := range inbound_users {
// userMap, ok := user.(map[string]interface{})
// if ok {
// name, exists := userMap["name"].(string)
// if exists && s.contains(users, name) {
// // Skip the user exists
// continue
// }
// username, exists := userMap["username"].(string)
// if exists && s.contains(users, username) {
// // Skip the username exists
// continue
// }
// }
// updatedUsers = append(updatedUsers, user)
// }
// // Exception for Naive and ShadowTLSv3
// if len(updatedUsers) == 0 {
// if inboundJson["type"].(string) == "naive" ||
// (inboundJson["type"].(string) == "shadowtls" &&
// inboundJson["version"].(float64) == 3) {
// updatedUsers = append(updatedUsers, make(map[string]interface{}))
// }
// }
// inboundJson["users"] = updatedUsers
// }
// modifiedInbound, err := json.MarshalIndent(inboundJson, "", " ")
// if err != nil {
// return err
// }
// inbounds[inbound_index] = modifiedInbound
// }
// err = s.Save(singboxConfig, true)
// if err != nil {
// return err
// }
return nil
}
func (s *ConfigService) contains(slice []string, item string) bool {
for _, str := range slice {
if str == item {
return true
}
}
return false
}
func (s *ConfigService) GetChanges(actor string, chngKey string, count string) []model.Changes {
c, _ := strconv.Atoi(count)
whereString := "`id`>0"
+72 -11
View File
@@ -2,6 +2,7 @@ package service
import (
"encoding/json"
"os"
"s-ui/database"
"s-ui/database/model"
@@ -10,24 +11,32 @@ import (
type EndpointService struct{}
func (o *EndpointService) GetAll() ([]*model.Endpoint, error) {
func (o *EndpointService) GetAll() (*[]map[string]interface{}, error) {
db := database.GetDB()
endpoints := []*model.Endpoint{}
err := db.Model(model.Endpoint{}).Scan(&endpoints).Error
if err != nil {
return nil, err
}
return endpoints, nil
}
func (o *EndpointService) Get(id uint) (*model.Endpoint, error) {
db := database.GetDB()
endpoint := &model.Endpoint{}
err := db.First(endpoint, id).Error
if err != nil {
return nil, err
var data []map[string]interface{}
for _, endpoint := range endpoints {
epData := map[string]interface{}{
"id": endpoint.Id,
"type": endpoint.Type,
"tag": endpoint.Tag,
}
if endpoint.Options != nil {
var restFields map[string]json.RawMessage
if err := json.Unmarshal(endpoint.Options, &restFields); err != nil {
return nil, err
}
for k, v := range restFields {
epData[k] = v
}
}
data = append(data, epData)
}
return endpoint, nil
return &data, nil
}
func (o *EndpointService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) {
@@ -46,3 +55,55 @@ func (o *EndpointService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) {
}
return endpointsJson, nil
}
func (s *EndpointService) Save(tx *gorm.DB, action string, data json.RawMessage) error {
var err error
switch action {
case "new", "edit":
var endpoint model.Endpoint
err = endpoint.UnmarshalJSON(data)
if err != nil {
return err
}
if corePtr.IsRunning() {
configData, err := endpoint.MarshalJSON()
if err != nil {
return err
}
if action == "edit" {
err = corePtr.RemoveEndpoint(endpoint.Tag)
if err != nil && err != os.ErrInvalid {
return err
}
}
err = corePtr.AddEndpoint(configData)
if err != nil {
return err
}
}
err = tx.Save(&endpoint).Error
if err != nil {
return err
}
case "del":
var tag string
err = json.Unmarshal(data, &tag)
if err != nil {
return err
}
if corePtr.IsRunning() {
err = corePtr.RemoveEndpoint(tag)
if err != nil && err != os.ErrInvalid {
return err
}
}
err = tx.Where("tag = ?", tag).Delete(model.Endpoint{}).Error
if err != nil {
return err
}
}
return nil
}
+174 -12
View File
@@ -2,22 +2,67 @@ package service
import (
"encoding/json"
"os"
"s-ui/database"
"s-ui/database/model"
"strings"
"gorm.io/gorm"
)
type InboundService struct{}
func (s *InboundService) GetAll() (*[]map[string]interface{}, error) {
func (s *InboundService) Get(ids string) (*[]map[string]interface{}, error) {
if ids == "" {
return s.GetAll()
}
return s.getById(ids)
}
func (s *InboundService) getById(ids string) (*[]map[string]interface{}, error) {
var inbound []model.Inbound
var result []map[string]interface{}
db := database.GetDB()
inbounds := []map[string]interface{}{}
err := db.Model(model.Inbound{}).Select("id, tag, type, address, port, tls_id , count(users) as ucount").Scan(&inbounds).Error
err := db.Model(model.Inbound{}).Where("id in ?", strings.Split(ids, ",")).Scan(&inbound).Error
if err != nil {
return nil, err
}
return &inbounds, nil
for _, inb := range inbound {
inbData, err := inb.MarshalFull()
if err != nil {
return nil, err
}
result = append(result, *inbData)
}
return &result, nil
}
func (s *InboundService) GetAll() (*[]map[string]interface{}, error) {
db := database.GetDB()
inbounds := []model.Inbound{}
err := db.Model(model.Inbound{}).Scan(&inbounds).Error
if err != nil {
return nil, err
}
var data []map[string]interface{}
for _, inbound := range inbounds {
inbData := map[string]interface{}{
"id": inbound.Id,
"type": inbound.Type,
"tag": inbound.Tag,
"tls_id": inbound.TlsId,
}
if inbound.Options != nil {
var restFields map[string]json.RawMessage
if err := json.Unmarshal(inbound.Options, &restFields); err != nil {
return nil, err
}
inbData["listen"] = restFields["listen"]
inbData["listen_port"] = restFields["listen_port"]
}
data = append(data, inbData)
}
return &data, nil
}
func (s *InboundService) FromIds(ids []uint) ([]*model.Inbound, error) {
@@ -30,8 +75,91 @@ func (s *InboundService) FromIds(ids []uint) ([]*model.Inbound, error) {
return inbounds, nil
}
func (s *InboundService) Save(db *gorm.DB, inbounds []*model.Inbound) error {
return db.Save(inbounds).Error
func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage) error {
var err error
switch act {
case "new", "edit":
var inbound model.Inbound
err = inbound.UnmarshalJSON(data)
if err != nil {
return err
}
if corePtr.IsRunning() {
if act == "edit" {
err = corePtr.RemoveInbound(inbound.Tag)
if err != nil && err != os.ErrInvalid {
return err
}
}
if inbound.TlsId > 0 {
err = tx.Model(model.Tls{}).Where("id = ?", inbound.TlsId).Find(&inbound.Tls).Error
if err != nil {
return err
}
}
inboundConfig, err := inbound.MarshalJSON()
if err != nil {
return err
}
inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type)
if err != nil {
return err
}
err = corePtr.AddInbound(inboundConfig)
if err != nil {
return err
}
}
err = tx.Save(&inbound).Error
if err != nil {
return err
}
case "del":
var tag string
err = json.Unmarshal(data, &tag)
if err != nil {
return err
}
if corePtr.IsRunning() {
err = corePtr.RemoveInbound(tag)
if err != nil && err != os.ErrInvalid {
return err
}
}
err = tx.Where("tag = ?", tag).Delete(model.Inbound{}).Error
if err != nil {
return err
}
}
return nil
}
func (s *InboundService) UpdateOutJsons(tx *gorm.DB, data json.RawMessage) error {
var outJsons []interface{}
err := json.Unmarshal(data, &outJsons)
if err != nil {
return err
}
for _, outJson := range outJsons {
outJsonData := outJson.(map[string]interface{})
tag := outJsonData["tag"].(string)
outJson, err := json.MarshalIndent(outJsonData["out_json"], "", " ")
if err != nil {
return err
}
err = tx.Model(model.Inbound{}).Where("tag = ?", tag).Update("out_json", outJson).Error
if err != nil {
return err
}
}
return nil
}
func (s *InboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) {
@@ -46,12 +174,9 @@ func (s *InboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) {
if err != nil {
return nil, err
}
switch inbound.Type {
case "mixed", "socks", "http", "shadowsocks", "vmess", "trojan", "naive", "hysteria", "shadowtls", "tuic", "hysteria2", "vless":
inboundJson, err = s.addUsers(db, inboundJson, inbound.Id, inbound.Type)
if err != nil {
return nil, err
}
inboundJson, err = s.addUsers(db, inboundJson, inbound.Id, inbound.Type)
if err != nil {
return nil, err
}
inboundsJson = append(inboundsJson, inboundJson)
}
@@ -59,11 +184,24 @@ func (s *InboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) {
}
func (s *InboundService) addUsers(db *gorm.DB, inboundJson []byte, inboundId uint, inboundType string) ([]byte, error) {
switch inboundType {
case "mixed", "socks", "http", "shadowsocks", "vmess", "trojan", "naive", "hysteria", "shadowtls", "tuic", "hysteria2", "vless":
break
default:
return inboundJson, nil
}
var inbound map[string]interface{}
err := json.Unmarshal(inboundJson, &inbound)
if err != nil {
return nil, err
}
if inboundType == "shadowsocks" {
method, _ := inbound["method"].(string)
if method == "2022-blake3-aes-128-gcm" {
inboundType = "shadowsocks16"
}
}
var users []string
err = db.Raw(`SELECT json_extract(clients.config, ?)
FROM clients, json_each(clients.inbounds) as je
@@ -80,3 +218,27 @@ func (s *InboundService) addUsers(db *gorm.DB, inboundJson []byte, inboundId uin
inbound["users"] = usersJson
return json.Marshal(inbound)
}
func (s *InboundService) RestartInbounds(tx *gorm.DB, ids []uint) error {
var inbounds []*model.Inbound
err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ?", ids).Find(&inbounds).Error
if err != nil {
return err
}
for _, inbound := range inbounds {
err = corePtr.RemoveInbound(inbound.Tag)
if err != nil && err != os.ErrInvalid {
return err
}
inboundConfig, err := inbound.MarshalJSON()
if err != nil {
return err
}
inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type)
err = corePtr.AddInbound(inboundConfig)
if err != nil {
return err
}
}
return nil
}
+72 -11
View File
@@ -2,6 +2,7 @@ package service
import (
"encoding/json"
"os"
"s-ui/database"
"s-ui/database/model"
@@ -10,24 +11,32 @@ import (
type OutboundService struct{}
func (o *OutboundService) GetAll() ([]*model.Outbound, error) {
func (o *OutboundService) GetAll() (*[]map[string]interface{}, error) {
db := database.GetDB()
outbounds := []*model.Outbound{}
err := db.Model(model.Outbound{}).Scan(&outbounds).Error
if err != nil {
return nil, err
}
return outbounds, nil
}
func (o *OutboundService) Get(id uint) (*model.Outbound, error) {
db := database.GetDB()
outbound := &model.Outbound{}
err := db.First(outbound, id).Error
if err != nil {
return nil, err
var data []map[string]interface{}
for _, outbound := range outbounds {
outData := map[string]interface{}{
"id": outbound.Id,
"type": outbound.Type,
"tag": outbound.Tag,
}
if outbound.Options != nil {
var restFields map[string]json.RawMessage
if err := json.Unmarshal(outbound.Options, &restFields); err != nil {
return nil, err
}
for k, v := range restFields {
outData[k] = v
}
}
data = append(data, outData)
}
return outbound, nil
return &data, nil
}
func (o *OutboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) {
@@ -46,3 +55,55 @@ func (o *OutboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) {
}
return outboundsJson, nil
}
func (s *OutboundService) Save(tx *gorm.DB, action string, data json.RawMessage) error {
var err error
switch action {
case "new", "edit":
var outbound model.Outbound
err = outbound.UnmarshalJSON(data)
if err != nil {
return err
}
if corePtr.IsRunning() {
configData, err := outbound.MarshalJSON()
if err != nil {
return err
}
if action == "edit" {
err = corePtr.RemoveOutbound(outbound.Tag)
if err != nil && err != os.ErrInvalid {
return err
}
}
err = corePtr.AddOutbound(configData)
if err != nil {
return err
}
}
err = tx.Save(&outbound).Error
if err != nil {
return err
}
case "del":
var tag string
err = json.Unmarshal(data, &tag)
if err != nil {
return err
}
if corePtr.IsRunning() {
err = corePtr.RemoveOutbound(tag)
if err != nil && err != os.ErrInvalid {
return err
}
}
err = tx.Where("tag = ?", tag).Delete(model.Outbound{}).Error
if err != nil {
return err
}
}
return nil
}
+8
View File
@@ -343,6 +343,14 @@ func (s *SettingService) SetConfig(config string) error {
return s.setString("config", config)
}
func (s *SettingService) SaveConfig(tx *gorm.DB, config json.RawMessage) error {
configs, err := json.MarshalIndent(config, "", " ")
if err != nil {
return err
}
return tx.Model(model.Setting{}).Where("key = ?", "config").Update("value", string(configs)).Error
}
func (s *SettingService) Save(tx *gorm.DB, changes []model.Changes) error {
var err error
for _, change := range changes {
+6 -2
View File
@@ -74,7 +74,7 @@ func (s *StatsService) SaveStats() error {
return err
}
func (s *StatsService) GetStats(resorce string, tag string, limit int) ([]model.Stats, error) {
func (s *StatsService) GetStats(resource string, tag string, limit int) ([]model.Stats, error) {
var err error
var result []model.Stats
@@ -82,7 +82,11 @@ func (s *StatsService) GetStats(resorce string, tag string, limit int) ([]model.
timeDiff := currentTime - (int64(limit) * 3600)
db := database.GetDB()
err = db.Model(model.Stats{}).Where("resource = ? AND tag = ? AND date_time > ?", resorce, tag, timeDiff).Scan(&result).Error
resources := []string{resource}
if resource == "endpoint" {
resources = []string{"inbound", "outbound"}
}
err = db.Model(model.Stats{}).Where("resource in ? AND tag = ? AND date_time > ?", resources, tag, timeDiff).Scan(&result).Error
if err != nil {
return nil, err
}
+37 -15
View File
@@ -4,11 +4,13 @@ import (
"encoding/json"
"s-ui/database"
"s-ui/database/model"
"s-ui/util/common"
"gorm.io/gorm"
)
type TlsService struct {
InboundService
}
func (s *TlsService) GetAll() ([]model.Tls, error) {
@@ -22,25 +24,45 @@ func (s *TlsService) GetAll() ([]model.Tls, error) {
return tlsConfig, nil
}
func (s *TlsService) Save(tx *gorm.DB, changes []model.Changes) error {
func (s *TlsService) Save(tx *gorm.DB, action string, data json.RawMessage) ([]uint, error) {
var err error
for _, change := range changes {
tlsConfig := model.Tls{}
err = json.Unmarshal(change.Obj, &tlsConfig)
var inboundIds []uint
switch action {
case "new", "edit":
var tls model.Tls
err = json.Unmarshal(data, &tls)
if err != nil {
return err
}
switch change.Action {
case "new":
err = tx.Create(&tlsConfig).Error
case "del":
err = tx.Where("id = ?", change.Index).Delete(model.Tls{}).Error
default:
err = tx.Save(tlsConfig).Error
return nil, err
}
err = tx.Save(&tls).Error
if err != nil {
return err
return nil, err
}
err = tx.Model(model.Inbound{}).Select("id").Where("tls_id = ?", tls.Id).Scan(&inboundIds).Error
if err != nil {
return nil, err
}
return inboundIds, nil
case "del":
var id uint
err = json.Unmarshal(data, &id)
if err != nil {
return nil, err
}
var inboundCount int64
err = tx.Model(model.Inbound{}).Where("tls_id = ?", id).Count(&inboundCount).Error
if err != nil {
return nil, err
}
if inboundCount > 0 {
return nil, common.NewError("tls in use")
}
err = tx.Where("id = ?", id).Delete(model.Tls{}).Error
if err != nil {
return nil, err
}
}
return err
return nil, nil
}
+6 -11
View File
@@ -87,27 +87,22 @@ func (j *JsonService) GetJson(subId string, format string) (*string, error) {
return &resultStr, nil
}
func (j *JsonService) getData(subId string) (*model.Client, *[]model.InboundData, error) {
func (j *JsonService) getData(subId string) (*model.Client, []*model.Inbound, error) {
db := database.GetDB()
client := &model.Client{}
err := db.Model(model.Client{}).Where("enable = true and name = ?", subId).First(client).Error
if err != nil {
return nil, nil, err
}
var inbounds []string
err = json.Unmarshal(client.Inbounds, &inbounds)
var inbounds []*model.Inbound
err = db.Model(model.Inbound{}).Where("tag in ?", client.Inbounds).Find(&inbounds).Error
if err != nil {
return nil, nil, err
}
inDatas := &[]model.InboundData{}
err = db.Model(model.InboundData{}).Where("tag in ?", inbounds).Find(&inDatas).Error
if err != nil {
return nil, nil, err
}
return client, inDatas, nil
return client, inbounds, nil
}
func (j *JsonService) getOutbounds(clientConfig json.RawMessage, inDatas *[]model.InboundData) (*[]map[string]interface{}, *[]string, error) {
func (j *JsonService) getOutbounds(clientConfig json.RawMessage, inbounds []*model.Inbound) (*[]map[string]interface{}, *[]string, error) {
var outbounds []map[string]interface{}
var configs map[string]interface{}
var outTags []string
@@ -116,7 +111,7 @@ func (j *JsonService) getOutbounds(clientConfig json.RawMessage, inDatas *[]mode
if err != nil {
return nil, nil, err
}
for _, inData := range *inDatas {
for _, inData := range inbounds {
if len(inData.OutJson) < 5 {
continue
}