diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/assets/js/model/models.js | 8 | ||||
| -rw-r--r-- | web/controller/setting.go | 45 | ||||
| -rw-r--r-- | web/controller/sub.go | 48 | ||||
| -rw-r--r-- | web/entity/entity.go | 30 | ||||
| -rw-r--r-- | web/global/global.go | 13 | ||||
| -rw-r--r-- | web/html/xui/client_bulk_modal.html | 6 | ||||
| -rw-r--r-- | web/html/xui/form/client.html | 4 | ||||
| -rw-r--r-- | web/html/xui/form/protocol/shadowsocks.html | 4 | ||||
| -rw-r--r-- | web/html/xui/form/protocol/trojan.html | 4 | ||||
| -rw-r--r-- | web/html/xui/form/protocol/vless.html | 4 | ||||
| -rw-r--r-- | web/html/xui/form/protocol/vmess.html | 4 | ||||
| -rw-r--r-- | web/html/xui/inbounds.html | 28 | ||||
| -rw-r--r-- | web/html/xui/settings.html | 18 | ||||
| -rw-r--r-- | web/service/inbound.go | 28 | ||||
| -rw-r--r-- | web/service/setting.go | 50 | ||||
| -rw-r--r-- | web/service/sub.go | 650 | ||||
| -rw-r--r-- | web/web.go | 2 |
17 files changed, 215 insertions, 731 deletions
diff --git a/web/assets/js/model/models.js b/web/assets/js/model/models.js index d8395b56..9a5dcc85 100644 --- a/web/assets/js/model/models.js +++ b/web/assets/js/model/models.js @@ -184,6 +184,14 @@ class AllSetting { this.tgLang = "en-US"; this.xrayTemplateConfig = ""; this.secretEnable = false; + this.subEnable = false; + this.subListen = ""; + this.subPort = "2096"; + this.subPath = "sub/"; + this.subDomain = ""; + this.subCertFile = ""; + this.subKeyFile = ""; + this.subUpdates = 0; this.timeLocation = "Asia/Tehran"; diff --git a/web/controller/setting.go b/web/controller/setting.go index 226b7975..0292c46a 100644 --- a/web/controller/setting.go +++ b/web/controller/setting.go @@ -85,11 +85,56 @@ func (a *SettingController) getDefaultSettings(c *gin.Context) { jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) return } + tgBotEnable, err := a.settingService.GetTgbotenabled() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + subEnable, err := a.settingService.GetSubEnable() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + subPort, err := a.settingService.GetSubPort() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + subPath, err := a.settingService.GetSubPath() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + subDomain, err := a.settingService.GetSubDomain() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + subKeyFile, err := a.settingService.GetSubKeyFile() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + subCertFile, err := a.settingService.GetSubCertFile() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + subTLS := false + if subKeyFile != "" || subCertFile != "" { + subTLS = true + } result := map[string]interface{}{ "expireDiff": expireDiff, "trafficDiff": trafficDiff, "defaultCert": defaultCert, "defaultKey": defaultKey, + "tgBotEnable": tgBotEnable, + "subEnable": subEnable, + "subPort": subPort, + "subPath": subPath, + "subDomain": subDomain, + "subTLS": subTLS, } jsonObj(c, result, nil) } diff --git a/web/controller/sub.go b/web/controller/sub.go deleted file mode 100644 index 2b218c21..00000000 --- a/web/controller/sub.go +++ /dev/null @@ -1,48 +0,0 @@ -package controller - -import ( - "encoding/base64" - "strings" - "x-ui/web/service" - - "github.com/gin-gonic/gin" -) - -type SUBController struct { - BaseController - - subService service.SubService -} - -func NewSUBController(g *gin.RouterGroup) *SUBController { - a := &SUBController{} - a.initRouter(g) - return a -} - -func (a *SUBController) initRouter(g *gin.RouterGroup) { - g = g.Group("/sub") - - g.GET("/:subid", a.subs) -} - -func (a *SUBController) subs(c *gin.Context) { - subId := c.Param("subid") - host := strings.Split(c.Request.Host, ":")[0] - subs, headers, err := a.subService.GetSubs(subId, host) - if err != nil || len(subs) == 0 { - c.String(400, "Error!") - } else { - result := "" - for _, sub := range subs { - result += sub + "\n" - } - - // Add headers - c.Writer.Header().Set("Subscription-Userinfo", headers[0]) - c.Writer.Header().Set("Profile-Update-Interval", headers[1]) - c.Writer.Header().Set("Profile-Title", headers[2]) - - c.String(200, base64.StdEncoding.EncodeToString([]byte(result))) - } -} diff --git a/web/entity/entity.go b/web/entity/entity.go index 52f26769..0bfbfd2a 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -45,6 +45,14 @@ type AllSetting struct { XrayTemplateConfig string `json:"xrayTemplateConfig" form:"xrayTemplateConfig"` TimeLocation string `json:"timeLocation" form:"timeLocation"` SecretEnable bool `json:"secretEnable" form:"secretEnable"` + SubEnable bool `json:"subEnable" form:"subEnable"` + SubListen string `json:"subListen" form:"subListen"` + SubPort int `json:"subPort" form:"subPort"` + SubPath string `json:"subPath" form:"subPath"` + SubDomain string `json:"subDomain" form:"subDomain"` + SubCertFile string `json:"subCertFile" form:"subCertFile"` + SubKeyFile string `json:"subKeyFile" form:"subKeyFile"` + SubUpdates int `json:"subUpdates" form:"subUpdates"` } func (s *AllSetting) CheckValid() error { @@ -55,10 +63,25 @@ func (s *AllSetting) CheckValid() error { } } + if s.SubListen != "" { + ip := net.ParseIP(s.SubListen) + if ip == nil { + return common.NewError("Sub listen is not valid ip:", s.SubListen) + } + } + if s.WebPort <= 0 || s.WebPort > 65535 { return common.NewError("web port is not a valid port:", s.WebPort) } + if s.SubPort <= 0 || s.SubPort > 65535 { + return common.NewError("Sub port is not a valid port:", s.SubPort) + } + + if s.SubPort == s.WebPort { + return common.NewError("Sub and Web could not use same port:", s.SubPort) + } + if s.WebCertFile != "" || s.WebKeyFile != "" { _, err := tls.LoadX509KeyPair(s.WebCertFile, s.WebKeyFile) if err != nil { @@ -66,6 +89,13 @@ func (s *AllSetting) CheckValid() error { } } + if s.SubCertFile != "" || s.SubKeyFile != "" { + _, err := tls.LoadX509KeyPair(s.SubCertFile, s.SubKeyFile) + if err != nil { + return common.NewErrorf("cert file <%v> or key file <%v> invalid: %v", s.SubCertFile, s.SubKeyFile, err) + } + } + if !strings.HasPrefix(s.WebBasePath, "/") { s.WebBasePath = "/" + s.WebBasePath } diff --git a/web/global/global.go b/web/global/global.go index 7ded94e7..7d0b4e1f 100644 --- a/web/global/global.go +++ b/web/global/global.go @@ -8,12 +8,17 @@ import ( ) var webServer WebServer +var subServer SubServer type WebServer interface { GetCron() *cron.Cron GetCtx() context.Context } +type SubServer interface { + GetCtx() context.Context +} + func SetWebServer(s WebServer) { webServer = s } @@ -21,3 +26,11 @@ func SetWebServer(s WebServer) { func GetWebServer() WebServer { return webServer } + +func SetSubServer(s SubServer) { + subServer = s +} + +func GetSubServer() SubServer { + return subServer +} diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html index 076579a9..48eb2cbc 100644 --- a/web/html/xui/client_bulk_modal.html +++ b/web/html/xui/client_bulk_modal.html @@ -33,7 +33,7 @@ <span slot="label">{{ i18n "pages.client.clientCount" }}</span> <a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number> </a-form-item> - <a-form-item> + <a-form-item v-if="app.subSettings.enable"> <span slot="label"> Subscription <a-tooltip> @@ -45,7 +45,7 @@ </span> <a-input v-model.trim="clientsBulkModal.subId"></a-input> </a-form-item> - <a-form-item> + <a-form-item v-if="app.tgBotEnable"> <span slot="label"> Telegram ID <a-tooltip> @@ -204,6 +204,7 @@ case Protocols.VMESS: return clientSettings.vmesses; case Protocols.VLESS: return clientSettings.vlesses; case Protocols.TROJAN: return clientSettings.trojans; + case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses; default: return null; } }, @@ -212,6 +213,7 @@ case Protocols.VMESS: return new Inbound.VmessSettings.Vmess(); case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS(); case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan(); + case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks(); default: return null; } }, diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html index 625ff0ca..9494f0ac 100644 --- a/web/html/xui/form/client.html +++ b/web/html/xui/form/client.html @@ -34,7 +34,7 @@ <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"> </a-icon> <a-input v-model.trim="client.id" style="width: 300px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.subSettings.enable"> <span slot="label"> Subscription <a-tooltip> @@ -47,7 +47,7 @@ <a-icon @click="client.subId = RandomUtil.randomText()" type="sync"> </a-icon> <a-input v-model.trim="client.subId" style="width: 150px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.tgBotEnable" > <span slot="label"> Telegram ID <a-tooltip> diff --git a/web/html/xui/form/protocol/shadowsocks.html b/web/html/xui/form/protocol/shadowsocks.html index aa3fde18..45385300 100644 --- a/web/html/xui/form/protocol/shadowsocks.html +++ b/web/html/xui/form/protocol/shadowsocks.html @@ -18,7 +18,7 @@ <a-icon @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon> <a-input v-model.trim="client.password" style="width: 250px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.subSettings.enable"> <span slot="label"> Subscription <a-tooltip> @@ -31,7 +31,7 @@ <a-icon @click="client.subId = RandomUtil.randomText()" type="sync"> </a-icon> <a-input v-model.trim="client.subId" style="width: 150px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.tgBotEnable"> <span slot="label"> Telegram ID <a-tooltip> diff --git a/web/html/xui/form/protocol/trojan.html b/web/html/xui/form/protocol/trojan.html index b6a57644..caec459a 100644 --- a/web/html/xui/form/protocol/trojan.html +++ b/web/html/xui/form/protocol/trojan.html @@ -18,7 +18,7 @@ <a-icon @click="client.password = RandomUtil.randomSeq(10)" type="sync"> </a-icon> <a-input v-model.trim="client.password" style="width: 150px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.subSettings.enable"> <span slot="label"> Subscription <a-tooltip> @@ -31,7 +31,7 @@ <a-icon @click="client.subId = RandomUtil.randomText()" type="sync"> </a-icon> <a-input v-model.trim="client.subId" style="width: 150px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.tgBotEnable"> <span slot="label"> Telegram ID <a-tooltip> diff --git a/web/html/xui/form/protocol/vless.html b/web/html/xui/form/protocol/vless.html index f2678065..3155ee28 100644 --- a/web/html/xui/form/protocol/vless.html +++ b/web/html/xui/form/protocol/vless.html @@ -18,7 +18,7 @@ <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"> </a-icon> <a-input v-model.trim="client.id" style="width: 300px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.subSettings.enable"> <span slot="label"> Subscription <a-tooltip> @@ -31,7 +31,7 @@ <a-icon @click="client.subId = RandomUtil.randomText()" type="sync"> </a-icon> <a-input v-model.trim="client.subId" style="width: 150px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.tgBotEnable"> <span slot="label"> Telegram ID <a-tooltip> diff --git a/web/html/xui/form/protocol/vmess.html b/web/html/xui/form/protocol/vmess.html index d46c16f9..469440c6 100644 --- a/web/html/xui/form/protocol/vmess.html +++ b/web/html/xui/form/protocol/vmess.html @@ -23,7 +23,7 @@ <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"> </a-icon> <a-input v-model.trim="client.id" style="width: 300px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.subSettings.enable"> <span slot="label"> Subscription <a-tooltip> @@ -36,7 +36,7 @@ <a-icon @click="client.subId = RandomUtil.randomText()" type="sync"> </a-icon> <a-input v-model.trim="client.subId" style="width: 150px;"></a-input> </a-form-item> - <a-form-item v-if="client.email"> + <a-form-item v-if="client.email && app.tgBotEnable"> <span slot="label"> Telegram ID <a-tooltip> diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html index b15798d4..e4d76f7e 100644 --- a/web/html/xui/inbounds.html +++ b/web/html/xui/inbounds.html @@ -343,7 +343,15 @@ clientCount: {}, isRefreshEnabled: localStorage.getItem("isRefreshEnabled") === "true" ? true : false, refreshing: false, - refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000 + refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000, + subSettings: { + enable : false, + port: 0, + path: '', + domain: '', + tls: false + }, + tgBotEnable: false }, methods: { loading(spinning = true) { @@ -365,10 +373,20 @@ if (!msg.success) { return; } - this.expireDiff = msg.obj.expireDiff * 86400000; - this.trafficDiff = msg.obj.trafficDiff * 1073741824; - this.defaultCert = msg.obj.defaultCert; - this.defaultKey = msg.obj.defaultKey; + with(msg.obj){ + this.expireDiff = expireDiff * 86400000; + this.trafficDiff = trafficDiff * 1073741824; + this.defaultCert = defaultCert; + this.defaultKey = defaultKey; + this.tgBotEnable = tgBotEnable; + this.subSettings = { + enable : subEnable, + port: subPort, + path: subPath, + domain: subDomain, + tls: subTLS + }; + } }, setInbounds(dbInbounds) { this.inbounds.splice(0); diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html index f5ea4994..b838b17f 100644 --- a/web/html/xui/settings.html +++ b/web/html/xui/settings.html @@ -363,6 +363,24 @@ </a-list-item> </a-list> </a-tab-pane> + <a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }}'> + <a-row :xs="24" :sm="24" :lg="12"> + <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 20px 20px; text-align: center;"> + <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon> + {{ i18n "pages.settings.infoDesc" }} + </h2> + </a-row> + <a-list item-layout="horizontal" :style="themeSwitcher.textStyle"> + <setting-list-item type="switch" title='{{ i18n "pages.settings.subEnable"}}' desc='{{ i18n "pages.settings.subEnableDesc"}}' v-model="allSetting.subEnable"></setting-list-item> + <setting-list-item type="text" title='{{ i18n "pages.settings.subListen"}}' desc='{{ i18n "pages.settings.subListenDesc"}}' v-model="allSetting.subListen"></setting-list-item> + <setting-list-item type="number" title='{{ i18n "pages.settings.subPort"}}' desc='{{ i18n "pages.settings.subPortDesc"}}' v-model.number="allSetting.subPort"></setting-list-item> + <setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subPath"></setting-list-item> + <setting-list-item type="text" title='{{ i18n "pages.settings.subDomain"}}' desc='{{ i18n "pages.settings.subDomainDesc"}}' v-model="allSetting.subDomain"></setting-list-item> + <setting-list-item type="text" title='{{ i18n "pages.settings.subCertPath"}}' desc='{{ i18n "pages.settings.subCertPathDesc"}}' v-model="allSetting.subCertFile"></setting-list-item> + <setting-list-item type="text" title='{{ i18n "pages.settings.subKeyPath"}}' desc='{{ i18n "pages.settings.subKeyPathDesc"}}' v-model="allSetting.subKeyFile"></setting-list-item> + <setting-list-item type="number" title='{{ i18n "pages.settings.subUpdates"}}' desc='{{ i18n "pages.settings.subUpdatesDesc"}}' v-model="allSetting.subUpdates"></setting-list-item> + </a-list> + </a-tab-pane> </a-tabs> </a-space> </a-spin> diff --git a/web/service/inbound.go b/web/service/inbound.go index 57df0b9e..6a182fcf 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -51,7 +51,7 @@ func (s *InboundService) checkPortExist(port int, ignoreId int) (bool, error) { return count > 0, nil } -func (s *InboundService) getClients(inbound *model.Inbound) ([]model.Client, error) { +func (s *InboundService) GetClients(inbound *model.Inbound) ([]model.Client, error) { settings := map[string][]model.Client{} json.Unmarshal([]byte(inbound.Settings), &settings) if settings == nil { @@ -110,7 +110,7 @@ func (s *InboundService) checkEmailsExistForClients(clients []model.Client) (str } func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (string, error) { - clients, err := s.getClients(inbound) + clients, err := s.GetClients(inbound) if err != nil { return "", err } @@ -150,7 +150,7 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, err return inbound, common.NewError("Duplicate email:", existEmail) } - clients, err := s.getClients(inbound) + clients, err := s.GetClients(inbound) if err != nil { return inbound, err } @@ -208,7 +208,7 @@ func (s *InboundService) DelInbound(id int) error { if err != nil { return err } - clients, err := s.getClients(inbound) + clients, err := s.GetClients(inbound) if err != nil { return err } @@ -263,7 +263,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, } func (s *InboundService) AddInboundClient(data *model.Inbound) error { - clients, err := s.getClients(data) + clients, err := s.GetClients(data) if err != nil { return err } @@ -372,7 +372,7 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) error } func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId string) error { - clients, err := s.getClients(data) + clients, err := s.GetClients(data) if err != nil { return err } @@ -390,7 +390,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin return err } - oldClients, err := s.getClients(oldInbound) + oldClients, err := s.GetClients(oldInbound) if err != nil { return err } @@ -712,7 +712,7 @@ func (s *InboundService) GetClientByEmail(clientEmail string) (*xray.ClientTraff return nil, nil, common.NewError("Inbound Not Found For Email:", clientEmail) } - clients, err := s.getClients(inbound) + clients, err := s.GetClients(inbound) if err != nil { return nil, nil, err } @@ -737,7 +737,7 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err clientEmail := traffic.Email - oldClients, err := s.getClients(inbound) + oldClients, err := s.GetClients(inbound) if err != nil { return err } @@ -791,7 +791,7 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, er return false, common.NewError("Inbound Not Found For Email:", clientEmail) } - oldClients, err := s.getClients(inbound) + oldClients, err := s.GetClients(inbound) if err != nil { return false, err } @@ -847,7 +847,7 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int return common.NewError("Inbound Not Found For Email:", clientEmail) } - oldClients, err := s.getClients(inbound) + oldClients, err := s.GetClients(inbound) if err != nil { return err } @@ -901,7 +901,7 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry return common.NewError("Inbound Not Found For Email:", clientEmail) } - oldClients, err := s.getClients(inbound) + oldClients, err := s.GetClients(inbound) if err != nil { return err } @@ -1100,7 +1100,7 @@ func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTr } var emails []string for _, inbound := range inbounds { - clients, err := s.getClients(inbound) + clients, err := s.GetClients(inbound) if err != nil { logger.Error("Unable to get clients from inbound") } @@ -1250,7 +1250,7 @@ func (s *InboundService) MigrationRequirements() { inbounds[inbound_index].Settings = string(modifiedSettings) } // Add client traffic row for all clients which has email - modelClients, err := s.getClients(inbounds[inbound_index]) + modelClients, err := s.GetClients(inbounds[inbound_index]) if err != nil { return } diff --git a/web/service/setting.go b/web/service/setting.go index fec324af..593b23be 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -41,6 +41,14 @@ var defaultValueMap = map[string]string{ "tgCpu": "0", "tgLang": "en-US", "secretEnable": "false", + "subEnable": "false", + "subListen": "", + "subPort": "2096", + "subPath": "sub/", + "subDomain": "", + "subCertFile": "", + "subKeyFile": "", + "subUpdates": "12", } type SettingService struct { @@ -336,6 +344,48 @@ func (s *SettingService) GetTimeLocation() (*time.Location, error) { return location, nil } +func (s *SettingService) GetSubEnable() (bool, error) { + return s.getBool("subEnable") +} + +func (s *SettingService) GetSubListen() (string, error) { + return s.getString("subListen") +} + +func (s *SettingService) GetSubPort() (int, error) { + return s.getInt("subPort") +} + +func (s *SettingService) GetSubPath() (string, error) { + subPath, err := s.getString("subPath") + if err != nil { + return "", err + } + if !strings.HasPrefix(subPath, "/") { + subPath = "/" + subPath + } + if !strings.HasSuffix(subPath, "/") { + subPath += "/" + } + return subPath, nil +} + +func (s *SettingService) GetSubDomain() (string, error) { + return s.getString("subDomain") +} + +func (s *SettingService) GetSubCertFile() (string, error) { + return s.getString("subCertFile") +} + +func (s *SettingService) GetSubKeyFile() (string, error) { + return s.getString("subKeyFile") +} + +func (s *SettingService) GetSubUpdates() (int, error) { + return s.getInt("subUpdates") +} + func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { if err := allSetting.CheckValid(); err != nil { return err diff --git a/web/service/sub.go b/web/service/sub.go deleted file mode 100644 index b9ea49bd..00000000 --- a/web/service/sub.go +++ /dev/null @@ -1,650 +0,0 @@ -package service - -import ( - "encoding/base64" - "fmt" - "net/url" - "strings" - "x-ui/database" - "x-ui/database/model" - "x-ui/logger" - "x-ui/xray" - - "github.com/goccy/go-json" -) - -type SubService struct { - address string - inboundService InboundService -} - -func (s *SubService) GetSubs(subId string, host string) ([]string, []string, error) { - s.address = host - var result []string - var headers []string - var traffic xray.ClientTraffic - var clientTraffics []xray.ClientTraffic - inbounds, err := s.getInboundsBySubId(subId) - if err != nil { - return nil, nil, err - } - for _, inbound := range inbounds { - clients, err := s.inboundService.getClients(inbound) - if err != nil { - logger.Error("SubService - GetSub: Unable to get clients from inbound") - } - if clients == nil { - continue - } - for _, client := range clients { - if client.Enable && client.SubID == subId { - link := s.getLink(inbound, client.Email) - result = append(result, link) - clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email)) - } - } - } - for index, clientTraffic := range clientTraffics { - if index == 0 { - traffic.Up = clientTraffic.Up - traffic.Down = clientTraffic.Down - traffic.Total = clientTraffic.Total - if clientTraffic.ExpiryTime > 0 { - traffic.ExpiryTime = clientTraffic.ExpiryTime - } - } else { - traffic.Up += clientTraffic.Up - traffic.Down += clientTraffic.Down - if traffic.Total == 0 || clientTraffic.Total == 0 { - traffic.Total = 0 - } else { - traffic.Total += clientTraffic.Total - } - if clientTraffic.ExpiryTime != traffic.ExpiryTime { - traffic.ExpiryTime = 0 - } - } - } - headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)) - headers = append(headers, "12") - headers = append(headers, subId) - return result, headers, nil -} - -func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) { - db := database.GetDB() - var inbounds []*model.Inbound - err := db.Model(model.Inbound{}).Preload("ClientStats").Where("settings like ? and enable = ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId), true).Find(&inbounds).Error - if err != nil { - return nil, err - } - return inbounds, nil -} - -func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic { - for _, traffic := range traffics { - if traffic.Email == email { - return traffic - } - } - return xray.ClientTraffic{} -} - -func (s *SubService) getLink(inbound *model.Inbound, email string) string { - switch inbound.Protocol { - case "vmess": - return s.genVmessLink(inbound, email) - case "vless": - return s.genVlessLink(inbound, email) - case "trojan": - return s.genTrojanLink(inbound, email) - case "shadowsocks": - return s.genShadowsocksLink(inbound, email) - } - return "" -} - -func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { - if inbound.Protocol != model.VMess { - return "" - } - remark := fmt.Sprintf("%s-%s", inbound.Remark, email) - obj := map[string]interface{}{ - "v": "2", - "ps": remark, - "add": s.address, - "port": inbound.Port, - "type": "none", - } - var stream map[string]interface{} - json.Unmarshal([]byte(inbound.StreamSettings), &stream) - network, _ := stream["network"].(string) - obj["net"] = network - switch network { - case "tcp": - tcp, _ := stream["tcpSettings"].(map[string]interface{}) - header, _ := tcp["header"].(map[string]interface{}) - typeStr, _ := header["type"].(string) - obj["type"] = typeStr - if typeStr == "http" { - request := header["request"].(map[string]interface{}) - requestPath, _ := request["path"].([]interface{}) - obj["path"] = requestPath[0].(string) - headers, _ := request["headers"].(map[string]interface{}) - obj["host"] = searchHost(headers) - } - case "kcp": - kcp, _ := stream["kcpSettings"].(map[string]interface{}) - header, _ := kcp["header"].(map[string]interface{}) - obj["type"], _ = header["type"].(string) - obj["path"], _ = kcp["seed"].(string) - case "ws": - ws, _ := stream["wsSettings"].(map[string]interface{}) - obj["path"] = ws["path"].(string) - headers, _ := ws["headers"].(map[string]interface{}) - obj["host"] = searchHost(headers) - case "http": - obj["net"] = "h2" - http, _ := stream["httpSettings"].(map[string]interface{}) - obj["path"], _ = http["path"].(string) - obj["host"] = searchHost(http) - case "quic": - quic, _ := stream["quicSettings"].(map[string]interface{}) - header := quic["header"].(map[string]interface{}) - obj["type"], _ = header["type"].(string) - obj["host"], _ = quic["security"].(string) - obj["path"], _ = quic["key"].(string) - case "grpc": - grpc, _ := stream["grpcSettings"].(map[string]interface{}) - obj["path"] = grpc["serviceName"].(string) - if grpc["multiMode"].(bool) { - obj["type"] = "multi" - } - } - - security, _ := stream["security"].(string) - obj["tls"] = security - if security == "tls" { - tlsSetting, _ := stream["tlsSettings"].(map[string]interface{}) - alpns, _ := tlsSetting["alpn"].([]interface{}) - if len(alpns) > 0 { - var alpn []string - for _, a := range alpns { - alpn = append(alpn, a.(string)) - } - obj["alpn"] = strings.Join(alpn, ",") - } - tlsSettings, _ := searchKey(tlsSetting, "settings") - if tlsSetting != nil { - if sniValue, ok := searchKey(tlsSettings, "serverName"); ok { - obj["sni"], _ = sniValue.(string) - } - if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok { - obj["fp"], _ = fpValue.(string) - } - if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok { - obj["allowInsecure"], _ = insecure.(bool) - } - } - serverName, _ := tlsSetting["serverName"].(string) - if serverName != "" { - obj["add"] = serverName - } - } - - clients, _ := s.inboundService.getClients(inbound) - clientIndex := -1
contacts: admin@thfree.ru |
