subService.go 41 KB

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