diff --git a/backend/api/api.go b/backend/api/api.go
index a883947..5ba3b33 100644
--- a/backend/api/api.go
+++ b/backend/api/api.go
@@ -1,7 +1,6 @@
package api
import (
- "fmt"
"s-ui/logger"
"s-ui/service"
"strconv"
@@ -157,8 +156,8 @@ func (a *APIHandler) getHandler(c *gin.Context) {
}
}
-func (a *APIHandler) loadData(c *gin.Context) (string, error) {
- var data string
+func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) {
+ data := make(map[string]interface{}, 0)
lu := c.Query("lu")
isUpdated, err := a.ConfigService.CheckChanges(lu)
if err != nil {
@@ -185,9 +184,13 @@ func (a *APIHandler) loadData(c *gin.Context) (string, error) {
if err != nil {
return "", err
}
- data = fmt.Sprintf(`{"config": %s, "clients": %s, "tls": %s, "subURI": "%s", "onlines": %s}`, string(*config), clients, tlsConfigs, subURI, onlines)
+ data["config"] = *config
+ data["clients"] = clients
+ data["tls"] = tlsConfigs
+ data["subURI"] = subURI
+ data["onlines"] = onlines
} else {
- data = fmt.Sprintf(`{"onlines": %s}`, onlines)
+ data["onlines"] = onlines
}
return data, nil
diff --git a/backend/database/model/model.go b/backend/database/model/model.go
index 9f06842..eca10f9 100644
--- a/backend/database/model/model.go
+++ b/backend/database/model/model.go
@@ -24,17 +24,17 @@ type User struct {
}
type Client struct {
- Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
- Enable bool `json:"enable" form:"enable"`
- Name string `json:"name" form:"name"`
- Config string `json:"config" form:"config"`
- Inbounds string `json:"inbounds" form:"inbounds"`
- Links string `json:"links" form:"links"`
- Volume int64 `json:"volume" form:"volume"`
- Expiry int64 `json:"expiry" form:"expiry"`
- Down int64 `json:"down" form:"down"`
- Up int64 `json:"up" form:"up"`
- Desc string `json:"desc" from:"desc"`
+ Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
+ Enable bool `json:"enable" form:"enable"`
+ Name string `json:"name" form:"name"`
+ Config json.RawMessage `json:"config" form:"config"`
+ Inbounds json.RawMessage `json:"inbounds" form:"inbounds"`
+ Links json.RawMessage `json:"links" form:"links"`
+ Volume int64 `json:"volume" form:"volume"`
+ Expiry int64 `json:"expiry" form:"expiry"`
+ Down int64 `json:"down" form:"down"`
+ Up int64 `json:"up" form:"up"`
+ Desc string `json:"desc" from:"desc"`
}
type Stats struct {
diff --git a/backend/service/client.go b/backend/service/client.go
index 44558cc..ffa86d1 100644
--- a/backend/service/client.go
+++ b/backend/service/client.go
@@ -5,7 +5,6 @@ import (
"s-ui/database"
"s-ui/database/model"
"s-ui/logger"
- "strings"
"time"
"gorm.io/gorm"
@@ -14,18 +13,14 @@ import (
type ClientService struct {
}
-func (s *ClientService) GetAll() (string, error) {
+func (s *ClientService) GetAll() ([]model.Client, error) {
db := database.GetDB()
clients := []model.Client{}
err := db.Model(model.Client{}).Scan(&clients).Error
if err != nil {
- return "", err
+ return nil, err
}
- data, err := json.Marshal(clients)
- if err != nil {
- return "", err
- }
- return string(data), nil
+ return clients, nil
}
func (s *ClientService) Save(tx *gorm.DB, changes []model.Changes) error {
@@ -62,14 +57,16 @@ func (s *ClientService) DepleteClients() ([]string, []string, error) {
return nil, nil, 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)
- userInbounds := strings.Split(client.Inbounds, ",")
+ var userInbounds []string
+ json.Unmarshal(client.Inbounds, &userInbounds)
inbounds = append(inbounds, userInbounds...)
changes = append(changes, model.Changes{
- DateTime: time.Now().Unix(),
+ DateTime: dt,
Actor: "DepleteJob",
Key: "clients",
Action: "disable",
@@ -87,6 +84,7 @@ func (s *ClientService) DepleteClients() ([]string, []string, error) {
if err != nil {
return nil, nil, err
}
+ LastUpdate = dt
}
return users, inbounds, nil
diff --git a/backend/service/config.go b/backend/service/config.go
index d30c0cf..2927d4f 100644
--- a/backend/service/config.go
+++ b/backend/service/config.go
@@ -54,16 +54,27 @@ func (s *ConfigService) InitConfig() error {
return err
}
}
- return s.RefreshApiAddr(&data)
+ var singboxConfig SingBoxConfig
+ err = json.Unmarshal(data, &singboxConfig)
+ if err != nil {
+ return err
+ }
+
+ return s.RefreshApiAddr(&singboxConfig)
}
-func (s *ConfigService) GetConfig() (*[]byte, error) {
+func (s *ConfigService) GetConfig() (*SingBoxConfig, error) {
configPath := config.GetBinFolderPath()
data, err := os.ReadFile(configPath + "/config.json")
if err != nil {
return nil, err
}
- return &data, nil
+ singboxConfig := SingBoxConfig{}
+ err = json.Unmarshal(data, &singboxConfig)
+ if err != nil {
+ return nil, err
+ }
+ return &singboxConfig, nil
}
func (s *ConfigService) SaveChanges(changes map[string]string, loginUser string) error {
@@ -127,11 +138,7 @@ func (s *ConfigService) SaveChanges(changes map[string]string, loginUser string)
if err != nil {
return err
}
- newConfig := SingBoxConfig{}
- err = json.Unmarshal(*singboxConfig, &newConfig)
- if err != nil {
- return err
- }
+ newConfig := *singboxConfig
for _, change := range configChanges {
rawObject := change.Obj
switch change.Key {
@@ -169,12 +176,7 @@ func (s *ConfigService) SaveChanges(changes map[string]string, loginUser string)
}
}
- // Save to config.json
- data, err := json.MarshalIndent(newConfig, "", " ")
- if err != nil {
- return err
- }
- err = s.Save(&data)
+ err = s.Save(&newConfig)
if err != nil {
return err
}
@@ -215,7 +217,7 @@ func (s *ConfigService) CheckChanges(lu string) (bool, error) {
}
}
-func (s *ConfigService) Save(data *[]byte) error {
+func (s *ConfigService) Save(singboxConfig *SingBoxConfig) error {
configPath := config.GetBinFolderPath()
_, err := os.Stat(configPath + "/config.json")
if os.IsNotExist(err) {
@@ -227,35 +229,36 @@ func (s *ConfigService) Save(data *[]byte) error {
return err
}
- err = os.WriteFile(configPath+"/config.json", *data, 0764)
+ data, err := json.MarshalIndent(singboxConfig, " ", " ")
if err != nil {
return err
}
- s.RefreshApiAddr(data)
+ err = os.WriteFile(configPath+"/config.json", data, 0764)
+ if err != nil {
+ return err
+ }
+
+ s.RefreshApiAddr(singboxConfig)
s.Controller.Restart()
return nil
}
-func (s *ConfigService) RefreshApiAddr(data *[]byte) error {
+func (s *ConfigService) RefreshApiAddr(singboxConfig *SingBoxConfig) error {
Env_API := config.GetEnvApi()
if len(Env_API) > 0 {
ApiAddr = Env_API
} else {
var err error
- if data == nil {
- data, err = s.GetConfig()
+ if singboxConfig == nil {
+ singboxConfig, err = s.GetConfig()
if err != nil {
return err
}
+
}
- singboxConfig := SingBoxConfig{}
- err = json.Unmarshal(*data, &singboxConfig)
- if err != nil {
- return err
- }
var experimental struct {
V2rayApi struct {
Listen string `json:"listen"`
@@ -282,12 +285,7 @@ func (s *ConfigService) DepleteClients() error {
if err != nil {
return err
}
- newConfig := SingBoxConfig{}
- err = json.Unmarshal(*singboxConfig, &newConfig)
- if err != nil {
- return err
- }
- for inbound_index, inbound := range newConfig.Inbounds {
+ for inbound_index, inbound := range singboxConfig.Inbounds {
var inboundJson map[string]interface{}
json.Unmarshal(inbound, &inboundJson)
if s.contains(inbounds, inboundJson["tag"].(string)) {
@@ -326,13 +324,10 @@ func (s *ConfigService) DepleteClients() error {
if err != nil {
return err
}
- newConfig.Inbounds[inbound_index] = modifiedInbound
+ singboxConfig.Inbounds[inbound_index] = modifiedInbound
}
- modifiedConfig, err := json.MarshalIndent(newConfig, "", " ")
- if err != nil {
- return err
- }
- err = s.Save(&modifiedConfig)
+
+ err = s.Save(singboxConfig)
if err != nil {
return err
}
diff --git a/backend/service/stats.go b/backend/service/stats.go
index 7394321..fd7bf36 100644
--- a/backend/service/stats.go
+++ b/backend/service/stats.go
@@ -1,7 +1,6 @@
package service
import (
- "encoding/json"
"s-ui/database"
"s-ui/database/model"
"time"
@@ -86,12 +85,8 @@ func (s *StatsService) GetStats(resorce string, tag string, limit int) ([]model.
return result, nil
}
-func (s *StatsService) GetOnlines() (string, error) {
- onlines, err := json.Marshal(onlineResources)
- if err != nil {
- return "", err
- }
- return string(onlines), nil
+func (s *StatsService) GetOnlines() (onlines, error) {
+ return *onlineResources, nil
}
func (s *StatsService) DelOldStats(days int) error {
oldTime := time.Now().AddDate(0, 0, -(days)).Unix()
diff --git a/backend/service/tls.go b/backend/service/tls.go
index 578aef1..ada8cb2 100644
--- a/backend/service/tls.go
+++ b/backend/service/tls.go
@@ -11,18 +11,15 @@ import (
type TlsService struct {
}
-func (s *TlsService) GetAll() (string, error) {
+func (s *TlsService) GetAll() ([]model.Tls, error) {
db := database.GetDB()
tlsConfig := []model.Tls{}
err := db.Model(model.Tls{}).Scan(&tlsConfig).Error
if err != nil {
- return "", err
+ return nil, err
}
- data, err := json.Marshal(tlsConfig)
- if err != nil {
- return "", err
- }
- return string(data), nil
+
+ return tlsConfig, nil
}
func (s *TlsService) Save(tx *gorm.DB, changes []model.Changes) error {
diff --git a/frontend/src/layouts/modals/Client.vue b/frontend/src/layouts/modals/Client.vue
index 256d0f4..ba98039 100644
--- a/frontend/src/layouts/modals/Client.vue
+++ b/frontend/src/layouts/modals/Client.vue
@@ -179,7 +179,7 @@ export default {
const newData = JSON.parse(this.$props.data)
this.client = createClient(newData)
this.title = "edit"
- this.clientConfig = JSON.parse(this.client.config)
+ this.clientConfig = this.client.config
}
else {
this.client = createClient()
@@ -187,10 +187,9 @@ export default {
this.clientConfig = randomConfigs('client')
}
this.clientStats = this.$props.stats
- const allLinks = JSON.parse(this.client.links)
- this.links = allLinks.filter(l => l.type == 'local')
- this.extLinks = allLinks.filter(l => l.type == 'external')
- this.subLinks = allLinks.filter(l => l.type == 'sub')
+ this.links = this.client.links.filter(l => l.type == 'local')
+ this.extLinks = this.client.links.filter(l => l.type == 'external')
+ this.subLinks = this.client.links.filter(l => l.type == 'sub')
this.tab = "t1"
},
closeModal() {
@@ -199,11 +198,11 @@ export default {
},
saveChanges() {
this.loading = true
- this.client.config = updateConfigs(JSON.stringify(this.clientConfig), this.client.name)
- this.client.links = JSON.stringify([
- ...this.links,
- ...this.extLinks.filter(l => l.uri != ''),
- ...this.subLinks.filter(l => l.uri != '')])
+ this.client.config = updateConfigs(this.clientConfig, this.client.name)
+ this.client.links = [
+ ...this.links,
+ ...this.extLinks.filter(l => l.uri != ''),
+ ...this.subLinks.filter(l => l.uri != '')]
this.$emit('save', this.client, this.clientStats)
this.loading = false
},
@@ -213,8 +212,8 @@ export default {
},
computed: {
clientInbounds: {
- get() { return this.client.inbounds == "" ? [] : this.client.inbounds.split(',').filter(i => this.inboundTags.includes(i)) },
- set(newValue:string[]) { this.client.inbounds = newValue.length == 0 ? "" : newValue.join(',') }
+ get() { return this.client.inbounds.length>0 ? this.client.inbounds.filter(i => this.inboundTags.includes(i)) : [] },
+ set(newValue:string[]) { this.client.inbounds = newValue.length == 0 ? [] : newValue }
},
expDate: {
get() { return this.client.expiry},
diff --git a/frontend/src/store/modules/data.ts b/frontend/src/store/modules/data.ts
index 3a39b4e..b725dcb 100644
--- a/frontend/src/store/modules/data.ts
+++ b/frontend/src/store/modules/data.ts
@@ -20,7 +20,7 @@ const Data = defineStore('Data', {
this.lastLoad = Math.floor((new Date()).getTime()/1000)
// Set new data
- const data = JSON.parse(msg.obj)
+ const data = JSON.parse(JSON.stringify(msg.obj))
if (data.subURI) this.subURI = data.subURI
if (data.config) this.config = data.config
if (data.clients) this.clients = data.clients
@@ -28,9 +28,9 @@ const Data = defineStore('Data', {
this.onlines = data.onlines
// To avoid ref copy
- if (data.config) this.oldData.config = { ...JSON.parse(msg.obj).config }
- if (data.clients) this.oldData.clients = [ ...JSON.parse(msg.obj).clients ]
- if (data.tls) this.oldData.tlsConfigs = [ ...JSON.parse(msg.obj).tls ]
+ if (data.config) this.oldData.config = { ...msg.obj }.config
+ if (data.clients) this.oldData.clients = { ...msg.obj }.clients
+ if (data.tls) this.oldData.tlsConfigs = { ...msg.obj }.tls
}
},
async pushData() {
diff --git a/frontend/src/types/clients.ts b/frontend/src/types/clients.ts
index 946e681..02fca3e 100644
--- a/frontend/src/types/clients.ts
+++ b/frontend/src/types/clients.ts
@@ -1,12 +1,13 @@
+import { Link } from "@/plugins/link"
import RandomUtil from "@/plugins/randomUtil"
export interface Client {
id?: number
enable: boolean
name: string
- config: string
- inbounds: string
- links: string
+ config: Config
+ inbounds: string[]
+ links: Link[]
volume: number
expiry: number
up: number
@@ -17,9 +18,9 @@ export interface Client {
const defaultClient: Client = {
enable: true,
name: "",
- config: "[]",
- inbounds: "",
- links: "[]",
+ config: {},
+ inbounds: [],
+ links: [],
volume: 0,
expiry: 0,
up: 0,
@@ -35,12 +36,11 @@ type Config = {
}
}
-export function updateConfigs(configs: string, newUserName: string): string {
- const updatedConfigs: Config = JSON.parse(configs)
+export function updateConfigs(configs: Config, newUserName: string): Config {
- for (const key in updatedConfigs) {
- if (updatedConfigs.hasOwnProperty(key)) {
- const config = updatedConfigs[key]
+ for (const key in configs) {
+ if (configs.hasOwnProperty(key)) {
+ const config = configs[key]
if (config.hasOwnProperty("name")) {
config.name = newUserName
} else if (config.hasOwnProperty("username")) {
@@ -49,7 +49,7 @@ export function updateConfigs(configs: string, newUserName: string): string {
}
}
- return JSON.stringify(updatedConfigs)
+ return configs
}
export function randomConfigs(user: string): Config {
diff --git a/frontend/src/views/Clients.vue b/frontend/src/views/Clients.vue
index 5abd3ea..f687f0e 100644
--- a/frontend/src/views/Clients.vue
+++ b/frontend/src/views/Clients.vue
@@ -38,7 +38,7 @@
@@ -53,9 +53,9 @@
{{ $t('pages.inbounds') }}
- {{ i }}
+ {{ i }}
- {{ item.inbounds != '' ? item.inbounds.split(',').length : 0 }}
+ {{ item.inbounds.length }}
@@ -189,19 +189,18 @@ const saveModal = (data:any, stats:boolean) => {
sb.showMessage(i18n.global.t('error.dplData') + ': ' + i18n.global.t('client.name') ,'error', 5000)
return
}
- const inboundTags: string[] = data.inbounds.split(',')?? []
if(modal.value.index == -1) {
clients.value.push(data)
} else {
const oldData = createClient(clients.value[modal.value.index])
- oldData.inbounds.split(',').forEach((i:string) => {
- if (!inboundTags.includes(i)) inboundTags.push(i)
+ oldData.inbounds.forEach((i:string) => {
+ if (!data.inbounds.includes(i)) data.inbounds.push(i)
})
clients.value[modal.value.index] = data
}
// Rebuild affected inbounds
- buildInboundsUsers(inboundTags)
+ buildInboundsUsers(data.inbounds)
// Rebuild links
data.links = updateLinks(data)
@@ -229,15 +228,14 @@ const buildInboundsUsers = (inboundTags:string[]) => {
if (inbound_index != -1){
const users = []
const newInbound = inbounds.value[inbound_index]
- const inboundClients = clients.value.filter(c => c.enable && c.inbounds.split(',').includes(tag))
+ const inboundClients = clients.value.filter(c => c.enable && c.inbounds.includes(tag))
inboundClients.forEach(c => {
- const clientConfig = JSON.parse(c.config)
// Remove flow in non tls VLESS
if (newInbound.type == InTypes.VLESS) {
const vlessInbound = newInbound
- if (!vlessInbound.tls?.enabled || vlessInbound.transport?.type) delete(clientConfig["vless"].flow)
+ if (!vlessInbound.tls?.enabled || vlessInbound.transport?.type) delete(c.config?.vless?.flow)
}
- users.push(clientConfig[newInbound.type])
+ users.push(c.config[newInbound.type])
})
newInbound.users = users
@@ -257,8 +255,8 @@ const buildInboundsUsers = (inboundTags:string[]) => {
}
})
}
-const updateLinks = (c:Client):string => {
- const clientInbounds = inbounds.value.filter(i => c.inbounds.split(',').includes(i.tag))
+const updateLinks = (c:Client):Link[] => {
+ const clientInbounds = inbounds.value.filter(i => c.inbounds.includes(i.tag))
const newLinks = []
clientInbounds.forEach(i =>{
const tlsConfig = Data().tlsConfigs?.findLast((t:any) => t.inbounds.includes(i.tag))
@@ -267,10 +265,10 @@ const updateLinks = (c:Client):string => {
newLinks.push({ type: 'local', remark: i.tag, uri: uri })
}
})
- let links = c.links && c.links.length>0? JSON.parse(c.links) : []
+ let links = c.links && c.links.length>0? c.links : []
links = [...newLinks, ...links.filter(l => l.type != 'local')]
- return JSON.stringify(links)
+ return links
}
const delClient = (clientIndex: number) => {
const id = clients.value[clientIndex].id
@@ -284,7 +282,7 @@ const delClient = (clientIndex: number) => {
}
clients.value.splice(clientIndex,1)
- buildInboundsUsers(oldData.inbounds.split(','))
+ buildInboundsUsers(oldData.inbounds)
if (id>0) Data().delClient(id)
delOverlay.value[clientIndex] = false
}
diff --git a/frontend/src/views/Inbounds.vue b/frontend/src/views/Inbounds.vue
index 088770f..a0f5c40 100644
--- a/frontend/src/views/Inbounds.vue
+++ b/frontend/src/views/Inbounds.vue
@@ -222,7 +222,7 @@ const updateLinks = (i: InboundWithUser) => {
i.users.forEach((u:any) => {
const client = clients.value.find(c => u.username? c.name == u.username : c.name == u.name)
if (client){
- const clientInbounds = inbounds.value.filter(inb => client?.inbounds.split(',').includes(inb.tag))
+ const clientInbounds = inbounds.value.filter(inb => client?.inbounds.includes(inb.tag))
const newLinks = []
clientInbounds.forEach(i =>{
const tlsClient = tlsConfigs?.value.findLast((t:any) => t.inbounds.includes(i.tag))?.client?? null
@@ -231,10 +231,10 @@ const updateLinks = (i: InboundWithUser) => {
newLinks.push({ type: 'local', remark: i.tag, uri: uri })
}
})
- let links = client.links && client.links.length>0? JSON.parse(client.links) : []
+ let links = client.links && client.links.length>0? client.links : []
links = [...newLinks, ...links.filter(l => l.type != 'local')]
- client.links = JSON.stringify(links)
+ client.links = links
}
})
}
@@ -250,8 +250,8 @@ const delInbound = (index: number) => {
inbU.users.forEach((u:any) => {
const c_index = clients.value.findIndex(c => u.username? u.username == c.name : u.user == c.name)
if (c_index != -1) {
- const clientInbounds = clients.value[c_index].inbounds.split(',').filter((x:string) => x!=tag)
- clients.value[c_index].inbounds = clientInbounds.join(',')
+ const clientInbounds = clients.value[c_index].inbounds.filter((x:string) => x!=tag)
+ clients.value[c_index].inbounds = clientInbounds
}
})
}
@@ -278,15 +278,14 @@ const delInbound = (index: number) => {
}
const buildInboundsUsers = (inbound:InboundWithUser):Inbound => {
const users = []
- const inboundClients = clients.value.filter(c => c.enable && c.inbounds.split(',').includes(inbound.tag))
+ const inboundClients = clients.value.filter(c => c.enable && c.inbounds.includes(inbound.tag))
inboundClients.forEach(c => {
- const clientConfig = JSON.parse(c.config)
// Remove flow in non tls VLESS
if (inbound.type == InTypes.VLESS) {
const vlessInbound = inbound
- if (!vlessInbound.tls?.enabled || vlessInbound.transport?.type) delete(clientConfig["vless"].flow)
+ if (!vlessInbound.tls?.enabled || vlessInbound.transport?.type) delete(c.config?.vless?.flow)
}
- users.push(clientConfig[inbound.type])
+ users.push(c.config[inbound.type])
})
inbound.users = users
@@ -306,11 +305,10 @@ const buildInboundsUsers = (inbound:InboundWithUser):Inbound => {
}
const changeClientInboundsTag = (oldtag: string, newTag:string) => {
clients.value.forEach((c, c_index) => {
- const inboundsArray = c.inbounds.split(',')
- const inbound_index = inboundsArray.findIndex(i => i == oldtag)
+ const inbound_index = c.inbounds.findIndex(i => i == oldtag)
if (inbound_index != -1) {
- inboundsArray[inbound_index] = newTag
- clients.value[c_index].inbounds = inboundsArray.join(',')
+ c.inbounds[inbound_index] = newTag
+ clients.value[c_index].inbounds = c.inbounds
}
})
}
diff --git a/frontend/src/views/Tls.vue b/frontend/src/views/Tls.vue
index 030648e..77c3dac 100644
--- a/frontend/src/views/Tls.vue
+++ b/frontend/src/views/Tls.vue
@@ -141,7 +141,7 @@ const updateLinks = (i:any,tlsClient:any) => {
i.users.forEach((u:any) => {
const client = clients.value.find(c => u.username? c.name == u.username : c.name == u.name)
if (client){
- const clientInbounds = inbounds.value.filter(inb => client?.inbounds.split(',').includes(inb.tag))
+ const clientInbounds = inbounds.value.filter(inb => client?.inbounds.includes(inb.tag))
const newLinks = []
clientInbounds.forEach(i =>{
const uri = LinkUtil.linkGenerator(client.name,i,tlsClient)
@@ -149,10 +149,10 @@ const updateLinks = (i:any,tlsClient:any) => {
newLinks.push({ type: 'local', remark: i.tag, uri: uri })
}
})
- let links = client.links && client.links.length>0? JSON.parse(client.links) : []
- links = [...newLinks, ...links.filter(l => l.type != 'local')]
+ let links = client.links && client.links.length>0? client.links : []
+ links = [...newLinks, ...links.filter((l:Link) => l.type != 'local')]
- client.links = JSON.stringify(links)
+ client.links = links
}
})
}