|
@@ -8,7 +8,6 @@ import (
|
|
|
"strconv"
|
|
"strconv"
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/middleware"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/middleware"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
|
@@ -78,7 +77,7 @@ func (a *NodeController) setMtlsTrustCA(c *gin.Context) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (a *NodeController) list(c *gin.Context) {
|
|
func (a *NodeController) list(c *gin.Context) {
|
|
|
- nodes, err := a.nodeService.GetNodeTree()
|
|
|
|
|
|
|
+ nodes, err := a.nodeService.GetNodeTreeView()
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.list"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.list"), err)
|
|
|
return
|
|
return
|
|
@@ -92,7 +91,7 @@ func (a *NodeController) get(c *gin.Context) {
|
|
|
jsonMsg(c, I18nWeb(c, "get"), err)
|
|
jsonMsg(c, I18nWeb(c, "get"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- n, err := a.nodeService.GetById(id)
|
|
|
|
|
|
|
+ n, err := a.nodeService.GetViewById(id)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
|
|
|
return
|
|
return
|
|
@@ -116,27 +115,32 @@ func (a *NodeController) webCert(c *gin.Context) {
|
|
|
jsonObj(c, files, nil)
|
|
jsonObj(c, files, nil)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (a *NodeController) ensureReachable(c *gin.Context, n *model.Node) error {
|
|
|
|
|
|
|
+func (a *NodeController) ensureReachable(c *gin.Context, n *service.NodeMutationRequest, id int) error {
|
|
|
|
|
+ runtimeNode, err := a.nodeService.RuntimeNodeFromRequest(id, n)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
|
|
|
defer cancel()
|
|
defer cancel()
|
|
|
- if _, err := a.nodeService.Probe(ctx, n); err != nil {
|
|
|
|
|
|
|
+ if _, err := a.nodeService.Probe(ctx, runtimeNode); err != nil {
|
|
|
return errors.New(service.FriendlyProbeError(err.Error()))
|
|
return errors.New(service.FriendlyProbeError(err.Error()))
|
|
|
}
|
|
}
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (a *NodeController) add(c *gin.Context) {
|
|
func (a *NodeController) add(c *gin.Context) {
|
|
|
- n, ok := middleware.BindAndValidate[model.Node](c)
|
|
|
|
|
|
|
+ n, ok := middleware.BindAndValidate[service.NodeMutationRequest](c)
|
|
|
if !ok {
|
|
if !ok {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
if n.OutboundTag == "" {
|
|
if n.OutboundTag == "" {
|
|
|
- if err := a.ensureReachable(c, n); err != nil {
|
|
|
|
|
|
|
+ if err := a.ensureReachable(c, n, 0); err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- if err := a.nodeService.Create(n); err != nil {
|
|
|
|
|
|
|
+ view, err := a.nodeService.CreateFromRequest(n)
|
|
|
|
|
+ if err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -144,12 +148,12 @@ func (a *NodeController) add(c *gin.Context) {
|
|
|
if err := a.xrayService.RestartXray(false); err != nil {
|
|
if err := a.xrayService.RestartXray(false); err != nil {
|
|
|
logger.Warning("apply node outbound bridge failed:", err)
|
|
logger.Warning("apply node outbound bridge failed:", err)
|
|
|
}
|
|
}
|
|
|
- if err := a.ensureReachable(c, n); err != nil {
|
|
|
|
|
|
|
+ if err := a.ensureReachable(c, n, view.Id); err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- jsonMsgObj(c, I18nWeb(c, "pages.nodes.toasts.add"), n, nil)
|
|
|
|
|
|
|
+ jsonMsgObj(c, I18nWeb(c, "pages.nodes.toasts.add"), view, nil)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (a *NodeController) update(c *gin.Context) {
|
|
func (a *NodeController) update(c *gin.Context) {
|
|
@@ -158,7 +162,7 @@ func (a *NodeController) update(c *gin.Context) {
|
|
|
jsonMsg(c, I18nWeb(c, "get"), err)
|
|
jsonMsg(c, I18nWeb(c, "get"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- n, ok := middleware.BindAndValidate[model.Node](c)
|
|
|
|
|
|
|
+ n, ok := middleware.BindAndValidate[service.NodeMutationRequest](c)
|
|
|
if !ok {
|
|
if !ok {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -167,13 +171,13 @@ func (a *NodeController) update(c *gin.Context) {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- if n.OutboundTag == "" && old.OutboundTag == "" {
|
|
|
|
|
- if err := a.ensureReachable(c, n); err != nil {
|
|
|
|
|
|
|
+ if n.OutboundTag == "" && old.OutboundTag == "" && (!n.ClearApiToken || n.Enable) {
|
|
|
|
|
+ if err := a.ensureReachable(c, n, id); err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- if err := a.nodeService.Update(id, n); err != nil {
|
|
|
|
|
|
|
+ if err := a.nodeService.UpdateFromRequest(id, n); err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -181,7 +185,7 @@ func (a *NodeController) update(c *gin.Context) {
|
|
|
if err := a.xrayService.RestartXray(false); err != nil {
|
|
if err := a.xrayService.RestartXray(false); err != nil {
|
|
|
logger.Warning("apply node outbound bridge change failed:", err)
|
|
logger.Warning("apply node outbound bridge change failed:", err)
|
|
|
}
|
|
}
|
|
|
- if err := a.ensureReachable(c, n); err != nil {
|
|
|
|
|
|
|
+ if err := a.ensureReachable(c, n, id); err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -233,58 +237,57 @@ func (a *NodeController) setEnable(c *gin.Context) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (a *NodeController) inbounds(c *gin.Context) {
|
|
func (a *NodeController) inbounds(c *gin.Context) {
|
|
|
- n := &model.Node{}
|
|
|
|
|
- if err := c.ShouldBind(n); err != nil {
|
|
|
|
|
|
|
+ n, ok := middleware.BindAndValidate[service.NodeMutationRequest](c)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ runtimeNode, err := a.nodeService.RuntimeNodeFromRequest(n.Id, n)
|
|
|
|
|
+ if err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
|
|
|
defer cancel()
|
|
defer cancel()
|
|
|
- options, err := a.nodeService.GetRemoteInboundOptions(ctx, n)
|
|
|
|
|
|
|
+ options, err := a.nodeService.GetRemoteInboundOptions(ctx, runtimeNode)
|
|
|
jsonObj(c, options, err)
|
|
jsonObj(c, options, err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (a *NodeController) test(c *gin.Context) {
|
|
func (a *NodeController) test(c *gin.Context) {
|
|
|
- n := &model.Node{}
|
|
|
|
|
- if err := c.ShouldBind(n); err != nil {
|
|
|
|
|
- jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
|
|
|
|
|
|
|
+ n, ok := middleware.BindAndValidate[service.NodeMutationRequest](c)
|
|
|
|
|
+ if !ok {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- if n.Scheme == "" {
|
|
|
|
|
- n.Scheme = "https"
|
|
|
|
|
- }
|
|
|
|
|
- if n.BasePath == "" {
|
|
|
|
|
- n.BasePath = "/"
|
|
|
|
|
|
|
+ runtimeNode, err := a.nodeService.RuntimeNodeFromRequest(n.Id, n)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
|
|
|
defer cancel()
|
|
defer cancel()
|
|
|
var patch service.HeartbeatPatch
|
|
var patch service.HeartbeatPatch
|
|
|
- var err error
|
|
|
|
|
- if n.OutboundTag != "" {
|
|
|
|
|
- patch, err = a.nodeService.ProbeWithOutbound(ctx, n, n.OutboundTag)
|
|
|
|
|
|
|
+ if runtimeNode.OutboundTag != "" {
|
|
|
|
|
+ patch, err = a.nodeService.ProbeWithOutbound(ctx, runtimeNode, runtimeNode.OutboundTag)
|
|
|
} else {
|
|
} else {
|
|
|
- patch, err = a.nodeService.Probe(ctx, n)
|
|
|
|
|
|
|
+ patch, err = a.nodeService.Probe(ctx, runtimeNode)
|
|
|
}
|
|
}
|
|
|
jsonObj(c, patch.ToUI(err == nil), nil)
|
|
jsonObj(c, patch.ToUI(err == nil), nil)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (a *NodeController) certFingerprint(c *gin.Context) {
|
|
func (a *NodeController) certFingerprint(c *gin.Context) {
|
|
|
- n := &model.Node{}
|
|
|
|
|
- if err := c.ShouldBind(n); err != nil {
|
|
|
|
|
- jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
|
|
|
|
|
|
|
+ n, ok := middleware.BindAndValidate[service.NodeMutationRequest](c)
|
|
|
|
|
+ if !ok {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- if n.Scheme == "" {
|
|
|
|
|
- n.Scheme = "https"
|
|
|
|
|
- }
|
|
|
|
|
- if n.BasePath == "" {
|
|
|
|
|
- n.BasePath = "/"
|
|
|
|
|
|
|
+ runtimeNode, err := a.nodeService.NodeFromRequestForCertificate(n)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
|
|
|
defer cancel()
|
|
defer cancel()
|
|
|
- fp, err := a.nodeService.FetchCertFingerprint(ctx, n)
|
|
|
|
|
|
|
+ fp, err := a.nodeService.FetchCertFingerprint(ctx, runtimeNode)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
|
|
jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
|
|
|
return
|
|
return
|