subService.go 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388
  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, ok := externalProxy.(map[string]interface{})
  907. if !ok {
  908. continue
  909. }
  910. dest, _ := ep["dest"].(string)
  911. portF, okPort := ep["port"].(float64)
  912. if dest == "" || !okPort {
  913. continue
  914. }
  915. epRemark, _ := ep["remark"].(string)
  916. link := fmt.Sprintf("%s://%s@%s:%d", protocol, auth, dest, int(portF))
  917. u, _ := url.Parse(link)
  918. q := u.Query()
  919. for k, v := range params {
  920. q.Add(k, v)
  921. }
  922. u.RawQuery = q.Encode()
  923. u.Fragment = s.genRemark(inbound, email, epRemark)
  924. links = append(links, u.String())
  925. }
  926. return strings.Join(links, "\n")
  927. }
  928. // No external proxy configured — fall back to the request host.
  929. link := fmt.Sprintf("%s://%s@%s:%d", protocol, auth, s.address, inbound.Port)
  930. url, _ := url.Parse(link)
  931. q := url.Query()
  932. for k, v := range params {
  933. q.Add(k, v)
  934. }
  935. url.RawQuery = q.Encode()
  936. url.Fragment = s.genRemark(inbound, email, "")
  937. return url.String()
  938. }
  939. func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
  940. separationChar := string(s.remarkModel[0])
  941. orderChars := s.remarkModel[1:]
  942. orders := map[byte]string{
  943. 'i': "",
  944. 'e': "",
  945. 'o': "",
  946. }
  947. if len(email) > 0 {
  948. orders['e'] = email
  949. }
  950. if len(inbound.Remark) > 0 {
  951. orders['i'] = inbound.Remark
  952. }
  953. if len(extra) > 0 {
  954. orders['o'] = extra
  955. }
  956. var remark []string
  957. for i := 0; i < len(orderChars); i++ {
  958. char := orderChars[i]
  959. order, exists := orders[char]
  960. if exists && order != "" {
  961. remark = append(remark, order)
  962. }
  963. }
  964. if s.showInfo {
  965. statsExist := false
  966. var stats xray.ClientTraffic
  967. for _, clientStat := range inbound.ClientStats {
  968. if clientStat.Email == email {
  969. stats = clientStat
  970. statsExist = true
  971. break
  972. }
  973. }
  974. // Get remained days
  975. if statsExist {
  976. if !stats.Enable {
  977. return fmt.Sprintf("⛔️N/A%s%s", separationChar, strings.Join(remark, separationChar))
  978. }
  979. if vol := stats.Total - (stats.Up + stats.Down); vol > 0 {
  980. remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊"))
  981. }
  982. now := time.Now().Unix()
  983. switch exp := stats.ExpiryTime / 1000; {
  984. case exp > 0:
  985. remainingSeconds := exp - now
  986. days := remainingSeconds / 86400
  987. hours := (remainingSeconds % 86400) / 3600
  988. minutes := (remainingSeconds % 3600) / 60
  989. if days > 0 {
  990. if hours > 0 {
  991. remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
  992. } else {
  993. remark = append(remark, fmt.Sprintf("%dD⏳", days))
  994. }
  995. } else if hours > 0 {
  996. remark = append(remark, fmt.Sprintf("%dH⏳", hours))
  997. } else {
  998. remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
  999. }
  1000. case exp < 0:
  1001. days := exp / -86400
  1002. hours := (exp % -86400) / 3600
  1003. minutes := (exp % -3600) / 60
  1004. if days > 0 {
  1005. if hours > 0 {
  1006. remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
  1007. } else {
  1008. remark = append(remark, fmt.Sprintf("%dD⏳", days))
  1009. }
  1010. } else if hours > 0 {
  1011. remark = append(remark, fmt.Sprintf("%dH⏳", hours))
  1012. } else {
  1013. remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
  1014. }
  1015. }
  1016. }
  1017. }
  1018. return strings.Join(remark, separationChar)
  1019. }
  1020. func searchKey(data any, key string) (any, bool) {
  1021. switch val := data.(type) {
  1022. case map[string]any:
  1023. for k, v := range val {
  1024. if k == key {
  1025. return v, true
  1026. }
  1027. if result, ok := searchKey(v, key); ok {
  1028. return result, true
  1029. }
  1030. }
  1031. case []any:
  1032. for _, v := range val {
  1033. if result, ok := searchKey(v, key); ok {
  1034. return result, true
  1035. }
  1036. }
  1037. }
  1038. return nil, false
  1039. }
  1040. // applyXhttpPaddingParams copies the xPadding* fields from an xhttpSettings
  1041. // map into the URL query params of a vless:// / trojan:// / ss:// link.
  1042. //
  1043. // Before this helper existed, only path / host / mode were propagated,
  1044. // so a server configured with a non-default xPaddingBytes (e.g. 80-600)
  1045. // or with xPaddingObfsMode=true + custom xPaddingKey / xPaddingHeader
  1046. // would silently diverge from the client: the client kept defaults,
  1047. // hit the server, and was rejected by its padding validation
  1048. // ("invalid padding" in the inbound log) — the client-visible symptom
  1049. // was "xhttp doesn't connect" on OpenWRT / sing-box.
  1050. //
  1051. // Two encodings are written so every popular client can read at least one:
  1052. //
  1053. // - x_padding_bytes=<range> — flat param, understood by sing-box and its
  1054. // derivatives (Podkop, OpenWRT sing-box, Karing, NekoBox, …).
  1055. // - extra=<url-encoded-json> — full xhttp settings blob, which is how
  1056. // xray-core clients (v2rayNG, Happ, Furious, Exclave, …) pick up the
  1057. // obfs-mode key / header / placement / method.
  1058. //
  1059. // Anything that doesn't map to a non-empty value is skipped, so simple
  1060. // inbounds (no custom padding) produce exactly the same URL as before.
  1061. func applyXhttpPaddingParams(xhttp map[string]any, params map[string]string) {
  1062. if xhttp == nil {
  1063. return
  1064. }
  1065. if xpb, ok := xhttp["xPaddingBytes"].(string); ok && len(xpb) > 0 {
  1066. params["x_padding_bytes"] = xpb
  1067. }
  1068. extra := map[string]any{}
  1069. if xpb, ok := xhttp["xPaddingBytes"].(string); ok && len(xpb) > 0 {
  1070. extra["xPaddingBytes"] = xpb
  1071. }
  1072. if obfs, ok := xhttp["xPaddingObfsMode"].(bool); ok && obfs {
  1073. extra["xPaddingObfsMode"] = true
  1074. // The obfs-mode-only fields: only populate the ones the admin
  1075. // actually set, so xray-core falls back to its own defaults for
  1076. // the rest instead of seeing spurious empty strings.
  1077. for _, field := range []string{"xPaddingKey", "xPaddingHeader", "xPaddingPlacement", "xPaddingMethod"} {
  1078. if v, ok := xhttp[field].(string); ok && len(v) > 0 {
  1079. extra[field] = v
  1080. }
  1081. }
  1082. }
  1083. if len(extra) > 0 {
  1084. if b, err := json.Marshal(extra); err == nil {
  1085. params["extra"] = string(b)
  1086. }
  1087. }
  1088. }
  1089. func searchHost(headers any) string {
  1090. data, _ := headers.(map[string]any)
  1091. for k, v := range data {
  1092. if strings.EqualFold(k, "host") {
  1093. switch v.(type) {
  1094. case []any:
  1095. hosts, _ := v.([]any)
  1096. if len(hosts) > 0 {
  1097. return hosts[0].(string)
  1098. } else {
  1099. return ""
  1100. }
  1101. case any:
  1102. return v.(string)
  1103. }
  1104. }
  1105. }
  1106. return ""
  1107. }
  1108. // PageData is a view model for subpage.html
  1109. // PageData contains data for rendering the subscription information page.
  1110. type PageData struct {
  1111. Host string
  1112. BasePath string
  1113. SId string
  1114. Download string
  1115. Upload string
  1116. Total string
  1117. Used string
  1118. Remained string
  1119. Expire int64
  1120. LastOnline int64
  1121. Datepicker string
  1122. DownloadByte int64
  1123. UploadByte int64
  1124. TotalByte int64
  1125. SubUrl string
  1126. SubJsonUrl string
  1127. SubClashUrl string
  1128. Result []string
  1129. }
  1130. // ResolveRequest extracts scheme and host info from request/headers consistently.
  1131. // ResolveRequest extracts scheme, host, and header information from an HTTP request.
  1132. func (s *SubService) ResolveRequest(c *gin.Context) (scheme string, host string, hostWithPort string, hostHeader string) {
  1133. // scheme
  1134. scheme = "http"
  1135. if c.Request.TLS != nil || strings.EqualFold(c.GetHeader("X-Forwarded-Proto"), "https") {
  1136. scheme = "https"
  1137. }
  1138. // base host (no port)
  1139. if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil && h != "" {
  1140. host = h
  1141. }
  1142. if host == "" {
  1143. host = c.GetHeader("X-Real-IP")
  1144. }
  1145. if host == "" {
  1146. var err error
  1147. host, _, err = net.SplitHostPort(c.Request.Host)
  1148. if err != nil {
  1149. host = c.Request.Host
  1150. }
  1151. }
  1152. // host:port for URLs
  1153. hostWithPort = c.GetHeader("X-Forwarded-Host")
  1154. if hostWithPort == "" {
  1155. hostWithPort = c.Request.Host
  1156. }
  1157. if hostWithPort == "" {
  1158. hostWithPort = host
  1159. }
  1160. // header display host
  1161. hostHeader = c.GetHeader("X-Forwarded-Host")
  1162. if hostHeader == "" {
  1163. hostHeader = c.GetHeader("X-Real-IP")
  1164. }
  1165. if hostHeader == "" {
  1166. hostHeader = host
  1167. }
  1168. return
  1169. }
  1170. // BuildURLs constructs absolute subscription and JSON subscription URLs for a given subscription ID.
  1171. // It prioritizes configured URIs, then individual settings, and finally falls back to request-derived components.
  1172. func (s *SubService) BuildURLs(scheme, hostWithPort, subPath, subJsonPath, subClashPath, subId string) (subURL, subJsonURL, subClashURL string) {
  1173. if subId == "" {
  1174. return "", "", ""
  1175. }
  1176. configuredSubURI, _ := s.settingService.GetSubURI()
  1177. configuredSubJsonURI, _ := s.settingService.GetSubJsonURI()
  1178. configuredSubClashURI, _ := s.settingService.GetSubClashURI()
  1179. var baseScheme, baseHostWithPort string
  1180. if configuredSubURI == "" || configuredSubJsonURI == "" || configuredSubClashURI == "" {
  1181. baseScheme, baseHostWithPort = s.getBaseSchemeAndHost(scheme, hostWithPort)
  1182. }
  1183. subURL = s.buildSingleURL(configuredSubURI, baseScheme, baseHostWithPort, subPath, subId)
  1184. subJsonURL = s.buildSingleURL(configuredSubJsonURI, baseScheme, baseHostWithPort, subJsonPath, subId)
  1185. subClashURL = s.buildSingleURL(configuredSubClashURI, baseScheme, baseHostWithPort, subClashPath, subId)
  1186. return subURL, subJsonURL, subClashURL
  1187. }
  1188. // getBaseSchemeAndHost determines the base scheme and host from settings or falls back to request values
  1189. func (s *SubService) getBaseSchemeAndHost(requestScheme, requestHostWithPort string) (string, string) {
  1190. subDomain, err := s.settingService.GetSubDomain()
  1191. if err != nil || subDomain == "" {
  1192. return requestScheme, requestHostWithPort
  1193. }
  1194. // Get port and TLS settings
  1195. subPort, _ := s.settingService.GetSubPort()
  1196. subKeyFile, _ := s.settingService.GetSubKeyFile()
  1197. subCertFile, _ := s.settingService.GetSubCertFile()
  1198. // Determine scheme from TLS configuration
  1199. scheme := "http"
  1200. if subKeyFile != "" && subCertFile != "" {
  1201. scheme = "https"
  1202. }
  1203. // Build host:port, always include port for clarity
  1204. hostWithPort := fmt.Sprintf("%s:%d", subDomain, subPort)
  1205. return scheme, hostWithPort
  1206. }
  1207. // buildSingleURL constructs a single URL using configured URI or base components
  1208. func (s *SubService) buildSingleURL(configuredURI, baseScheme, baseHostWithPort, basePath, subId string) string {
  1209. if configuredURI != "" {
  1210. return s.joinPathWithID(configuredURI, subId)
  1211. }
  1212. baseURL := fmt.Sprintf("%s://%s", baseScheme, baseHostWithPort)
  1213. return s.joinPathWithID(baseURL+basePath, subId)
  1214. }
  1215. // joinPathWithID safely joins a base path with a subscription ID
  1216. func (s *SubService) joinPathWithID(basePath, subId string) string {
  1217. if strings.HasSuffix(basePath, "/") {
  1218. return basePath + subId
  1219. }
  1220. return basePath + "/" + subId
  1221. }
  1222. // BuildPageData parses header and prepares the template view model.
  1223. // BuildPageData constructs page data for rendering the subscription information page.
  1224. func (s *SubService) BuildPageData(subId string, hostHeader string, traffic xray.ClientTraffic, lastOnline int64, subs []string, subURL, subJsonURL, subClashURL string, basePath string) PageData {
  1225. download := common.FormatTraffic(traffic.Down)
  1226. upload := common.FormatTraffic(traffic.Up)
  1227. total := "∞"
  1228. used := common.FormatTraffic(traffic.Up + traffic.Down)
  1229. remained := ""
  1230. if traffic.Total > 0 {
  1231. total = common.FormatTraffic(traffic.Total)
  1232. left := max(traffic.Total-(traffic.Up+traffic.Down), 0)
  1233. remained = common.FormatTraffic(left)
  1234. }
  1235. datepicker := s.datepicker
  1236. if datepicker == "" {
  1237. datepicker = "gregorian"
  1238. }
  1239. return PageData{
  1240. Host: hostHeader,
  1241. BasePath: basePath,
  1242. SId: subId,
  1243. Download: download,
  1244. Upload: upload,
  1245. Total: total,
  1246. Used: used,
  1247. Remained: remained,
  1248. Expire: traffic.ExpiryTime / 1000,
  1249. LastOnline: lastOnline,
  1250. Datepicker: datepicker,
  1251. DownloadByte: traffic.Down,
  1252. UploadByte: traffic.Up,
  1253. TotalByte: traffic.Total,
  1254. SubUrl: subURL,
  1255. SubJsonUrl: subJsonURL,
  1256. SubClashUrl: subClashURL,
  1257. Result: subs,
  1258. }
  1259. }
  1260. func getHostFromXFH(s string) (string, error) {
  1261. if strings.Contains(s, ":") {
  1262. realHost, _, err := net.SplitHostPort(s)
  1263. if err != nil {
  1264. return "", err
  1265. }
  1266. return realHost, nil
  1267. }
  1268. return s, nil
  1269. }