3x-ui/web/controller/host.go
2026-01-09 15:36:14 +03:00

253 lines
7.2 KiB
Go

// Package controller provides HTTP handlers for host management in multi-node mode.
package controller
import (
"bytes"
"encoding/json"
"io"
"strconv"
"github.com/mhsanaei/3x-ui/v2/database/model"
"github.com/mhsanaei/3x-ui/v2/logger"
"github.com/mhsanaei/3x-ui/v2/web/service"
"github.com/mhsanaei/3x-ui/v2/web/session"
"github.com/gin-gonic/gin"
)
// HostController handles HTTP requests related to host management.
type HostController struct {
hostService service.HostService
}
// NewHostController creates a new HostController and sets up its routes.
func NewHostController(g *gin.RouterGroup) *HostController {
a := &HostController{
hostService: service.HostService{},
}
a.initRouter(g)
return a
}
// initRouter initializes the routes for host-related operations.
func (a *HostController) initRouter(g *gin.RouterGroup) {
g.GET("/list", a.getHosts)
g.GET("/get/:id", a.getHost)
g.POST("/add", a.addHost)
g.POST("/update/:id", a.updateHost)
g.POST("/del/:id", a.deleteHost)
}
// getHosts retrieves the list of all hosts for the current user.
func (a *HostController) getHosts(c *gin.Context) {
user := session.GetLoginUser(c)
hosts, err := a.hostService.GetHosts(user.Id)
if err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
jsonObj(c, hosts, nil)
}
// getHost retrieves a specific host by its ID.
func (a *HostController) getHost(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, "Invalid host ID", err)
return
}
user := session.GetLoginUser(c)
host, err := a.hostService.GetHost(id)
if err != nil {
jsonMsg(c, "Failed to get host", err)
return
}
if host.UserId != user.Id {
jsonMsg(c, "Host not found or access denied", nil)
return
}
jsonObj(c, host, nil)
}
// addHost creates a new host.
func (a *HostController) addHost(c *gin.Context) {
user := session.GetLoginUser(c)
// Extract inboundIds from JSON or form data
var inboundIdsFromJSON []int
var hasInboundIdsInJSON bool
if c.ContentType() == "application/json" {
// Read raw body to extract inboundIds
bodyBytes, err := c.GetRawData()
if err == nil && len(bodyBytes) > 0 {
// Parse JSON to extract inboundIds
var jsonData map[string]interface{}
if err := json.Unmarshal(bodyBytes, &jsonData); err == nil {
// Check for inboundIds array
if inboundIdsVal, ok := jsonData["inboundIds"]; ok {
hasInboundIdsInJSON = true
if inboundIdsArray, ok := inboundIdsVal.([]interface{}); ok {
for _, val := range inboundIdsArray {
if num, ok := val.(float64); ok {
inboundIdsFromJSON = append(inboundIdsFromJSON, int(num))
} else if num, ok := val.(int); ok {
inboundIdsFromJSON = append(inboundIdsFromJSON, num)
}
}
} else if num, ok := inboundIdsVal.(float64); ok {
// Single number instead of array
inboundIdsFromJSON = append(inboundIdsFromJSON, int(num))
} else if num, ok := inboundIdsVal.(int); ok {
inboundIdsFromJSON = append(inboundIdsFromJSON, num)
}
}
}
// Restore body for ShouldBind
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
}
host := &model.Host{}
err := c.ShouldBind(host)
if err != nil {
jsonMsg(c, "Invalid host data", err)
return
}
// Set inboundIds from JSON if available
if hasInboundIdsInJSON {
host.InboundIds = inboundIdsFromJSON
logger.Debugf("AddHost: extracted inboundIds from JSON: %v", inboundIdsFromJSON)
} else {
// Try to get from form data
inboundIdsStr := c.PostFormArray("inboundIds")
if len(inboundIdsStr) > 0 {
var inboundIds []int
for _, idStr := range inboundIdsStr {
if idStr != "" {
if id, err := strconv.Atoi(idStr); err == nil && id > 0 {
inboundIds = append(inboundIds, id)
}
}
}
host.InboundIds = inboundIds
logger.Debugf("AddHost: extracted inboundIds from form: %v", inboundIds)
}
}
logger.Debugf("AddHost: host.InboundIds before service call: %v", host.InboundIds)
err = a.hostService.AddHost(user.Id, host)
if err != nil {
logger.Errorf("Failed to add host: %v", err)
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
jsonMsgObj(c, I18nWeb(c, "pages.hosts.toasts.hostCreateSuccess"), host, nil)
}
// updateHost updates an existing host.
func (a *HostController) updateHost(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, "Invalid host ID", err)
return
}
user := session.GetLoginUser(c)
// Extract inboundIds from JSON or form data
var inboundIdsFromJSON []int
var hasInboundIdsInJSON bool
if c.ContentType() == "application/json" {
// Read raw body to extract inboundIds
bodyBytes, err := c.GetRawData()
if err == nil && len(bodyBytes) > 0 {
// Parse JSON to extract inboundIds
var jsonData map[string]interface{}
if err := json.Unmarshal(bodyBytes, &jsonData); err == nil {
// Check for inboundIds array
if inboundIdsVal, ok := jsonData["inboundIds"]; ok {
hasInboundIdsInJSON = true
if inboundIdsArray, ok := inboundIdsVal.([]interface{}); ok {
for _, val := range inboundIdsArray {
if num, ok := val.(float64); ok {
inboundIdsFromJSON = append(inboundIdsFromJSON, int(num))
} else if num, ok := val.(int); ok {
inboundIdsFromJSON = append(inboundIdsFromJSON, num)
}
}
} else if num, ok := inboundIdsVal.(float64); ok {
// Single number instead of array
inboundIdsFromJSON = append(inboundIdsFromJSON, int(num))
} else if num, ok := inboundIdsVal.(int); ok {
inboundIdsFromJSON = append(inboundIdsFromJSON, num)
}
}
}
// Restore body for ShouldBind
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
}
host := &model.Host{}
err = c.ShouldBind(host)
if err != nil {
jsonMsg(c, "Invalid host data", err)
return
}
// Set inboundIds from JSON if available
if hasInboundIdsInJSON {
host.InboundIds = inboundIdsFromJSON
logger.Debugf("UpdateHost: extracted inboundIds from JSON: %v", inboundIdsFromJSON)
} else {
// Try to get from form data
inboundIdsStr := c.PostFormArray("inboundIds")
if len(inboundIdsStr) > 0 {
var inboundIds []int
for _, idStr := range inboundIdsStr {
if idStr != "" {
if id, err := strconv.Atoi(idStr); err == nil && id > 0 {
inboundIds = append(inboundIds, id)
}
}
}
host.InboundIds = inboundIds
logger.Debugf("UpdateHost: extracted inboundIds from form: %v", inboundIds)
} else {
logger.Debugf("UpdateHost: inboundIds not provided, keeping existing assignments")
}
}
host.Id = id
err = a.hostService.UpdateHost(user.Id, host)
if err != nil {
logger.Errorf("Failed to update host: %v", err)
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
jsonMsgObj(c, I18nWeb(c, "pages.hosts.toasts.hostUpdateSuccess"), host, nil)
}
// deleteHost deletes a host by ID.
func (a *HostController) deleteHost(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, "Invalid host ID", err)
return
}
user := session.GetLoginUser(c)
err = a.hostService.DeleteHost(user.Id, id)
if err != nil {
logger.Errorf("Failed to delete host: %v", err)
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
jsonMsg(c, I18nWeb(c, "pages.hosts.toasts.hostDeleteSuccess"), nil)
}