[feature] separate subscription service
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
This commit is contained in:
@@ -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";
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
+14
-14
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
for i, client := range clients {
|
||||
if client.Email == email {
|
||||
clientIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
obj["id"] = clients[clientIndex].ID
|
||||
obj["aid"] = clients[clientIndex].AlterIds
|
||||
|
||||
jsonStr, _ := json.MarshalIndent(obj, "", " ")
|
||||
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
||||
}
|
||||
|
||||
func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
address := s.address
|
||||
if inbound.Protocol != model.VLESS {
|
||||
return ""
|
||||
}
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
clients, _ := s.inboundService.getClients(inbound)
|
||||
clientIndex := -1
|
||||
for i, client := range clients {
|
||||
if client.Email == email {
|
||||
clientIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
uuid := clients[clientIndex].ID
|
||||
port := inbound.Port
|
||||
streamNetwork := stream["network"].(string)
|
||||
params := make(map[string]string)
|
||||
params["type"] = streamNetwork
|
||||
|
||||
switch streamNetwork {
|
||||
case "tcp":
|
||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
||||
header, _ := tcp["header"].(map[string]interface{})
|
||||
typeStr, _ := header["type"].(string)
|
||||
if typeStr == "http" {
|
||||
request := header["request"].(map[string]interface{})
|
||||
requestPath, _ := request["path"].([]interface{})
|
||||
params["path"] = requestPath[0].(string)
|
||||
headers, _ := request["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
params["headerType"] = "http"
|
||||
}
|
||||
case "kcp":
|
||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
||||
header, _ := kcp["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
params["seed"] = kcp["seed"].(string)
|
||||
case "ws":
|
||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
||||
params["path"] = ws["path"].(string)
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
case "http":
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
params["path"] = http["path"].(string)
|
||||
params["host"] = searchHost(http)
|
||||
case "quic":
|
||||
quic, _ := stream["quicSettings"].(map[string]interface{})
|
||||
params["quicSecurity"] = quic["security"].(string)
|
||||
params["key"] = quic["key"].(string)
|
||||
header := quic["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
params["serviceName"] = grpc["serviceName"].(string)
|
||||
if grpc["multiMode"].(bool) {
|
||||
params["mode"] = "multi"
|
||||
}
|
||||
}
|
||||
|
||||
security, _ := stream["security"].(string)
|
||||
if security == "tls" {
|
||||
params["security"] = "tls"
|
||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||
if tlsSetting != nil {
|
||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := tlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
if security == "reality" {
|
||||
params["security"] = "reality"
|
||||
realitySetting, _ := stream["realitySettings"].(map[string]interface{})
|
||||
realitySettings, _ := searchKey(realitySetting, "settings")
|
||||
if realitySetting != nil {
|
||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
||||
sNames, _ := sniValue.([]interface{})
|
||||
params["sni"], _ = sNames[0].(string)
|
||||
}
|
||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
||||
params["pbk"], _ = pbkValue.(string)
|
||||
}
|
||||
if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
|
||||
shortIds, _ := sidValue.([]interface{})
|
||||
params["sid"], _ = shortIds[0].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
||||
params["fp"] = fp
|
||||
}
|
||||
}
|
||||
if spxValue, ok := searchKey(realitySettings, "spiderX"); ok {
|
||||
if spx, ok := spxValue.(string); ok && len(spx) > 0 {
|
||||
params["spx"] = spx
|
||||
}
|
||||
}
|
||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
||||
address = sname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
}
|
||||
|
||||
if security == "xtls" {
|
||||
params["security"] = "xtls"
|
||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
|
||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||
if xtlsSetting != nil {
|
||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := xtlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
|
||||
url, _ := url.Parse(link)
|
||||
q := url.Query()
|
||||
|
||||
for k, v := range params {
|
||||
q.Add(k, v)
|
||||
}
|
||||
|
||||
// Set the new query values on the URL
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
|
||||
url.Fragment = remark
|
||||
return url.String()
|
||||
}
|
||||
|
||||
func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
|
||||
address := s.address
|
||||
if inbound.Protocol != model.Trojan {
|
||||
return ""
|
||||
}
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
clients, _ := s.inboundService.getClients(inbound)
|
||||
clientIndex := -1
|
||||
for i, client := range clients {
|
||||
if client.Email == email {
|
||||
clientIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
password := clients[clientIndex].Password
|
||||
port := inbound.Port
|
||||
streamNetwork := stream["network"].(string)
|
||||
params := make(map[string]string)
|
||||
params["type"] = streamNetwork
|
||||
|
||||
switch streamNetwork {
|
||||
case "tcp":
|
||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
||||
header, _ := tcp["header"].(map[string]interface{})
|
||||
typeStr, _ := header["type"].(string)
|
||||
if typeStr == "http" {
|
||||
request := header["request"].(map[string]interface{})
|
||||
requestPath, _ := request["path"].([]interface{})
|
||||
params["path"] = requestPath[0].(string)
|
||||
headers, _ := request["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
params["headerType"] = "http"
|
||||
}
|
||||
case "kcp":
|
||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
||||
header, _ := kcp["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
params["seed"] = kcp["seed"].(string)
|
||||
case "ws":
|
||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
||||
params["path"] = ws["path"].(string)
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
case "http":
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
params["path"] = http["path"].(string)
|
||||
params["host"] = searchHost(http)
|
||||
case "quic":
|
||||
quic, _ := stream["quicSettings"].(map[string]interface{})
|
||||
params["quicSecurity"] = quic["security"].(string)
|
||||
params["key"] = quic["key"].(string)
|
||||
header := quic["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
params["serviceName"] = grpc["serviceName"].(string)
|
||||
if grpc["multiMode"].(bool) {
|
||||
params["mode"] = "multi"
|
||||
}
|
||||
}
|
||||
|
||||
security, _ := stream["security"].(string)
|
||||
if security == "tls" {
|
||||
params["security"] = "tls"
|
||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||
if tlsSetting != nil {
|
||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serverName, _ := tlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
if security == "reality" {
|
||||
params["security"] = "reality"
|
||||
realitySetting, _ := stream["realitySettings"].(map[string]interface{})
|
||||
realitySettings, _ := searchKey(realitySetting, "settings")
|
||||
if realitySetting != nil {
|
||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
||||
sNames, _ := sniValue.([]interface{})
|
||||
params["sni"], _ = sNames[0].(string)
|
||||
}
|
||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
||||
params["pbk"], _ = pbkValue.(string)
|
||||
}
|
||||
if sidValue, ok := searchKey(realitySettings, "shortIds"); ok {
|
||||
shortIds, _ := sidValue.([]interface{})
|
||||
params["sid"], _ = shortIds[0].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
||||
params["fp"] = fp
|
||||
}
|
||||
}
|
||||
if spxValue, ok := searchKey(realitySettings, "spiderX"); ok {
|
||||
if spx, ok := spxValue.(string); ok && len(spx) > 0 {
|
||||
params["spx"] = spx
|
||||
}
|
||||
}
|
||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
||||
address = sname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
}
|
||||
|
||||
if security == "xtls" {
|
||||
params["security"] = "xtls"
|
||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
|
||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||
if xtlsSetting != nil {
|
||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := xtlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
|
||||
|
||||
url, _ := url.Parse(link)
|
||||
q := url.Query()
|
||||
|
||||
for k, v := range params {
|
||||
q.Add(k, v)
|
||||
}
|
||||
|
||||
// Set the new query values on the URL
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
|
||||
url.Fragment = remark
|
||||
return url.String()
|
||||
}
|
||||
|
||||
func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string {
|
||||
address := s.address
|
||||
if inbound.Protocol != model.Shadowsocks {
|
||||
return ""
|
||||
}
|
||||
clients, _ := s.inboundService.getClients(inbound)
|
||||
|
||||
var settings map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
||||
inboundPassword := settings["password"].(string)
|
||||
method := settings["method"].(string)
|
||||
clientIndex := -1
|
||||
for i, client := range clients {
|
||||
if client.Email == email {
|
||||
clientIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
encPart := fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
||||
remark := fmt.Sprintf("%s-%s", inbound.Remark, clients[clientIndex].Email)
|
||||
return fmt.Sprintf("ss://%s@%s:%d#%s", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port, remark)
|
||||
}
|
||||
|
||||
func searchKey(data interface{}, key string) (interface{}, bool) {
|
||||
switch val := data.(type) {
|
||||
case map[string]interface{}:
|
||||
for k, v := range val {
|
||||
if k == key {
|
||||
return v, true
|
||||
}
|
||||
if result, ok := searchKey(v, key); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
for _, v := range val {
|
||||
if result, ok := searchKey(v, key); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func searchHost(headers interface{}) string {
|
||||
data, _ := headers.(map[string]interface{})
|
||||
for k, v := range data {
|
||||
if strings.EqualFold(k, "host") {
|
||||
switch v.(type) {
|
||||
case []interface{}:
|
||||
hosts, _ := v.([]interface{})
|
||||
if len(hosts) > 0 {
|
||||
return hosts[0].(string)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
case interface{}:
|
||||
return v.(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -83,7 +83,6 @@ type Server struct {
|
||||
server *controller.ServerController
|
||||
panel *controller.XUIController
|
||||
api *controller.APIController
|
||||
sub *controller.SUBController
|
||||
|
||||
xrayService service.XrayService
|
||||
settingService service.SettingService
|
||||
@@ -242,7 +241,6 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||
s.server = controller.NewServerController(g)
|
||||
s.panel = controller.NewXUIController(g)
|
||||
s.api = controller.NewAPIController(g)
|
||||
s.sub = controller.NewSUBController(g)
|
||||
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user