subService.go 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380
  1. package sub
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "net"
  6. "net/url"
  7. "strings"
  8. "time"
  9. "github.com/gin-gonic/gin"
  10. "github.com/goccy/go-json"
  11. "github.com/mhsanaei/3x-ui/v2/database"
  12. "github.com/mhsanaei/3x-ui/v2/database/model"
  13. "github.com/mhsanaei/3x-ui/v2/logger"
  14. "github.com/mhsanaei/3x-ui/v2/util/common"
  15. "github.com/mhsanaei/3x-ui/v2/util/random"
  16. "github.com/mhsanaei/3x-ui/v2/web/service"
  17. "github.com/mhsanaei/3x-ui/v2/xray"
  18. )
  19. // SubService provides business logic for generating subscription links and managing subscription data.
  20. type SubService struct {
  21. address string
  22. showInfo bool
  23. remarkModel string
  24. datepicker string
  25. inboundService service.InboundService
  26. settingService service.SettingService
  27. }
  28. // NewSubService creates a new subscription service with the given configuration.
  29. func NewSubService(showInfo bool, remarkModel string) *SubService {
  30. return &SubService{
  31. showInfo: showInfo,
  32. remarkModel: remarkModel,
  33. }
  34. }
  35. // GetSubs retrieves subscription links for a given subscription ID and host.
  36. func (s *SubService) GetSubs(subId string, host string) ([]string, int64, xray.ClientTraffic, error) {
  37. s.address = host
  38. var result []string
  39. var traffic xray.ClientTraffic
  40. var lastOnline int64
  41. var clientTraffics []xray.ClientTraffic
  42. inbounds, err := s.getInboundsBySubId(subId)
  43. if err != nil {
  44. return nil, 0, traffic, err
  45. }
  46. if len(inbounds) == 0 {
  47. return nil, 0, traffic, common.NewError("No inbounds found with ", subId)
  48. }
  49. s.datepicker, err = s.settingService.GetDatepicker()
  50. if err != nil {
  51. s.datepicker = "gregorian"
  52. }
  53. for _, inbound := range inbounds {
  54. clients, err := s.inboundService.GetClients(inbound)
  55. if err != nil {
  56. logger.Error("SubService - GetClients: Unable to get clients from inbound")
  57. }
  58. if clients == nil {
  59. continue
  60. }
  61. if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {
  62. listen, port, streamSettings, err := s.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
  63. if err == nil {
  64. inbound.Listen = listen
  65. inbound.Port = port
  66. inbound.StreamSettings = streamSettings
  67. }
  68. }
  69. for _, client := range clients {
  70. if client.Enable && client.SubID == subId {
  71. link := s.getLink(inbound, client.Email)
  72. result = append(result, link)
  73. ct := s.getClientTraffics(inbound.ClientStats, client.Email)
  74. clientTraffics = append(clientTraffics, ct)
  75. if ct.LastOnline > lastOnline {
  76. lastOnline = ct.LastOnline
  77. }
  78. }
  79. }
  80. }
  81. // Prepare statistics
  82. for index, clientTraffic := range clientTraffics {
  83. if index == 0 {
  84. traffic.Up = clientTraffic.Up
  85. traffic.Down = clientTraffic.Down
  86. traffic.Total = clientTraffic.Total
  87. if clientTraffic.ExpiryTime > 0 {
  88. traffic.ExpiryTime = clientTraffic.ExpiryTime
  89. }
  90. } else {
  91. traffic.Up += clientTraffic.Up
  92. traffic.Down += clientTraffic.Down
  93. if traffic.Total == 0 || clientTraffic.Total == 0 {
  94. traffic.Total = 0
  95. } else {
  96. traffic.Total += clientTraffic.Total
  97. }
  98. if clientTraffic.ExpiryTime != traffic.ExpiryTime {
  99. traffic.ExpiryTime = 0
  100. }
  101. }
  102. }
  103. return result, lastOnline, traffic, nil
  104. }
  105. func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
  106. db := database.GetDB()
  107. var inbounds []*model.Inbound
  108. err := db.Model(model.Inbound{}).Preload("ClientStats").Where(`id in (
  109. SELECT DISTINCT inbounds.id
  110. FROM inbounds,
  111. JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
  112. WHERE
  113. protocol in ('vmess','vless','trojan','shadowsocks','hysteria')
  114. AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ?
  115. )`, subId, true).Find(&inbounds).Error
  116. if err != nil {
  117. return nil, err
  118. }
  119. return inbounds, nil
  120. }
  121. func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic {
  122. for _, traffic := range traffics {
  123. if traffic.Email == email {
  124. return traffic
  125. }
  126. }
  127. return xray.ClientTraffic{}
  128. }
  129. func (s *SubService) getFallbackMaster(dest string, streamSettings string) (string, int, string, error) {
  130. db := database.GetDB()
  131. var inbound *model.Inbound
  132. err := db.Model(model.Inbound{}).
  133. Where("JSON_TYPE(settings, '$.fallbacks') = 'array'").
  134. Where("EXISTS (SELECT * FROM json_each(settings, '$.fallbacks') WHERE json_extract(value, '$.dest') = ?)", dest).
  135. Find(&inbound).Error
  136. if err != nil {
  137. return "", 0, "", err
  138. }
  139. var stream map[string]any
  140. json.Unmarshal([]byte(streamSettings), &stream)
  141. var masterStream map[string]any
  142. json.Unmarshal([]byte(inbound.StreamSettings), &masterStream)
  143. stream["security"] = masterStream["security"]
  144. stream["tlsSettings"] = masterStream["tlsSettings"]
  145. stream["externalProxy"] = masterStream["externalProxy"]
  146. modifiedStream, _ := json.MarshalIndent(stream, "", " ")
  147. return inbound.Listen, inbound.Port, string(modifiedStream), nil
  148. }
  149. func (s *SubService) getLink(inbound *model.Inbound, email string) string {
  150. switch inbound.Protocol {
  151. case "vmess":
  152. return s.genVmessLink(inbound, email)
  153. case "vless":
  154. return s.genVlessLink(inbound, email)
  155. case "trojan":
  156. return s.genTrojanLink(inbound, email)
  157. case "shadowsocks":
  158. return s.genShadowsocksLink(inbound, email)
  159. case "hysteria":
  160. return s.genHysteriaLink(inbound, email)
  161. }
  162. return ""
  163. }
  164. func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
  165. if inbound.Protocol != model.VMESS {
  166. return ""
  167. }
  168. var address string
  169. if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
  170. address = s.address
  171. } else {
  172. address = inbound.Listen
  173. }
  174. obj := map[string]any{
  175. "v": "2",
  176. "add": address,
  177. "port": inbound.Port,
  178. "type": "none",
  179. }
  180. var stream map[string]any
  181. json.Unmarshal([]byte(inbound.StreamSettings), &stream)
  182. network, _ := stream["network"].(string)
  183. obj["net"] = network
  184. switch network {
  185. case "tcp":
  186. tcp, _ := stream["tcpSettings"].(map[string]any)
  187. header, _ := tcp["header"].(map[string]any)
  188. typeStr, _ := header["type"].(string)
  189. obj["type"] = typeStr
  190. if typeStr == "http" {
  191. request := header["request"].(map[string]any)
  192. requestPath, _ := request["path"].([]any)
  193. obj["path"] = requestPath[0].(string)
  194. headers, _ := request["headers"].(map[string]any)
  195. obj["host"] = searchHost(headers)
  196. }
  197. case "kcp":
  198. kcp, _ := stream["kcpSettings"].(map[string]any)
  199. header, _ := kcp["header"].(map[string]any)
  200. obj["type"], _ = header["type"].(string)
  201. obj["path"], _ = kcp["seed"].(string)
  202. case "ws":
  203. ws, _ := stream["wsSettings"].(map[string]any)
  204. obj["path"] = ws["path"].(string)
  205. if host, ok := ws["host"].(string); ok && len(host) > 0 {
  206. obj["host"] = host
  207. } else {
  208. headers, _ := ws["headers"].(map[string]any)
  209. obj["host"] = searchHost(headers)
  210. }
  211. case "grpc":
  212. grpc, _ := stream["grpcSettings"].(map[string]any)
  213. obj["path"] = grpc["serviceName"].(string)
  214. obj["authority"] = grpc["authority"].(string)
  215. if grpc["multiMode"].(bool) {
  216. obj["type"] = "multi"
  217. }
  218. case "httpupgrade":
  219. httpupgrade, _ := stream["httpupgradeSettings"].(map[string]any)
  220. obj["path"] = httpupgrade["path"].(string)
  221. if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
  222. obj["host"] = host
  223. } else {
  224. headers, _ := httpupgrade["headers"].(map[string]any)
  225. obj["host"] = searchHost(headers)
  226. }
  227. case "xhttp":
  228. xhttp, _ := stream["xhttpSettings"].(map[string]any)
  229. obj["path"] = xhttp["path"].(string)
  230. if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
  231. obj["host"] = host
  232. } else {
  233. headers, _ := xhttp["headers"].(map[string]any)
  234. obj["host"] = searchHost(headers)
  235. }
  236. obj["mode"], _ = xhttp["mode"].(string)
  237. // VMess base64 JSON supports arbitrary keys; copy the padding
  238. // settings through so clients can match the server's xhttp
  239. // xPaddingBytes range and, when the admin opted into obfs
  240. // mode, the custom key / header / placement / method.
  241. if xpb, ok := xhttp["xPaddingBytes"].(string); ok && len(xpb) > 0 {
  242. obj["x_padding_bytes"] = xpb
  243. }
  244. if obfs, ok := xhttp["xPaddingObfsMode"].(bool); ok && obfs {
  245. obj["xPaddingObfsMode"] = true
  246. for _, field := range []string{"xPaddingKey", "xPaddingHeader", "xPaddingPlacement", "xPaddingMethod"} {
  247. if v, ok := xhttp[field].(string); ok && len(v) > 0 {
  248. obj[field] = v
  249. }
  250. }
  251. }
  252. }
  253. security, _ := stream["security"].(string)
  254. obj["tls"] = security
  255. if security == "tls" {
  256. tlsSetting, _ := stream["tlsSettings"].(map[string]any)
  257. alpns, _ := tlsSetting["alpn"].([]any)
  258. if len(alpns) > 0 {
  259. var alpn []string
  260. for _, a := range alpns {
  261. alpn = append(alpn, a.(string))
  262. }
  263. obj["alpn"] = strings.Join(alpn, ",")
  264. }
  265. if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
  266. obj["sni"], _ = sniValue.(string)
  267. }
  268. tlsSettings, _ := searchKey(tlsSetting, "settings")
  269. if tlsSetting != nil {
  270. if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
  271. obj["fp"], _ = fpValue.(string)
  272. }
  273. }
  274. }
  275. clients, _ := s.inboundService.GetClients(inbound)
  276. clientIndex := -1
  277. for i, client := range clients {
  278. if client.Email == email {
  279. clientIndex = i
  280. break
  281. }
  282. }
  283. obj["id"] = clients[clientIndex].ID
  284. obj["scy"] = clients[clientIndex].Security
  285. externalProxies, _ := stream["externalProxy"].([]any)
  286. if len(externalProxies) > 0 {
  287. links := ""
  288. for index, externalProxy := range externalProxies {
  289. ep, _ := externalProxy.(map[string]any)
  290. newSecurity, _ := ep["forceTls"].(string)
  291. newObj := map[string]any{}
  292. for key, value := range obj {
  293. if !(newSecurity == "none" && (key == "alpn" || key == "sni" || key == "fp")) {
  294. newObj[key] = value
  295. }
  296. }
  297. newObj["ps"] = s.genRemark(inbound, email, ep["remark"].(string))
  298. newObj["add"] = ep["dest"].(string)
  299. newObj["port"] = int(ep["port"].(float64))
  300. if newSecurity != "same" {
  301. newObj["tls"] = newSecurity
  302. }
  303. if index > 0 {
  304. links += "\n"
  305. }
  306. jsonStr, _ := json.MarshalIndent(newObj, "", " ")
  307. links += "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
  308. }
  309. return links
  310. }
  311. obj["ps"] = s.genRemark(inbound, email, "")
  312. jsonStr, _ := json.MarshalIndent(obj, "", " ")
  313. return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
  314. }
  315. func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
  316. var address string
  317. if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
  318. address = s.address
  319. } else {
  320. address = inbound.Listen
  321. }
  322. if inbound.Protocol != model.VLESS {
  323. return ""
  324. }
  325. var stream map[string]any
  326. json.Unmarshal([]byte(inbound.StreamSettings), &stream)
  327. clients, _ := s.inboundService.GetClients(inbound)
  328. clientIndex := -1
  329. for i, client := range clients {
  330. if client.Email == email {
  331. clientIndex = i
  332. break
  333. }
  334. }
  335. uuid := clients[clientIndex].ID
  336. port := inbound.Port
  337. streamNetwork := stream["network"].(string)
  338. params := make(map[string]string)
  339. params["type"] = streamNetwork
  340. // Add encryption parameter for VLESS from inbound settings
  341. var settings map[string]any
  342. json.Unmarshal([]byte(inbound.Settings), &settings)
  343. if encryption, ok := settings["encryption"].(string); ok {
  344. params["encryption"] = encryption
  345. }
  346. switch streamNetwork {
  347. case "tcp":
  348. tcp, _ := stream["tcpSettings"].(map[string]any)
  349. header, _ := tcp["header"].(map[string]any)
  350. typeStr, _ := header["type"].(string)
  351. if typeStr == "http" {
  352. request := header["request"].(map[string]any)
  353. requestPath, _ := request["path"].([]any)
  354. params["path"] = requestPath[0].(string)
  355. headers, _ := request["headers"].(map[string]any)
  356. params["host"] = searchHost(headers)
  357. params["headerType"] = "http"
  358. }
  359. case "kcp":
  360. kcp, _ := stream["kcpSettings"].(map[string]any)
  361. header, _ := kcp["header"].(map[string]any)
  362. params["headerType"] = header["type"].(string)
  363. params["seed"] = kcp["seed"].(string)
  364. case "ws":
  365. ws, _ := stream["wsSettings"].(map[string]any)
  366. params["path"] = ws["path"].(string)
  367. if host, ok := ws["host"].(string); ok && len(host) > 0 {
  368. params["host"] = host
  369. } else {
  370. headers, _ := ws["headers"].(map[string]any)
  371. params["host"] = searchHost(headers)
  372. }
  373. case "grpc":
  374. grpc, _ := stream["grpcSettings"].(map[string]any)
  375. params["serviceName"] = grpc["serviceName"].(string)
  376. params["authority"], _ = grpc["authority"].(string)
  377. if grpc["multiMode"].(bool) {
  378. params["mode"] = "multi"
  379. }
  380. case "httpupgrade":
  381. httpupgrade, _ := stream["httpupgradeSettings"].(map[string]any)
  382. params["path"] = httpupgrade["path"].(string)
  383. if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
  384. params["host"] = host
  385. } else {
  386. headers, _ := httpupgrade["headers"].(map[string]any)
  387. params["host"] = searchHost(headers)
  388. }
  389. case "xhttp":
  390. xhttp, _ := stream["xhttpSettings"].(map[string]any)
  391. params["path"] = xhttp["path"].(string)
  392. if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
  393. params["host"] = host
  394. } else {
  395. headers, _ := xhttp["headers"].(map[string]any)
  396. params["host"] = searchHost(headers)
  397. }
  398. params["mode"], _ = xhttp["mode"].(string)
  399. applyXhttpPaddingParams(xhttp, params)
  400. }
  401. security, _ := stream["security"].(string)
  402. if security == "tls" {
  403. params["security"] = "tls"
  404. tlsSetting, _ := stream["tlsSettings"].(map[string]any)
  405. alpns, _ := tlsSetting["alpn"].([]any)
  406. var alpn []string
  407. for _, a := range alpns {
  408. alpn = append(alpn, a.(string))
  409. }
  410. if len(alpn) > 0 {
  411. params["alpn"] = strings.Join(alpn, ",")
  412. }
  413. if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
  414. params["sni"], _ = sniValue.(string)
  415. }
  416. tlsSettings, _ := searchKey(tlsSetting, "settings")
  417. if tlsSetting != nil {
  418. if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
  419. params["fp"], _ = fpValue.(string)
  420. }
  421. }
  422. if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
  423. params["flow"] = clients[clientIndex].Flow
  424. }
  425. }
  426. if security == "reality" {
  427. params["security"] = "reality"
  428. realitySetting, _ := stream["realitySettings"].(map[string]any)
  429. realitySettings, _ := searchKey(realitySetting, "settings")
  430. if realitySetting != nil {
  431. if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
  432. sNames, _ := sniValue.([]any)
  433. params["sni"] = sNames[random.Num(len(sNames))].(string)
  434. }
  435. if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
  436. params["pbk"], _ = pbkValue.(string)
  437. }
  438. if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
  439. shortIds, _ := sidValue.([]any)
  440. params["sid"] = shortIds[random.Num(len(shortIds))].(string)
  441. }
  442. if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
  443. if fp, ok := fpValue.(string); ok && len(fp) > 0 {
  444. params["fp"] = fp
  445. }
  446. }
  447. if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok {
  448. if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 {
  449. params["pqv"] = pqv
  450. }
  451. }
  452. params["spx"] = "/" + random.Seq(15)
  453. }
  454. if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
  455. params["flow"] = clients[clientIndex].Flow
  456. }
  457. }
  458. if security != "tls" && security != "reality" {
  459. params["security"] = "none"
  460. }
  461. externalProxies, _ := stream["externalProxy"].([]any)
  462. if len(externalProxies) > 0 {
  463. links := make([]string, 0, len(externalProxies))
  464. for _, externalProxy := range externalProxies {
  465. ep, _ := externalProxy.(map[string]any)
  466. newSecurity, _ := ep["forceTls"].(string)
  467. dest, _ := ep["dest"].(string)
  468. port := int(ep["port"].(float64))
  469. link := fmt.Sprintf("vless://%s@%s:%d", uuid, dest, port)
  470. if newSecurity != "same" {
  471. params["security"] = newSecurity
  472. } else {
  473. params["security"] = security
  474. }
  475. url, _ := url.Parse(link)
  476. q := url.Query()
  477. for k, v := range params {
  478. if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp")) {
  479. q.Add(k, v)
  480. }
  481. }
  482. // Set the new query values on the URL
  483. url.RawQuery = q.Encode()
  484. url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
  485. links = append(links, url.String())
  486. }
  487. return strings.Join(links, "\n")
  488. }
  489. link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
  490. url, _ := url.Parse(link)
  491. q := url.Query()
  492. for k, v := range params {
  493. q.Add(k, v)
  494. }
  495. // Set the new query values on the URL
  496. url.RawQuery = q.Encode()
  497. url.Fragment = s.genRemark(inbound, email, "")
  498. return url.String()
  499. }
  500. func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
  501. var address string
  502. if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
  503. address = s.address
  504. } else {
  505. address = inbound.Listen
  506. }
  507. if inbound.Protocol != model.Trojan {
  508. return ""
  509. }
  510. var stream map[string]any
  511. json.Unmarshal([]byte(inbound.StreamSettings), &stream)
  512. clients, _ := s.inboundService.GetClients(inbound)
  513. clientIndex := -1
  514. for i, client := range clients {
  515. if client.Email == email {
  516. clientIndex = i
  517. break
  518. }
  519. }
  520. password := clients[clientIndex].Password
  521. port := inbound.Port
  522. streamNetwork := stream["network"].(string)
  523. params := make(map[string]string)
  524. params["type"] = streamNetwork
  525. switch streamNetwork {
  526. case "tcp":
  527. tcp, _ := stream["tcpSettings"].(map[string]any)
  528. header, _ := tcp["header"].(map[string]any)
  529. typeStr, _ := header["type"].(string)
  530. if typeStr == "http" {
  531. request := header["request"].(map[string]any)
  532. requestPath, _ := request["path"].([]any)
  533. params["path"] = requestPath[0].(string)
  534. headers, _ := request["headers"].(map[string]any)
  535. params["host"] = searchHost(headers)
  536. params["headerType"] = "http"
  537. }
  538. case "kcp":
  539. kcp, _ := stream["kcpSettings"].(map[string]any)
  540. header, _ := kcp["header"].(map[string]any)
  541. params["headerType"] = header["type"].(string)
  542. params["seed"] = kcp["seed"].(string)
  543. case "ws":
  544. ws, _ := stream["wsSettings"].(map[string]any)
  545. params["path"] = ws["path"].(string)
  546. if host, ok := ws["host"].(string); ok && len(host) > 0 {
  547. params["host"] = host
  548. } else {
  549. headers, _ := ws["headers"].(map[string]any)
  550. params["host"] = searchHost(headers)
  551. }
  552. case "grpc":
  553. grpc, _ := stream["grpcSettings"].(map[string]any)
  554. params["serviceName"] = grpc["serviceName"].(string)
  555. params["authority"], _ = grpc["authority"].(string)
  556. if grpc["multiMode"].(bool) {
  557. params["mode"] = "multi"
  558. }
  559. case "httpupgrade":
  560. httpupgrade, _ := stream["httpupgradeSettings"].(map[string]any)
  561. params["path"] = httpupgrade["path"].(string)
  562. if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
  563. params["host"] = host
  564. } else {
  565. headers, _ := httpupgrade["headers"].(map[string]any)
  566. params["host"] = searchHost(headers)
  567. }
  568. case "xhttp":
  569. xhttp, _ := stream["xhttpSettings"].(map[string]any)
  570. params["path"] = xhttp["path"].(string)
  571. if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
  572. params["host"] = host
  573. } else {
  574. headers, _ := xhttp["headers"].(map[string]any)
  575. params["host"] = searchHost(headers)
  576. }
  577. params["mode"], _ = xhttp["mode"].(string)
  578. applyXhttpPaddingParams(xhttp, params)
  579. }
  580. security, _ := stream["security"].(string)
  581. if security == "tls" {
  582. params["security"] = "tls"
  583. tlsSetting, _ := stream["tlsSettings"].(map[string]any)
  584. alpns, _ := tlsSetting["alpn"].([]any)
  585. var alpn []string
  586. for _, a := range alpns {
  587. alpn = append(alpn, a.(string))
  588. }
  589. if len(alpn) > 0 {
  590. params["alpn"] = strings.Join(alpn, ",")
  591. }
  592. if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
  593. params["sni"], _ = sniValue.(string)
  594. }
  595. tlsSettings, _ := searchKey(tlsSetting, "settings")
  596. if tlsSetting != nil {
  597. if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
  598. params["fp"], _ = fpValue.(string)
  599. }
  600. }
  601. }
  602. if security == "reality" {
  603. params["security"] = "reality"
  604. realitySetting, _ := stream["realitySettings"].(map[string]any)
  605. realitySettings, _ := searchKey(realitySetting, "settings")
  606. if realitySetting != nil {
  607. if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
  608. sNames, _ := sniValue.([]any)
  609. params["sni"] = sNames[random.Num(len(sNames))].(string)
  610. }
  611. if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
  612. params["pbk"], _ = pbkValue.(string)
  613. }
  614. if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
  615. shortIds, _ := sidValue.([]any)
  616. params["sid"] = shortIds[random.Num(len(shortIds))].(string)
  617. }
  618. if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
  619. if fp, ok := fpValue.(string); ok && len(fp) > 0 {
  620. params["fp"] = fp
  621. }
  622. }
  623. if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok {
  624. if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 {
  625. params["pqv"] = pqv
  626. }
  627. }
  628. params["spx"] = "/" + random.Seq(15)
  629. }
  630. if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
  631. params["flow"] = clients[clientIndex].Flow
  632. }
  633. }
  634. if security != "tls" && security != "reality" {
  635. params["security"] = "none"
  636. }
  637. externalProxies, _ := stream["externalProxy"].([]any)
  638. if len(externalProxies) > 0 {
  639. links := ""
  640. for index, externalProxy := range externalProxies {
  641. ep, _ := externalProxy.(map[string]any)
  642. newSecurity, _ := ep["forceTls"].(string)
  643. dest, _ := ep["dest"].(string)
  644. port := int(ep["port"].(float64))
  645. link := fmt.Sprintf("trojan://%s@%s:%d", password, dest, port)
  646. if newSecurity != "same" {
  647. params["security"] = newSecurity
  648. } else {
  649. params["security"] = security
  650. }
  651. url, _ := url.Parse(link)
  652. q := url.Query()
  653. for k, v := range params {
  654. if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp")) {
  655. q.Add(k, v)
  656. }
  657. }
  658. // Set the new query values on the URL
  659. url.RawQuery = q.Encode()
  660. url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
  661. if index > 0 {
  662. links += "\n"
  663. }
  664. links += url.String()
  665. }
  666. return links
  667. }
  668. link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
  669. url, _ := url.Parse(link)
  670. q := url.Query()
  671. for k, v := range params {
  672. q.Add(k, v)
  673. }
  674. // Set the new query values on the URL
  675. url.RawQuery = q.Encode()
  676. url.Fragment = s.genRemark(inbound, email, "")
  677. return url.String()
  678. }
  679. func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string {
  680. var address string
  681. if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
  682. address = s.address
  683. } else {
  684. address = inbound.Listen
  685. }
  686. if inbound.Protocol != model.Shadowsocks {
  687. return ""
  688. }
  689. var stream map[string]any
  690. json.Unmarshal([]byte(inbound.StreamSettings), &stream)
  691. clients, _ := s.inboundService.GetClients(inbound)
  692. var settings map[string]any
  693. json.Unmarshal([]byte(inbound.Settings), &settings)
  694. inboundPassword := settings["password"].(string)
  695. method := settings["method"].(string)
  696. clientIndex := -1
  697. for i, client := range clients {
  698. if client.Email == email {
  699. clientIndex = i
  700. break
  701. }
  702. }
  703. streamNetwork := stream["network"].(string)
  704. params := make(map[string]string)
  705. params["type"] = streamNetwork
  706. switch streamNetwork {
  707. case "tcp":
  708. tcp, _ := stream["tcpSettings"].(map[string]any)
  709. header, _ := tcp["header"].(map[string]any)
  710. typeStr, _ := header["type"].(string)
  711. if typeStr == "http" {
  712. request := header["request"].(map[string]any)
  713. requestPath, _ := request["path"].([]any)
  714. params["path"] = requestPath[0].(string)
  715. headers, _ := request["headers"].(map[string]any)
  716. params["host"] = searchHost(headers)
  717. params["headerType"] = "http"
  718. }
  719. case "kcp":
  720. kcp, _ := stream["kcpSettings"].(map[string]any)
  721. header, _ := kcp["header"].(map[string]any)
  722. params["headerType"] = header["type"].(string)
  723. params["seed"] = kcp["seed"].(string)
  724. case "ws":
  725. ws, _ := stream["wsSettings"].(map[string]any)
  726. params["path"] = ws["path"].(string)
  727. if host, ok := ws["host"].(string); ok && len(host) > 0 {
  728. params["host"] = host
  729. } else {
  730. headers, _ := ws["headers"].(map[string]any)
  731. params["host"] = searchHost(headers)
  732. }
  733. case "grpc":
  734. grpc, _ := stream["grpcSettings"].(map[string]any)
  735. params["serviceName"] = grpc["serviceName"].(string)
  736. params["authority"], _ = grpc["authority"].(string)
  737. if grpc["multiMode"].(bool) {
  738. params["mode"] = "multi"
  739. }
  740. case "httpupgrade":
  741. httpupgrade, _ := stream["httpupgradeSettings"].(map[string]any)
  742. params["path"] = httpupgrade["path"].(string)
  743. if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
  744. params["host"] = host
  745. } else {
  746. headers, _ := httpupgrade["headers"].(map[string]any)
  747. params["host"] = searchHost(headers)
  748. }
  749. case "xhttp":
  750. xhttp, _ := stream["xhttpSettings"].(map[string]any)
  751. params["path"] = xhttp["path"].(string)
  752. if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
  753. params["host"] = host
  754. } else {
  755. headers, _ := xhttp["headers"].(map[string]any)
  756. params["host"] = searchHost(headers)
  757. }
  758. params["mode"], _ = xhttp["mode"].(string)
  759. applyXhttpPaddingParams(xhttp, params)
  760. }
  761. security, _ := stream["security"].(string)
  762. if security == "tls" {
  763. params["security"] = "tls"
  764. tlsSetting, _ := stream["tlsSettings"].(map[string]any)
  765. alpns, _ := tlsSetting["alpn"].([]any)
  766. var alpn []string
  767. for _, a := range alpns {
  768. alpn = append(alpn, a.(string))
  769. }
  770. if len(alpn) > 0 {
  771. params["alpn"] = strings.Join(alpn, ",")
  772. }
  773. if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
  774. params["sni"], _ = sniValue.(string)
  775. }
  776. tlsSettings, _ := searchKey(tlsSetting, "settings")
  777. if tlsSetting != nil {
  778. if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
  779. params["fp"], _ = fpValue.(string)
  780. }
  781. }
  782. }
  783. encPart := fmt.Sprintf("%s:%s", method, clients[clientIndex].Password)
  784. if method[0] == '2' {
  785. encPart = fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
  786. }
  787. externalProxies, _ := stream["externalProxy"].([]any)
  788. if len(externalProxies) > 0 {
  789. links := ""
  790. for index, externalProxy := range externalProxies {
  791. ep, _ := externalProxy.(map[string]any)
  792. newSecurity, _ := ep["forceTls"].(string)
  793. dest, _ := ep["dest"].(string)
  794. port := int(ep["port"].(float64))
  795. link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), dest, port)
  796. if newSecurity != "same" {
  797. params["security"] = newSecurity
  798. } else {
  799. params["security"] = security
  800. }
  801. url, _ := url.Parse(link)
  802. q := url.Query()
  803. for k, v := range params {
  804. if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp")) {
  805. q.Add(k, v)
  806. }
  807. }
  808. // Set the new query values on the URL
  809. url.RawQuery = q.Encode()
  810. url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
  811. if index > 0 {
  812. links += "\n"
  813. }
  814. links += url.String()
  815. }
  816. return links
  817. }
  818. link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
  819. url, _ := url.Parse(link)
  820. q := url.Query()
  821. for k, v := range params {
  822. q.Add(k, v)
  823. }
  824. // Set the new query values on the URL
  825. url.RawQuery = q.Encode()
  826. url.Fragment = s.genRemark(inbound, email, "")
  827. return url.String()
  828. }
  829. func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) string {
  830. if inbound.Protocol != model.Hysteria {
  831. return ""
  832. }
  833. var stream map[string]interface{}
  834. json.Unmarshal([]byte(inbound.StreamSettings), &stream)
  835. clients, _ := s.inboundService.GetClients(inbound)
  836. clientIndex := -1
  837. for i, client := range clients {
  838. if client.Email == email {
  839. clientIndex = i
  840. break
  841. }
  842. }
  843. auth := clients[clientIndex].Auth
  844. params := make(map[string]string)
  845. params["security"] = "tls"
  846. tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
  847. alpns, _ := tlsSetting["alpn"].([]interface{})
  848. var alpn []string
  849. for _, a := range alpns {
  850. alpn = append(alpn, a.(string))
  851. }
  852. if len(alpn) > 0 {
  853. params["alpn"] = strings.Join(alpn, ",")
  854. }
  855. if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
  856. params["sni"], _ = sniValue.(string)
  857. }
  858. tlsSettings, _ := searchKey(tlsSetting, "settings")
  859. if tlsSetting != nil {
  860. if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
  861. params["fp"], _ = fpValue.(string)
  862. }
  863. if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
  864. if insecure.(bool) {
  865. params["insecure"] = "1"
  866. }
  867. }
  868. }
  869. // salamander obfs (Hysteria2). The panel-side link generator already
  870. // emits these; keep the subscription output in sync so a client has
  871. // the obfs password to match the server.
  872. if finalmask, ok := stream["finalmask"].(map[string]interface{}); ok {
  873. if udpMasks, ok := finalmask["udp"].([]interface{}); ok {
  874. for _, m := range udpMasks {
  875. mask, _ := m.(map[string]interface{})
  876. if mask == nil || mask["type"] != "salamander" {
  877. continue
  878. }
  879. settings, _ := mask["settings"].(map[string]interface{})
  880. if pw, ok := settings["password"].(string); ok && pw != "" {
  881. params["obfs"] = "salamander"
  882. params["obfs-password"] = pw
  883. break
  884. }
  885. }
  886. }
  887. }
  888. var settings map[string]interface{}
  889. json.Unmarshal([]byte(inbound.Settings), &settings)
  890. version, _ := settings["version"].(float64)
  891. protocol := "hysteria2"
  892. if int(version) == 1 {
  893. protocol = "hysteria"
  894. }
  895. // Fan out one link per External Proxy entry if any. Previously this
  896. // generator ignored `externalProxy` entirely, so the link kept the
  897. // server's own IP/port even when the admin configured an alternate
  898. // endpoint (e.g. a CDN hostname + port that forwards to the node).
  899. // Matches the behaviour of genVlessLink / genTrojanLink / ….
  900. externalProxies, _ := stream["externalProxy"].([]interface{})
  901. if len(externalProxies) > 0 {
  902. links := make([]string, 0, len(externalProxies))
  903. for _, externalProxy := range externalProxies {
  904. ep, _ := externalProxy.(map[string]interface{})
  905. dest, _ := ep["dest"].(string)
  906. epPort := int(ep["port"].(float64))
  907. epRemark, _ := ep["remark"].(string)
  908. link := fmt.Sprintf("%s://%s@%s:%d", protocol, auth, dest, epPort)
  909. u, _ := url.Parse(link)
  910. q := u.Query()
  911. for k, v := range params {
  912. q.Add(k, v)
  913. }
  914. u.RawQuery = q.Encode()
  915. u.Fragment = s.genRemark(inbound, email, epRemark)
  916. links = append(links, u.String())
  917. }
  918. return strings.Join(links, "\n")
  919. }
  920. // No external proxy configured — fall back to the request host.
  921. link := fmt.Sprintf("%s://%s@%s:%d", protocol, auth, s.address, inbound.Port)
  922. url, _ := url.Parse(link)
  923. q := url.Query()
  924. for k, v := range params {
  925. q.Add(k, v)
  926. }
  927. url.RawQuery = q.Encode()
  928. url.Fragment = s.genRemark(inbound, email, "")
  929. return url.String()
  930. }
  931. func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
  932. separationChar := string(s.remarkModel[0])
  933. orderChars := s.remarkModel[1:]
  934. orders := map[byte]string{
  935. 'i': "",
  936. 'e': "",
  937. 'o': "",
  938. }
  939. if len(email) > 0 {
  940. orders['e'] = email
  941. }
  942. if len(inbound.Remark) > 0 {
  943. orders['i'] = inbound.Remark
  944. }
  945. if len(extra) > 0 {
  946. orders['o'] = extra
  947. }
  948. var remark []string
  949. for i := 0; i < len(orderChars); i++ {
  950. char := orderChars[i]
  951. order, exists := orders[char]
  952. if exists && order != "" {
  953. remark = append(remark, order)
  954. }
  955. }
  956. if s.showInfo {
  957. statsExist := false
  958. var stats xray.ClientTraffic
  959. for _, clientStat := range inbound.ClientStats {
  960. if clientStat.Email == email {
  961. stats = clientStat
  962. statsExist = true
  963. break
  964. }
  965. }
  966. // Get remained days
  967. if statsExist {
  968. if !stats.Enable {
  969. return fmt.Sprintf("⛔️N/A%s%s", separationChar, strings.Join(remark, separationChar))
  970. }
  971. if vol := stats.Total - (stats.Up + stats.Down); vol > 0 {
  972. remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊"))
  973. }
  974. now := time.Now().Unix()
  975. switch exp := stats.ExpiryTime / 1000; {
  976. case exp > 0:
  977. remainingSeconds := exp - now
  978. days := remainingSeconds / 86400
  979. hours := (remainingSeconds % 86400) / 3600
  980. minutes := (remainingSeconds % 3600) / 60
  981. if days > 0 {
  982. if hours > 0 {
  983. remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
  984. } else {
  985. remark = append(remark, fmt.Sprintf("%dD⏳", days))
  986. }
  987. } else if hours > 0 {
  988. remark = append(remark, fmt.Sprintf("%dH⏳", hours))
  989. } else {
  990. remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
  991. }
  992. case exp < 0:
  993. days := exp / -86400
  994. hours := (exp % -86400) / 3600
  995. minutes := (exp % -3600) / 60
  996. if days > 0 {
  997. if hours > 0 {
  998. remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
  999. } else {
  1000. remark = append(remark, fmt.Sprintf("%dD⏳", days))
  1001. }
  1002. } else if hours > 0 {
  1003. remark = append(remark, fmt.Sprintf("%dH⏳", hours))
  1004. } else {
  1005. remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
  1006. }
  1007. }
  1008. }
  1009. }
  1010. return strings.Join(remark, separationChar)
  1011. }
  1012. func searchKey(data any, key string) (any, bool) {
  1013. switch val := data.(type) {
  1014. case map[string]any:
  1015. for k, v := range val {
  1016. if k == key {
  1017. return v, true
  1018. }
  1019. if result, ok := searchKey(v, key); ok {
  1020. return result, true
  1021. }
  1022. }
  1023. case []any:
  1024. for _, v := range val {
  1025. if result, ok := searchKey(v, key); ok {
  1026. return result, true
  1027. }
  1028. }
  1029. }
  1030. return nil, false
  1031. }
  1032. // applyXhttpPaddingParams copies the xPadding* fields from an xhttpSettings
  1033. // map into the URL query params of a vless:// / trojan:// / ss:// link.
  1034. //
  1035. // Before this helper existed, only path / host / mode were propagated,
  1036. // so a server configured with a non-default xPaddingBytes (e.g. 80-600)
  1037. // or with xPaddingObfsMode=true + custom xPaddingKey / xPaddingHeader
  1038. // would silently diverge from the client: the client kept defaults,
  1039. // hit the server, and was rejected by its padding validation
  1040. // ("invalid padding" in the inbound log) — the client-visible symptom
  1041. // was "xhttp doesn't connect" on OpenWRT / sing-box.
  1042. //
  1043. // Two encodings are written so every popular client can read at least one:
  1044. //
  1045. // - x_padding_bytes=<range> — flat param, understood by sing-box and its
  1046. // derivatives (Podkop, OpenWRT sing-box, Karing, NekoBox, …).
  1047. // - extra=<url-encoded-json> — full xhttp settings blob, which is how
  1048. // xray-core clients (v2rayNG, Happ, Furious, Exclave, …) pick up the
  1049. // obfs-mode key / header / placement / method.
  1050. //
  1051. // Anything that doesn't map to a non-empty value is skipped, so simple
  1052. // inbounds (no custom padding) produce exactly the same URL as before.
  1053. func applyXhttpPaddingParams(xhttp map[string]any, params map[string]string) {
  1054. if xhttp == nil {
  1055. return
  1056. }
  1057. if xpb, ok := xhttp["xPaddingBytes"].(string); ok && len(xpb) > 0 {
  1058. params["x_padding_bytes"] = xpb
  1059. }
  1060. extra := map[string]any{}
  1061. if xpb, ok := xhttp["xPaddingBytes"].(string); ok && len(xpb) > 0 {
  1062. extra["xPaddingBytes"] = xpb
  1063. }
  1064. if obfs, ok := xhttp["xPaddingObfsMode"].(bool); ok && obfs {
  1065. extra["xPaddingObfsMode"] = true
  1066. // The obfs-mode-only fields: only populate the ones the admin
  1067. // actually set, so xray-core falls back to its own defaults for
  1068. // the rest instead of seeing spurious empty strings.
  1069. for _, field := range []string{"xPaddingKey", "xPaddingHeader", "xPaddingPlacement", "xPaddingMethod"} {
  1070. if v, ok := xhttp[field].(string); ok && len(v) > 0 {
  1071. extra[field] = v
  1072. }
  1073. }
  1074. }
  1075. if len(extra) > 0 {
  1076. if b, err := json.Marshal(extra); err == nil {
  1077. params["extra"] = string(b)
  1078. }
  1079. }
  1080. }
  1081. func searchHost(headers any) string {
  1082. data, _ := headers.(map[string]any)
  1083. for k, v := range data {
  1084. if strings.EqualFold(k, "host") {
  1085. switch v.(type) {
  1086. case []any:
  1087. hosts, _ := v.([]any)
  1088. if len(hosts) > 0 {
  1089. return hosts[0].(string)
  1090. } else {
  1091. return ""
  1092. }
  1093. case any:
  1094. return v.(string)
  1095. }
  1096. }
  1097. }
  1098. return ""
  1099. }
  1100. // PageData is a view model for subpage.html
  1101. // PageData contains data for rendering the subscription information page.
  1102. type PageData struct {
  1103. Host string
  1104. BasePath string
  1105. SId string
  1106. Download string
  1107. Upload string
  1108. Total string
  1109. Used string
  1110. Remained string
  1111. Expire int64
  1112. LastOnline int64
  1113. Datepicker string
  1114. DownloadByte int64
  1115. UploadByte int64
  1116. TotalByte int64
  1117. SubUrl string
  1118. SubJsonUrl string
  1119. SubClashUrl string
  1120. Result []string
  1121. }
  1122. // ResolveRequest extracts scheme and host info from request/headers consistently.
  1123. // ResolveRequest extracts scheme, host, and header information from an HTTP request.
  1124. func (s *SubService) ResolveRequest(c *gin.Context) (scheme string, host string, hostWithPort string, hostHeader string) {
  1125. // scheme
  1126. scheme = "http"
  1127. if c.Request.TLS != nil || strings.EqualFold(c.GetHeader("X-Forwarded-Proto"), "https") {
  1128. scheme = "https"
  1129. }
  1130. // base host (no port)
  1131. if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil && h != "" {
  1132. host = h
  1133. }
  1134. if host == "" {
  1135. host = c.GetHeader("X-Real-IP")
  1136. }
  1137. if host == "" {
  1138. var err error
  1139. host, _, err = net.SplitHostPort(c.Request.Host)
  1140. if err != nil {
  1141. host = c.Request.Host
  1142. }
  1143. }
  1144. // host:port for URLs
  1145. hostWithPort = c.GetHeader("X-Forwarded-Host")
  1146. if hostWithPort == "" {
  1147. hostWithPort = c.Request.Host
  1148. }
  1149. if hostWithPort == "" {
  1150. hostWithPort = host
  1151. }
  1152. // header display host
  1153. hostHeader = c.GetHeader("X-Forwarded-Host")
  1154. if hostHeader == "" {
  1155. hostHeader = c.GetHeader("X-Real-IP")
  1156. }
  1157. if hostHeader == "" {
  1158. hostHeader = host
  1159. }
  1160. return
  1161. }
  1162. // BuildURLs constructs absolute subscription and JSON subscription URLs for a given subscription ID.
  1163. // It prioritizes configured URIs, then individual settings, and finally falls back to request-derived components.
  1164. func (s *SubService) BuildURLs(scheme, hostWithPort, subPath, subJsonPath, subClashPath, subId string) (subURL, subJsonURL, subClashURL string) {
  1165. if subId == "" {
  1166. return "", "", ""
  1167. }
  1168. configuredSubURI, _ := s.settingService.GetSubURI()
  1169. configuredSubJsonURI, _ := s.settingService.GetSubJsonURI()
  1170. configuredSubClashURI, _ := s.settingService.GetSubClashURI()
  1171. var baseScheme, baseHostWithPort string
  1172. if configuredSubURI == "" || configuredSubJsonURI == "" || configuredSubClashURI == "" {
  1173. baseScheme, baseHostWithPort = s.getBaseSchemeAndHost(scheme, hostWithPort)
  1174. }
  1175. subURL = s.buildSingleURL(configuredSubURI, baseScheme, baseHostWithPort, subPath, subId)
  1176. subJsonURL = s.buildSingleURL(configuredSubJsonURI, baseScheme, baseHostWithPort, subJsonPath, subId)
  1177. subClashURL = s.buildSingleURL(configuredSubClashURI, baseScheme, baseHostWithPort, subClashPath, subId)
  1178. return subURL, subJsonURL, subClashURL
  1179. }
  1180. // getBaseSchemeAndHost determines the base scheme and host from settings or falls back to request values
  1181. func (s *SubService) getBaseSchemeAndHost(requestScheme, requestHostWithPort string) (string, string) {
  1182. subDomain, err := s.settingService.GetSubDomain()
  1183. if err != nil || subDomain == "" {
  1184. return requestScheme, requestHostWithPort
  1185. }
  1186. // Get port and TLS settings
  1187. subPort, _ := s.settingService.GetSubPort()
  1188. subKeyFile, _ := s.settingService.GetSubKeyFile()
  1189. subCertFile, _ := s.settingService.GetSubCertFile()
  1190. // Determine scheme from TLS configuration
  1191. scheme := "http"
  1192. if subKeyFile != "" && subCertFile != "" {
  1193. scheme = "https"
  1194. }
  1195. // Build host:port, always include port for clarity
  1196. hostWithPort := fmt.Sprintf("%s:%d", subDomain, subPort)
  1197. return scheme, hostWithPort
  1198. }
  1199. // buildSingleURL constructs a single URL using configured URI or base components
  1200. func (s *SubService) buildSingleURL(configuredURI, baseScheme, baseHostWithPort, basePath, subId string) string {
  1201. if configuredURI != "" {
  1202. return s.joinPathWithID(configuredURI, subId)
  1203. }
  1204. baseURL := fmt.Sprintf("%s://%s", baseScheme, baseHostWithPort)
  1205. return s.joinPathWithID(baseURL+basePath, subId)
  1206. }
  1207. // joinPathWithID safely joins a base path with a subscription ID
  1208. func (s *SubService) joinPathWithID(basePath, subId string) string {
  1209. if strings.HasSuffix(basePath, "/") {
  1210. return basePath + subId
  1211. }
  1212. return basePath + "/" + subId
  1213. }
  1214. // BuildPageData parses header and prepares the template view model.
  1215. // BuildPageData constructs page data for rendering the subscription information page.
  1216. func (s *SubService) BuildPageData(subId string, hostHeader string, traffic xray.ClientTraffic, lastOnline int64, subs []string, subURL, subJsonURL, subClashURL string, basePath string) PageData {
  1217. download := common.FormatTraffic(traffic.Down)
  1218. upload := common.FormatTraffic(traffic.Up)
  1219. total := "∞"
  1220. used := common.FormatTraffic(traffic.Up + traffic.Down)
  1221. remained := ""
  1222. if traffic.Total > 0 {
  1223. total = common.FormatTraffic(traffic.Total)
  1224. left := max(traffic.Total-(traffic.Up+traffic.Down), 0)
  1225. remained = common.FormatTraffic(left)
  1226. }
  1227. datepicker := s.datepicker
  1228. if datepicker == "" {
  1229. datepicker = "gregorian"
  1230. }
  1231. return PageData{
  1232. Host: hostHeader,
  1233. BasePath: basePath,
  1234. SId: subId,
  1235. Download: download,
  1236. Upload: upload,
  1237. Total: total,
  1238. Used: used,
  1239. Remained: remained,
  1240. Expire: traffic.ExpiryTime / 1000,
  1241. LastOnline: lastOnline,
  1242. Datepicker: datepicker,
  1243. DownloadByte: traffic.Down,
  1244. UploadByte: traffic.Up,
  1245. TotalByte: traffic.Total,
  1246. SubUrl: subURL,
  1247. SubJsonUrl: subJsonURL,
  1248. SubClashUrl: subClashURL,
  1249. Result: subs,
  1250. }
  1251. }
  1252. func getHostFromXFH(s string) (string, error) {
  1253. if strings.Contains(s, ":") {
  1254. realHost, _, err := net.SplitHostPort(s)
  1255. if err != nil {
  1256. return "", err
  1257. }
  1258. return realHost, nil
  1259. }
  1260. return s, nil
  1261. }