mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-28 12:10:56 +00:00
Add .golangci.yml (v2): the standard linters plus bodyclose, errorlint, noctx, misspell, rowserrcheck, sqlclosecheck, unconvert, usestdlibvars, with gofumpt + goimports formatters. Enable the std-error-handling exclusion preset for idiomatic Close/Remove/Setenv ignores; scope-exclude SA1019 (parser.ParseDir in tools/openapigen) and ST1005 (intentional capitalized user-facing error copy that tests assert verbatim). No inline nolint directives were introduced. Resolve all 217 findings behavior-preserving: gofumpt/goimports formatting, explicit blank assignment on intentionally ignored errors, errors.Is/errors.As and %w wrapping, context-aware stdlib calls (CommandContext/QueryContext/NewRequestWithContext/Dialer), staticcheck simplifications, removed redundant conversions, http.StatusOK and http.MethodGet, inlined the go:fix intPtr helper, and deferred sql rows Close. Add a golangci CI job mirroring the existing Go jobs.
114 lines
3.4 KiB
Go
114 lines
3.4 KiB
Go
package controller
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/middleware"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service/panel"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service/tgbot"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/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
|
|
hostController *HostController
|
|
settingController *SettingController
|
|
xraySettingController *XraySettingController
|
|
userService panel.UserService
|
|
apiTokenService panel.ApiTokenService
|
|
Tgbot tgbot.Tgbot
|
|
}
|
|
|
|
// NewAPIController creates a new APIController instance and initializes its routes.
|
|
func NewAPIController(g *gin.RouterGroup) *APIController {
|
|
a := &APIController{}
|
|
a.initRouter(g)
|
|
return a
|
|
}
|
|
|
|
func (a *APIController) checkAPIAuth(c *gin.Context) {
|
|
// A verified client certificate (a completed mTLS handshake) authenticates
|
|
// the caller, equivalent to a valid bearer token. api_authed must be set so
|
|
// the CSRF middleware lets cert-authed mutations through.
|
|
if c.Request.TLS != nil && len(c.Request.TLS.VerifiedChains) > 0 {
|
|
if u, err := a.userService.GetFirstUser(); err == nil {
|
|
session.SetAPIAuthUser(c, u)
|
|
}
|
|
c.Set("api_authed", true)
|
|
c.Next()
|
|
return
|
|
}
|
|
auth := c.GetHeader("Authorization")
|
|
if after, ok := strings.CutPrefix(auth, "Bearer "); ok {
|
|
tok := after
|
|
if a.apiTokenService.Match(tok) {
|
|
if u, err := a.userService.GetFirstUser(); err == nil {
|
|
session.SetAPIAuthUser(c, u)
|
|
}
|
|
c.Set("api_authed", true)
|
|
c.Next()
|
|
return
|
|
}
|
|
}
|
|
if !session.IsLogin(c) {
|
|
if c.GetHeader("X-Requested-With") == "XMLHttpRequest" {
|
|
c.AbortWithStatus(http.StatusUnauthorized)
|
|
} else {
|
|
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) {
|
|
// Main API group
|
|
api := g.Group("/panel/api")
|
|
api.Use(a.checkAPIAuth)
|
|
// Decode + verify the node config envelope (zstd + X-Config-Sha256) and
|
|
// advertise support, before CSRF/handlers read the body.
|
|
api.Use(middleware.ConfigEnvelopeMiddleware())
|
|
api.Use(middleware.CSRFMiddleware())
|
|
|
|
// Inbounds API
|
|
inbounds := api.Group("/inbounds")
|
|
a.inboundController = NewInboundController(inbounds)
|
|
|
|
clients := api.Group("/clients")
|
|
NewClientController(clients)
|
|
NewGroupController(clients)
|
|
|
|
// Server API
|
|
server := api.Group("/server")
|
|
a.serverController = NewServerController(server)
|
|
|
|
// Nodes API — multi-panel management
|
|
nodes := api.Group("/nodes")
|
|
a.nodeController = NewNodeController(nodes)
|
|
|
|
// Hosts API — per-inbound override endpoints for subscription links
|
|
hosts := api.Group("/hosts")
|
|
a.hostController = NewHostController(hosts)
|
|
|
|
// Settings + Xray config management live under the API surface too, so the
|
|
// same API token drives them. Paths are /panel/api/setting/* and
|
|
// /panel/api/xray/*.
|
|
a.settingController = NewSettingController(api)
|
|
a.xraySettingController = NewXraySettingController(api)
|
|
|
|
// Extra routes
|
|
api.POST("/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()
|
|
}
|