init users on create inbound #411

This commit is contained in:
Alireza Ahmadi
2025-01-18 10:55:22 +01:00
parent b43a6ade97
commit d06b6be4a2
7 changed files with 184 additions and 37 deletions
+2 -1
View File
@@ -88,7 +88,8 @@ func (a *APIHandler) postHandler(c *gin.Context) {
obj := c.Request.FormValue("object") obj := c.Request.FormValue("object")
act := c.Request.FormValue("action") act := c.Request.FormValue("action")
data := c.Request.FormValue("data") data := c.Request.FormValue("data")
objs, err := a.ConfigService.Save(obj, act, json.RawMessage(data), loginUser, hostname) initUsers := c.Request.FormValue("initUsers")
objs, err := a.ConfigService.Save(obj, act, json.RawMessage(data), initUsers, loginUser, hostname)
if err != nil { if err != nil {
jsonMsg(c, "save", err) jsonMsg(c, "save", err)
return return
+51
View File
@@ -7,6 +7,7 @@ import (
"s-ui/logger" "s-ui/logger"
"s-ui/util" "s-ui/util"
"s-ui/util/common" "s-ui/util/common"
"strings"
"time" "time"
"gorm.io/gorm" "gorm.io/gorm"
@@ -156,6 +157,56 @@ func (s *ClientService) updateLinksWithFixedInbounds(tx *gorm.DB, clients []*mod
return nil return nil
} }
func (s *ClientService) UpdateClientsOnInboundAdd(tx *gorm.DB, initIds string, inboundId uint, hostname string) error {
clientIds := strings.Split(initIds, ",")
var clients []model.Client
err := tx.Model(model.Client{}).Where("id in ?", clientIds).Find(&clients).Error
if err != nil {
return err
}
var inbound model.Inbound
err = tx.Model(model.Inbound{}).Preload("Tls").Where("id = ?", inboundId).Find(&inbound).Error
if err != nil {
return err
}
for _, client := range clients {
// Add inbounds
var clientInbounds []uint
json.Unmarshal(client.Inbounds, &clientInbounds)
clientInbounds = append(clientInbounds, inboundId)
client.Inbounds, err = json.MarshalIndent(clientInbounds, "", " ")
if err != nil {
return err
}
// Add links
var clientLinks, newClientLinks []map[string]string
json.Unmarshal(client.Links, &clientLinks)
newLinks := util.LinkGenerator(client.Config, &inbound, hostname)
for _, newLink := range newLinks {
newClientLinks = append(newClientLinks, map[string]string{
"remark": inbound.Tag,
"type": "local",
"uri": newLink,
})
}
for _, clientLink := range clientLinks {
if clientLink["remark"] != inbound.Tag {
newClientLinks = append(newClientLinks, clientLink)
}
}
client.Links, err = json.MarshalIndent(newClientLinks, "", " ")
if err != nil {
return err
}
err = tx.Save(&client).Error
if err != nil {
return err
}
}
return nil
}
func (s *ClientService) UpdateClientsOnInboundDelete(tx *gorm.DB, id uint, tag string) error { func (s *ClientService) UpdateClientsOnInboundDelete(tx *gorm.DB, id uint, tag string) error {
var clients []model.Client var clients []model.Client
err := tx.Table("clients"). err := tx.Table("clients").
+5 -4
View File
@@ -116,7 +116,7 @@ func (s *ConfigService) StopCore() error {
return nil return nil
} }
func (s *ConfigService) Save(obj string, act string, data json.RawMessage, loginUser string, hostname string) ([]string, error) { func (s *ConfigService) Save(obj string, act string, data json.RawMessage, initUsers string, loginUser string, hostname string) ([]string, error) {
var err error var err error
var inboundIds []uint var inboundIds []uint
var inboundId uint var inboundId uint
@@ -139,7 +139,7 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, login
case "tls": case "tls":
inboundIds, err = s.TlsService.Save(tx, act, data) inboundIds, err = s.TlsService.Save(tx, act, data)
case "inbounds": case "inbounds":
inboundId, err = s.InboundService.Save(tx, act, data, hostname) inboundId, err = s.InboundService.Save(tx, act, data, initUsers, hostname)
case "outbounds": case "outbounds":
err = s.OutboundService.Save(tx, act, data) err = s.OutboundService.Save(tx, act, data)
case "endpoints": case "endpoints":
@@ -173,7 +173,6 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, login
// Commit changes so far // Commit changes so far
tx.Commit() tx.Commit()
LastUpdate = time.Now().Unix() LastUpdate = time.Now().Unix()
var objs []string = []string{obj}
tx = db.Begin() tx = db.Begin()
// Update side changes // Update side changes
@@ -186,8 +185,10 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, login
} }
objs = append(objs, "clients") objs = append(objs, "clients")
} }
if obj == "inbounds" && act != "add" { if obj == "inbounds" {
switch act { switch act {
case "new":
err = s.ClientService.UpdateClientsOnInboundAdd(tx, initUsers, inboundId, hostname)
case "edit": case "edit":
err = s.ClientService.UpdateLinksByInboundChange(tx, []uint{inboundId}, hostname) err = s.ClientService.UpdateLinksByInboundChange(tx, []uint{inboundId}, hostname)
case "del": case "del":
+65 -12
View File
@@ -96,7 +96,7 @@ func (s *InboundService) FromIds(ids []uint) ([]*model.Inbound, error) {
return inbounds, nil return inbounds, nil
} }
func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, hostname string) (uint, error) { func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, initUsers string, hostname string) (uint, error) {
var err error var err error
var id uint var id uint
@@ -107,7 +107,6 @@ func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, hos
if err != nil { if err != nil {
return 0, err return 0, err
} }
id = inbound.Id
if inbound.TlsId > 0 { if inbound.TlsId > 0 {
err = tx.Model(model.Tls{}).Where("id = ?", inbound.TlsId).Find(&inbound.Tls).Error err = tx.Model(model.Tls{}).Where("id = ?", inbound.TlsId).Find(&inbound.Tls).Error
if err != nil { if err != nil {
@@ -115,6 +114,17 @@ func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, hos
} }
} }
err = util.FillOutJson(&inbound, hostname)
if err != nil {
return 0, err
}
err = tx.Save(&inbound).Error
if err != nil {
return 0, err
}
id = inbound.Id
if corePtr.IsRunning() { if corePtr.IsRunning() {
if act == "edit" { if act == "edit" {
var oldTag string var oldTag string
@@ -133,7 +143,11 @@ func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, hos
return 0, err return 0, err
} }
if act == "edit" {
inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type) inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type)
} else {
inboundConfig, err = s.initUsers(tx, inboundConfig, initUsers, inbound.Id, inbound.Type)
}
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -143,16 +157,6 @@ func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, hos
return 0, err return 0, err
} }
} }
err = util.FillOutJson(&inbound, hostname)
if err != nil {
return 0, err
}
err = tx.Save(&inbound).Error
if err != nil {
return 0, err
}
case "del": case "del":
var tag string var tag string
err = json.Unmarshal(data, &tag) err = json.Unmarshal(data, &tag)
@@ -263,7 +267,56 @@ func (s *InboundService) addUsers(db *gorm.DB, inboundJson []byte, inboundId uin
inbound["users"] = usersJson inbound["users"] = usersJson
} }
return json.Marshal(inbound)
}
func (s *InboundService) initUsers(db *gorm.DB, inboundJson []byte, clientIds string, inboundId uint, inboundType string) ([]byte, error) {
ClientIds := strings.Split(clientIds, ",")
if len(ClientIds) == 0 {
return inboundJson, nil
}
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 == "shadowtls" {
version, _ := inbound["version"].(float64)
if int(version) < 3 {
return inboundJson, nil
}
}
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
WHERE enable = true AND id in ?`,
"$."+inboundType, ClientIds).Scan(&users).Error
if err != nil {
return nil, err
}
var usersJson []json.RawMessage
for _, user := range users {
usersJson = append(usersJson, json.RawMessage(user))
}
if len(usersJson) > 0 || inboundType != "shadowsocks" {
inbound["users"] = usersJson inbound["users"] = usersJson
}
return json.Marshal(inbound) return json.Marshal(inbound)
} }
+20 -13
View File
@@ -2,34 +2,41 @@
<v-card :subtitle="$t('pages.clients')"> <v-card :subtitle="$t('pages.clients')">
<v-row> <v-row>
<v-col cols="12" sm="6" md="4"> <v-col cols="12" sm="6" md="4">
<v-switch <v-select v-model="data.model" :items="initUsersModels" @update:model-value="data.values = []" hide-details></v-select>
v-model="hasUser" </v-col>
@change="() => {inbound.users = hasUser? [] : undefined}" <v-col cols="12" sm="6" md="4" v-if="data.model == 'group'">
color="primary" <v-select v-model="data.values" multiple chips :items="groupNames" :label="$t('client.group')" hide-details></v-select>
:label="$t('in.clients')" </v-col>
hide-details></v-switch> <v-col cols="12" sm="8" v-if="data.model == 'client'">
<v-select v-model="data.values" multiple chips :items="clientNames" :label="$t('pages.clients')" hide-details></v-select>
</v-col> </v-col>
</v-row> </v-row>
</v-card> </v-card>
</template> </template>
<script lang="ts"> <script lang="ts">
import { i18n } from '@/locales';
export default { export default {
props: ['inbound'], props: ['data', 'clients'],
data() { data() {
return { return {
hasUser: false, initUsersModels: [
{ title: i18n.global.t('none'), value: 'none' },
{ title: i18n.global.t('all'), value: 'all' },
{ title: i18n.global.t('client.group'), value: 'group' },
{ title: i18n.global.t('pages.clients'), value: 'client' },
],
} }
}, },
computed: { computed: {
cardTitle() { clientNames() {
this.hasUser = Object.hasOwn(this.$props.inbound,'users') return this.$props.clients.map((c:any) => { return { title: c.name, value: c.id } } )
return this.$props.inbound?.type.toUpperCase()
}, },
groupNames() {
return Array.from(new Set(this.$props.clients.map((c:any) => c.group)))
}, },
mounted() {
this.hasUser = Object.hasOwn(this.$props.inbound,'users')
} }
} }
</script> </script>
+35 -3
View File
@@ -50,7 +50,7 @@
<Tun v-if="inbound.type == inTypes.Tun" :data="inbound" /> <Tun v-if="inbound.type == inTypes.Tun" :data="inbound" />
<TProxy v-if="inbound.type == inTypes.TProxy" :inbound="inbound" /> <TProxy v-if="inbound.type == inTypes.TProxy" :inbound="inbound" />
<Transport v-if="Object.hasOwn(inbound,'transport')" :data="inbound" /> <Transport v-if="Object.hasOwn(inbound,'transport')" :data="inbound" />
<Users v-if="HasOptionalUser.includes(inbound.type)" :inbound="inbound" /> <Users v-if="hasUser" :clients="clients" :data="initUsers" />
<InTls v-if="HasTls.includes(inbound.type)" :inbound="inbound" :tlsConfigs="tlsConfigs" :tls_id="inbound.tls_id" /> <InTls v-if="HasTls.includes(inbound.type)" :inbound="inbound" :tlsConfigs="tlsConfigs" :tls_id="inbound.tls_id" />
<Multiplex v-if="Object.hasOwn(inbound,'multiplex')" direction="in" :data="inbound" /> <Multiplex v-if="Object.hasOwn(inbound,'multiplex')" direction="in" :data="inbound" />
</v-window-item> </v-window-item>
@@ -95,7 +95,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { InTypes, createInbound, Addr } from '@/types/inbounds' import { InTypes, createInbound, Addr, inboundWithUsers, ShadowTLS } from '@/types/inbounds'
import RandomUtil from '@/plugins/randomUtil' import RandomUtil from '@/plugins/randomUtil'
import Listen from '@/components/Listen.vue' import Listen from '@/components/Listen.vue'
@@ -125,7 +125,11 @@ export default {
loading: false, loading: false,
side: "s", side: "s",
inTypes: InTypes, inTypes: InTypes,
HasOptionalUser: [InTypes.Mixed,InTypes.SOCKS,InTypes.HTTP,InTypes.Shadowsocks], inboundWithUsers: inboundWithUsers,
initUsers: {
model: 'none',
values: <any>[],
},
HasInData: [ HasInData: [
InTypes.SOCKS, InTypes.SOCKS,
InTypes.HTTP, InTypes.HTTP,
@@ -179,6 +183,10 @@ export default {
this.loading = false this.loading = false
} }
this.side = "s" this.side = "s"
this.initUsers = {
model: 'none',
values: [],
}
}, },
changeType() { changeType() {
if (!this.inbound.listen_port) this.inbound.listen_port = RandomUtil.randomIntRange(10000, 60000) if (!this.inbound.listen_port) this.inbound.listen_port = RandomUtil.randomIntRange(10000, 60000)
@@ -205,7 +213,22 @@ export default {
}, },
saveChanges() { saveChanges() {
this.loading = true this.loading = true
if (this.hasUser) {
let clientIds = []
switch (this.initUsers.model) {
case 'all':
clientIds = this.clients.map((c:any) => c.id)
break
case 'group':
clientIds = this.clients.filter((c:any) => this.initUsers.values.includes(c.group)).map((c:any) => c.id)
break
case 'user':
clientIds = this.initUsers.values
}
this.$emit('save', this.inbound, clientIds.length > 0 ? clientIds : undefined)
} else {
this.$emit('save', this.inbound) this.$emit('save', this.inbound)
}
this.loading = false this.loading = false
}, },
}, },
@@ -217,6 +240,15 @@ export default {
if (this.OnlyTLS.includes(this.inbound.type) && this.inbound.tls_id == 0) return false if (this.OnlyTLS.includes(this.inbound.type) && this.inbound.tls_id == 0) return false
return true return true
}, },
clients() {
return Data().clients?? []
},
hasUser() {
if (this.$props.id > 0) return false
if (!inboundWithUsers.includes(this.inbound.type)) return false
if (this.inbound.type == InTypes.ShadowTLS && (<ShadowTLS>this.inbound).version < 3 ) return false
return true
}
}, },
watch: { watch: {
visible(newValue) { visible(newValue) {
+2
View File
@@ -64,10 +64,12 @@ const Data = defineStore('Data', {
} }
return <Client>{} return <Client>{}
}, },
async save (object: string, action: string, data: any, initUsers?: number[]): Promise<boolean> {
let postData = { let postData = {
object: object, object: object,
action: action, action: action,
data: JSON.stringify(data, null, 2), data: JSON.stringify(data, null, 2),
initUsers: initUsers?.join(',') ?? undefined
} }
const msg = await HttpUtils.post('api/save', postData) const msg = await HttpUtils.post('api/save', postData)
if (msg.success) { if (msg.success) {