1
0

inbound.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. package controller
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strconv"
  6. "time"
  7. "github.com/mhsanaei/3x-ui/v2/database/model"
  8. "github.com/mhsanaei/3x-ui/v2/web/service"
  9. "github.com/mhsanaei/3x-ui/v2/web/session"
  10. "github.com/mhsanaei/3x-ui/v2/web/websocket"
  11. "github.com/gin-gonic/gin"
  12. )
  13. // InboundController handles HTTP requests related to Xray inbounds management.
  14. type InboundController struct {
  15. inboundService service.InboundService
  16. xrayService service.XrayService
  17. }
  18. // NewInboundController creates a new InboundController and sets up its routes.
  19. func NewInboundController(g *gin.RouterGroup) *InboundController {
  20. a := &InboundController{}
  21. a.initRouter(g)
  22. return a
  23. }
  24. // initRouter initializes the routes for inbound-related operations.
  25. func (a *InboundController) initRouter(g *gin.RouterGroup) {
  26. g.GET("/list", a.getInbounds)
  27. g.GET("/get/:id", a.getInbound)
  28. g.GET("/getClientTraffics/:email", a.getClientTraffics)
  29. g.GET("/getClientTrafficsById/:id", a.getClientTrafficsById)
  30. g.POST("/add", a.addInbound)
  31. g.POST("/del/:id", a.delInbound)
  32. g.POST("/update/:id", a.updateInbound)
  33. g.POST("/clientIps/:email", a.getClientIps)
  34. g.POST("/clearClientIps/:email", a.clearClientIps)
  35. g.POST("/addClient", a.addInboundClient)
  36. g.POST("/:id/delClient/:clientId", a.delInboundClient)
  37. g.POST("/updateClient/:clientId", a.updateInboundClient)
  38. g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
  39. g.POST("/resetAllTraffics", a.resetAllTraffics)
  40. g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
  41. g.POST("/delDepletedClients/:id", a.delDepletedClients)
  42. g.POST("/import", a.importInbound)
  43. g.POST("/onlines", a.onlines)
  44. g.POST("/lastOnline", a.lastOnline)
  45. g.POST("/updateClientTraffic/:email", a.updateClientTraffic)
  46. g.POST("/:id/delClientByEmail/:email", a.delInboundClientByEmail)
  47. }
  48. // getInbounds retrieves the list of inbounds for the logged-in user.
  49. func (a *InboundController) getInbounds(c *gin.Context) {
  50. user := session.GetLoginUser(c)
  51. inbounds, err := a.inboundService.GetInbounds(user.Id)
  52. if err != nil {
  53. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err)
  54. return
  55. }
  56. jsonObj(c, inbounds, nil)
  57. }
  58. // getInbound retrieves a specific inbound by its ID.
  59. func (a *InboundController) getInbound(c *gin.Context) {
  60. id, err := strconv.Atoi(c.Param("id"))
  61. if err != nil {
  62. jsonMsg(c, I18nWeb(c, "get"), err)
  63. return
  64. }
  65. inbound, err := a.inboundService.GetInbound(id)
  66. if err != nil {
  67. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err)
  68. return
  69. }
  70. jsonObj(c, inbound, nil)
  71. }
  72. // getClientTraffics retrieves client traffic information by email.
  73. func (a *InboundController) getClientTraffics(c *gin.Context) {
  74. email := c.Param("email")
  75. clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
  76. if err != nil {
  77. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.trafficGetError"), err)
  78. return
  79. }
  80. jsonObj(c, clientTraffics, nil)
  81. }
  82. // getClientTrafficsById retrieves client traffic information by inbound ID.
  83. func (a *InboundController) getClientTrafficsById(c *gin.Context) {
  84. id := c.Param("id")
  85. clientTraffics, err := a.inboundService.GetClientTrafficByID(id)
  86. if err != nil {
  87. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.trafficGetError"), err)
  88. return
  89. }
  90. jsonObj(c, clientTraffics, nil)
  91. }
  92. // addInbound creates a new inbound configuration.
  93. func (a *InboundController) addInbound(c *gin.Context) {
  94. inbound := &model.Inbound{}
  95. err := c.ShouldBind(inbound)
  96. if err != nil {
  97. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), err)
  98. return
  99. }
  100. user := session.GetLoginUser(c)
  101. inbound.UserId = user.Id
  102. if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
  103. inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
  104. } else {
  105. inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
  106. }
  107. inbound, needRestart, err := a.inboundService.AddInbound(inbound)
  108. if err != nil {
  109. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  110. return
  111. }
  112. jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), inbound, nil)
  113. if needRestart {
  114. a.xrayService.SetToNeedRestart()
  115. }
  116. // Broadcast inbounds update via WebSocket
  117. inbounds, _ := a.inboundService.GetInbounds(user.Id)
  118. websocket.BroadcastInbounds(inbounds)
  119. }
  120. // delInbound deletes an inbound configuration by its ID.
  121. func (a *InboundController) delInbound(c *gin.Context) {
  122. id, err := strconv.Atoi(c.Param("id"))
  123. if err != nil {
  124. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundDeleteSuccess"), err)
  125. return
  126. }
  127. needRestart, err := a.inboundService.DelInbound(id)
  128. if err != nil {
  129. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  130. return
  131. }
  132. jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundDeleteSuccess"), id, nil)
  133. if needRestart {
  134. a.xrayService.SetToNeedRestart()
  135. }
  136. // Broadcast inbounds update via WebSocket
  137. user := session.GetLoginUser(c)
  138. inbounds, _ := a.inboundService.GetInbounds(user.Id)
  139. websocket.BroadcastInbounds(inbounds)
  140. }
  141. // updateInbound updates an existing inbound configuration.
  142. func (a *InboundController) updateInbound(c *gin.Context) {
  143. id, err := strconv.Atoi(c.Param("id"))
  144. if err != nil {
  145. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  146. return
  147. }
  148. inbound := &model.Inbound{
  149. Id: id,
  150. }
  151. err = c.ShouldBind(inbound)
  152. if err != nil {
  153. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  154. return
  155. }
  156. inbound, needRestart, err := a.inboundService.UpdateInbound(inbound)
  157. if err != nil {
  158. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  159. return
  160. }
  161. jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), inbound, nil)
  162. if needRestart {
  163. a.xrayService.SetToNeedRestart()
  164. }
  165. // Broadcast inbounds update via WebSocket
  166. user := session.GetLoginUser(c)
  167. inbounds, _ := a.inboundService.GetInbounds(user.Id)
  168. websocket.BroadcastInbounds(inbounds)
  169. }
  170. // getClientIps retrieves the IP addresses associated with a client by email.
  171. func (a *InboundController) getClientIps(c *gin.Context) {
  172. email := c.Param("email")
  173. ips, err := a.inboundService.GetInboundClientIps(email)
  174. if err != nil || ips == "" {
  175. jsonObj(c, "No IP Record", nil)
  176. return
  177. }
  178. // Prefer returning a normalized string list for consistent UI rendering
  179. type ipWithTimestamp struct {
  180. IP string `json:"ip"`
  181. Timestamp int64 `json:"timestamp"`
  182. }
  183. var ipsWithTime []ipWithTimestamp
  184. if err := json.Unmarshal([]byte(ips), &ipsWithTime); err == nil && len(ipsWithTime) > 0 {
  185. formatted := make([]string, 0, len(ipsWithTime))
  186. for _, item := range ipsWithTime {
  187. if item.IP == "" {
  188. continue
  189. }
  190. if item.Timestamp > 0 {
  191. ts := time.Unix(item.Timestamp, 0).Local().Format("2006-01-02 15:04:05")
  192. formatted = append(formatted, fmt.Sprintf("%s (%s)", item.IP, ts))
  193. continue
  194. }
  195. formatted = append(formatted, item.IP)
  196. }
  197. jsonObj(c, formatted, nil)
  198. return
  199. }
  200. var oldIps []string
  201. if err := json.Unmarshal([]byte(ips), &oldIps); err == nil && len(oldIps) > 0 {
  202. jsonObj(c, oldIps, nil)
  203. return
  204. }
  205. // If parsing fails, return as string
  206. jsonObj(c, ips, nil)
  207. }
  208. // clearClientIps clears the IP addresses for a client by email.
  209. func (a *InboundController) clearClientIps(c *gin.Context) {
  210. email := c.Param("email")
  211. err := a.inboundService.ClearClientIps(email)
  212. if err != nil {
  213. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.updateSuccess"), err)
  214. return
  215. }
  216. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.logCleanSuccess"), nil)
  217. }
  218. // addInboundClient adds a new client to an existing inbound.
  219. func (a *InboundController) addInboundClient(c *gin.Context) {
  220. data := &model.Inbound{}
  221. err := c.ShouldBind(data)
  222. if err != nil {
  223. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  224. return
  225. }
  226. needRestart, err := a.inboundService.AddInboundClient(data)
  227. if err != nil {
  228. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  229. return
  230. }
  231. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientAddSuccess"), nil)
  232. if needRestart {
  233. a.xrayService.SetToNeedRestart()
  234. }
  235. }
  236. // delInboundClient deletes a client from an inbound by inbound ID and client ID.
  237. func (a *InboundController) delInboundClient(c *gin.Context) {
  238. id, err := strconv.Atoi(c.Param("id"))
  239. if err != nil {
  240. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  241. return
  242. }
  243. clientId := c.Param("clientId")
  244. needRestart, err := a.inboundService.DelInboundClient(id, clientId)
  245. if err != nil {
  246. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  247. return
  248. }
  249. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientDeleteSuccess"), nil)
  250. if needRestart {
  251. a.xrayService.SetToNeedRestart()
  252. }
  253. }
  254. // updateInboundClient updates a client's configuration in an inbound.
  255. func (a *InboundController) updateInboundClient(c *gin.Context) {
  256. clientId := c.Param("clientId")
  257. inbound := &model.Inbound{}
  258. err := c.ShouldBind(inbound)
  259. if err != nil {
  260. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  261. return
  262. }
  263. needRestart, err := a.inboundService.UpdateInboundClient(inbound, clientId)
  264. if err != nil {
  265. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  266. return
  267. }
  268. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil)
  269. if needRestart {
  270. a.xrayService.SetToNeedRestart()
  271. }
  272. }
  273. // resetClientTraffic resets the traffic counter for a specific client in an inbound.
  274. func (a *InboundController) resetClientTraffic(c *gin.Context) {
  275. id, err := strconv.Atoi(c.Param("id"))
  276. if err != nil {
  277. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  278. return
  279. }
  280. email := c.Param("email")
  281. needRestart, err := a.inboundService.ResetClientTraffic(id, email)
  282. if err != nil {
  283. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  284. return
  285. }
  286. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetInboundClientTrafficSuccess"), nil)
  287. if needRestart {
  288. a.xrayService.SetToNeedRestart()
  289. }
  290. }
  291. // resetAllTraffics resets all traffic counters across all inbounds.
  292. func (a *InboundController) resetAllTraffics(c *gin.Context) {
  293. err := a.inboundService.ResetAllTraffics()
  294. if err != nil {
  295. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  296. return
  297. } else {
  298. a.xrayService.SetToNeedRestart()
  299. }
  300. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetAllTrafficSuccess"), nil)
  301. }
  302. // resetAllClientTraffics resets traffic counters for all clients in a specific inbound.
  303. func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
  304. id, err := strconv.Atoi(c.Param("id"))
  305. if err != nil {
  306. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  307. return
  308. }
  309. err = a.inboundService.ResetAllClientTraffics(id)
  310. if err != nil {
  311. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  312. return
  313. } else {
  314. a.xrayService.SetToNeedRestart()
  315. }
  316. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetAllClientTrafficSuccess"), nil)
  317. }
  318. // importInbound imports an inbound configuration from provided data.
  319. func (a *InboundController) importInbound(c *gin.Context) {
  320. inbound := &model.Inbound{}
  321. err := json.Unmarshal([]byte(c.PostForm("data")), inbound)
  322. if err != nil {
  323. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  324. return
  325. }
  326. user := session.GetLoginUser(c)
  327. inbound.Id = 0
  328. inbound.UserId = user.Id
  329. if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
  330. inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
  331. } else {
  332. inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
  333. }
  334. for index := range inbound.ClientStats {
  335. inbound.ClientStats[index].Id = 0
  336. inbound.ClientStats[index].Enable = true
  337. }
  338. needRestart := false
  339. inbound, needRestart, err = a.inboundService.AddInbound(inbound)
  340. jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), inbound, err)
  341. if err == nil && needRestart {
  342. a.xrayService.SetToNeedRestart()
  343. }
  344. }
  345. // delDepletedClients deletes clients in an inbound who have exhausted their traffic limits.
  346. func (a *InboundController) delDepletedClients(c *gin.Context) {
  347. id, err := strconv.Atoi(c.Param("id"))
  348. if err != nil {
  349. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  350. return
  351. }
  352. err = a.inboundService.DelDepletedClients(id)
  353. if err != nil {
  354. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  355. return
  356. }
  357. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.delDepletedClientsSuccess"), nil)
  358. }
  359. // onlines retrieves the list of currently online clients.
  360. func (a *InboundController) onlines(c *gin.Context) {
  361. jsonObj(c, a.inboundService.GetOnlineClients(), nil)
  362. }
  363. // lastOnline retrieves the last online timestamps for clients.
  364. func (a *InboundController) lastOnline(c *gin.Context) {
  365. data, err := a.inboundService.GetClientsLastOnline()
  366. jsonObj(c, data, err)
  367. }
  368. // updateClientTraffic updates the traffic statistics for a client by email.
  369. func (a *InboundController) updateClientTraffic(c *gin.Context) {
  370. email := c.Param("email")
  371. // Define the request structure for traffic update
  372. type TrafficUpdateRequest struct {
  373. Upload int64 `json:"upload"`
  374. Download int64 `json:"download"`
  375. }
  376. var request TrafficUpdateRequest
  377. err := c.ShouldBindJSON(&request)
  378. if err != nil {
  379. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err)
  380. return
  381. }
  382. err = a.inboundService.UpdateClientTrafficByEmail(email, request.Upload, request.Download)
  383. if err != nil {
  384. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  385. return
  386. }
  387. jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil)
  388. }
  389. // delInboundClientByEmail deletes a client from an inbound by email address.
  390. func (a *InboundController) delInboundClientByEmail(c *gin.Context) {
  391. inboundId, err := strconv.Atoi(c.Param("id"))
  392. if err != nil {
  393. jsonMsg(c, "Invalid inbound ID", err)
  394. return
  395. }
  396. email := c.Param("email")
  397. needRestart, err := a.inboundService.DelInboundClientByEmail(inboundId, email)
  398. if err != nil {
  399. jsonMsg(c, "Failed to delete client by email", err)
  400. return
  401. }
  402. jsonMsg(c, "Client deleted successfully", nil)
  403. if needRestart {
  404. a.xrayService.SetToNeedRestart()
  405. }
  406. }