1
0

api.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package controller
  2. import (
  3. "net/http"
  4. "strings"
  5. "github.com/mhsanaei/3x-ui/v2/web/middleware"
  6. "github.com/mhsanaei/3x-ui/v2/web/service"
  7. "github.com/mhsanaei/3x-ui/v2/web/session"
  8. "github.com/gin-gonic/gin"
  9. )
  10. // APIController handles the main API routes for the 3x-ui panel, including inbounds and server management.
  11. type APIController struct {
  12. BaseController
  13. inboundController *InboundController
  14. serverController *ServerController
  15. nodeController *NodeController
  16. settingService service.SettingService
  17. userService service.UserService
  18. Tgbot service.Tgbot
  19. }
  20. // NewAPIController creates a new APIController instance and initializes its routes.
  21. func NewAPIController(g *gin.RouterGroup, customGeo *service.CustomGeoService) *APIController {
  22. a := &APIController{}
  23. a.initRouter(g, customGeo)
  24. return a
  25. }
  26. // checkAPIAuth is a middleware that returns 404 for unauthenticated API requests
  27. // to hide the existence of API endpoints from unauthorized users.
  28. //
  29. // Two auth paths are accepted:
  30. // 1. Authorization: Bearer <apiToken> — used by remote central panels
  31. // polling this instance as a node. Matches via constant-time compare.
  32. // Sets c.Set("api_authed", true) so CSRFMiddleware can short-circuit.
  33. // 2. Existing session cookie — used by browsers logged into the panel UI.
  34. //
  35. // Anything else falls through to a 404 so the API endpoints remain hidden.
  36. func (a *APIController) checkAPIAuth(c *gin.Context) {
  37. auth := c.GetHeader("Authorization")
  38. if strings.HasPrefix(auth, "Bearer ") {
  39. tok := strings.TrimPrefix(auth, "Bearer ")
  40. if a.settingService.MatchApiToken(tok) {
  41. // Handlers like InboundController.addInbound assume a logged-in
  42. // user (inbound.UserId = user.Id). Bearer callers have no
  43. // session, so attach the first user as a fallback. Single-user
  44. // panels are the norm here.
  45. if u, err := a.userService.GetFirstUser(); err == nil {
  46. session.SetAPIAuthUser(c, u)
  47. }
  48. c.Set("api_authed", true)
  49. c.Next()
  50. return
  51. }
  52. }
  53. if !session.IsLogin(c) {
  54. c.AbortWithStatus(http.StatusNotFound)
  55. return
  56. }
  57. c.Next()
  58. }
  59. // initRouter sets up the API routes for inbounds, server, and other endpoints.
  60. func (a *APIController) initRouter(g *gin.RouterGroup, customGeo *service.CustomGeoService) {
  61. // Main API group
  62. api := g.Group("/panel/api")
  63. api.Use(a.checkAPIAuth)
  64. api.Use(middleware.CSRFMiddleware())
  65. // Inbounds API
  66. inbounds := api.Group("/inbounds")
  67. a.inboundController = NewInboundController(inbounds)
  68. // Server API
  69. server := api.Group("/server")
  70. a.serverController = NewServerController(server)
  71. // Nodes API — multi-panel management
  72. nodes := api.Group("/nodes")
  73. a.nodeController = NewNodeController(nodes)
  74. NewCustomGeoController(api.Group("/custom-geo"), customGeo)
  75. // Extra routes
  76. api.GET("/backuptotgbot", a.BackuptoTgbot)
  77. }
  78. // BackuptoTgbot sends a backup of the panel data to Telegram bot admins.
  79. func (a *APIController) BackuptoTgbot(c *gin.Context) {
  80. a.Tgbot.SendBackupToAdmins()
  81. }