subService.go 35 KB

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