| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- package controller
- import (
- "net/http"
- "strings"
- "github.com/mhsanaei/3x-ui/v3/web/middleware"
- "github.com/mhsanaei/3x-ui/v3/web/service"
- "github.com/mhsanaei/3x-ui/v3/web/session"
- "github.com/gin-gonic/gin"
- )
- // APIController handles the main API routes for the 3x-ui panel, including inbounds and server management.
- type APIController struct {
- BaseController
- inboundController *InboundController
- serverController *ServerController
- nodeController *NodeController
- settingService service.SettingService
- userService service.UserService
- Tgbot service.Tgbot
- }
- // NewAPIController creates a new APIController instance and initializes its routes.
- func NewAPIController(g *gin.RouterGroup, customGeo *service.CustomGeoService) *APIController {
- a := &APIController{}
- a.initRouter(g, customGeo)
- return a
- }
- // checkAPIAuth is a middleware that returns 404 for unauthenticated API requests
- // to hide the existence of API endpoints from unauthorized users.
- //
- // Two auth paths are accepted:
- // 1. Authorization: Bearer <apiToken> — used by remote central panels
- // polling this instance as a node. Matches via constant-time compare.
- // Sets c.Set("api_authed", true) so CSRFMiddleware can short-circuit.
- // 2. Existing session cookie — used by browsers logged into the panel UI.
- //
- // Anything else falls through to a 404 so the API endpoints remain hidden.
- func (a *APIController) checkAPIAuth(c *gin.Context) {
- auth := c.GetHeader("Authorization")
- if strings.HasPrefix(auth, "Bearer ") {
- tok := strings.TrimPrefix(auth, "Bearer ")
- if a.settingService.MatchApiToken(tok) {
- // Handlers like InboundController.addInbound assume a logged-in
- // user (inbound.UserId = user.Id). Bearer callers have no
- // session, so attach the first user as a fallback. Single-user
- // panels are the norm here.
- if u, err := a.userService.GetFirstUser(); err == nil {
- session.SetAPIAuthUser(c, u)
- }
- c.Set("api_authed", true)
- c.Next()
- return
- }
- }
- if !session.IsLogin(c) {
- c.AbortWithStatus(http.StatusNotFound)
- return
- }
- c.Next()
- }
- // initRouter sets up the API routes for inbounds, server, and other endpoints.
- func (a *APIController) initRouter(g *gin.RouterGroup, customGeo *service.CustomGeoService) {
- // Main API group
- api := g.Group("/panel/api")
- api.Use(a.checkAPIAuth)
- api.Use(middleware.CSRFMiddleware())
- // Inbounds API
- inbounds := api.Group("/inbounds")
- a.inboundController = NewInboundController(inbounds)
- // Server API
- server := api.Group("/server")
- a.serverController = NewServerController(server)
- // Nodes API — multi-panel management
- nodes := api.Group("/nodes")
- a.nodeController = NewNodeController(nodes)
- NewCustomGeoController(api.Group("/custom-geo"), customGeo)
- // Extra routes
- api.GET("/backuptotgbot", a.BackuptoTgbot)
- }
- // BackuptoTgbot sends a backup of the panel data to Telegram bot admins.
- func (a *APIController) BackuptoTgbot(c *gin.Context) {
- a.Tgbot.SendBackupToAdmins()
- }
|