瀏覽代碼

docs: update docs about insecure skip verify

Jacky 9 月之前
父節點
當前提交
6c7b644f60

+ 119 - 119
api/openai/openai.go

@@ -1,18 +1,18 @@
 package openai
 package openai
 
 
 import (
 import (
-    "context"
-    "crypto/tls"
-    "fmt"
-    "github.com/0xJacky/Nginx-UI/api"
-    "github.com/0xJacky/Nginx-UI/internal/chatbot"
-    "github.com/0xJacky/Nginx-UI/settings"
-    "github.com/gin-gonic/gin"
-    "github.com/pkg/errors"
-    "github.com/sashabaranov/go-openai"
-    "io"
-    "net/http"
-    "net/url"
+	"context"
+	"crypto/tls"
+	"fmt"
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/internal/chatbot"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/gin-gonic/gin"
+	"github.com/pkg/errors"
+	"github.com/sashabaranov/go-openai"
+	"io"
+	"net/http"
+	"net/url"
 )
 )
 
 
 const ChatGPTInitPrompt = `You are a assistant who can help users write and optimise the configurations of Nginx,
 const ChatGPTInitPrompt = `You are a assistant who can help users write and optimise the configurations of Nginx,
@@ -22,111 +22,111 @@ Later the language environment depends on the user message.
 The first reply should involve the key information of the file and ask user what can you help them.`
 The first reply should involve the key information of the file and ask user what can you help them.`
 
 
 func MakeChatCompletionRequest(c *gin.Context) {
 func MakeChatCompletionRequest(c *gin.Context) {
-    var json struct {
-        Filepath string                         `json:"filepath"`
-        Messages []openai.ChatCompletionMessage `json:"messages"`
-    }
-
-    if !api.BindAndValid(c, &json) {
-        return
-    }
-
-    messages := []openai.ChatCompletionMessage{
-        {
-            Role:    openai.ChatMessageRoleSystem,
-            Content: ChatGPTInitPrompt,
-        },
-    }
-
-    messages = append(messages, json.Messages...)
-
-    if json.Filepath != "" {
-        messages = chatbot.ChatCompletionWithContext(json.Filepath, messages)
-    }
-
-    // SSE server
-    c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
-    c.Writer.Header().Set("Cache-Control", "no-cache")
-    c.Writer.Header().Set("Connection", "keep-alive")
-    c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
-
-    config := openai.DefaultConfig(settings.OpenAISettings.Token)
-
-    if settings.OpenAISettings.Proxy != "" {
-        proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
-        if err != nil {
-            c.Stream(func(w io.Writer) bool {
-                c.SSEvent("message", gin.H{
-                    "type":    "error",
-                    "content": err.Error(),
-                })
-                return false
-            })
-            return
-        }
-        transport := &http.Transport{
-            Proxy:           http.ProxyURL(proxyUrl),
-            TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
-        }
-        config.HTTPClient = &http.Client{
-            Transport: transport,
-        }
-    }
-
-    if settings.OpenAISettings.BaseUrl != "" {
-        config.BaseURL = settings.OpenAISettings.BaseUrl
-    }
-
-    openaiClient := openai.NewClientWithConfig(config)
-    ctx := context.Background()
-
-    req := openai.ChatCompletionRequest{
-        Model:    settings.OpenAISettings.Model,
-        Messages: messages,
-        Stream:   true,
-    }
-    stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
-    if err != nil {
-        fmt.Printf("CompletionStream error: %v\n", err)
-        c.Stream(func(w io.Writer) bool {
-            c.SSEvent("message", gin.H{
-                "type":    "error",
-                "content": err.Error(),
-            })
-            return false
-        })
-        return
-    }
-    defer stream.Close()
-    msgChan := make(chan string)
-    go func() {
-        defer close(msgChan)
-        for {
-            response, err := stream.Recv()
-            if errors.Is(err, io.EOF) {
-                fmt.Println()
-                return
-            }
-
-            if err != nil {
-                fmt.Printf("Stream error: %v\n", err)
-                return
-            }
-
-            message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
-
-            msgChan <- message
-        }
-    }()
-
-    c.Stream(func(w io.Writer) bool {
-        if m, ok := <-msgChan; ok {
-            c.SSEvent("message", gin.H{
-                "type":    "message",
-                "content": m,
-            })
-            return true
-        }
-        return false
-    })
+	var json struct {
+		Filepath string                         `json:"filepath"`
+		Messages []openai.ChatCompletionMessage `json:"messages"`
+	}
+
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	messages := []openai.ChatCompletionMessage{
+		{
+			Role:    openai.ChatMessageRoleSystem,
+			Content: ChatGPTInitPrompt,
+		},
+	}
+
+	messages = append(messages, json.Messages...)
+
+	if json.Filepath != "" {
+		messages = chatbot.ChatCompletionWithContext(json.Filepath, messages)
+	}
+
+	// SSE server
+	c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
+	c.Writer.Header().Set("Cache-Control", "no-cache")
+	c.Writer.Header().Set("Connection", "keep-alive")
+	c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
+
+	config := openai.DefaultConfig(settings.OpenAISettings.Token)
+
+	if settings.OpenAISettings.Proxy != "" {
+		proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
+		if err != nil {
+			c.Stream(func(w io.Writer) bool {
+				c.SSEvent("message", gin.H{
+					"type":    "error",
+					"content": err.Error(),
+				})
+				return false
+			})
+			return
+		}
+		transport := &http.Transport{
+			Proxy:           http.ProxyURL(proxyUrl),
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
+		}
+		config.HTTPClient = &http.Client{
+			Transport: transport,
+		}
+	}
+
+	if settings.OpenAISettings.BaseUrl != "" {
+		config.BaseURL = settings.OpenAISettings.BaseUrl
+	}
+
+	openaiClient := openai.NewClientWithConfig(config)
+	ctx := context.Background()
+
+	req := openai.ChatCompletionRequest{
+		Model:    settings.OpenAISettings.Model,
+		Messages: messages,
+		Stream:   true,
+	}
+	stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
+	if err != nil {
+		fmt.Printf("CompletionStream error: %v\n", err)
+		c.Stream(func(w io.Writer) bool {
+			c.SSEvent("message", gin.H{
+				"type":    "error",
+				"content": err.Error(),
+			})
+			return false
+		})
+		return
+	}
+	defer stream.Close()
+	msgChan := make(chan string)
+	go func() {
+		defer close(msgChan)
+		for {
+			response, err := stream.Recv()
+			if errors.Is(err, io.EOF) {
+				fmt.Println()
+				return
+			}
+
+			if err != nil {
+				fmt.Printf("Stream error: %v\n", err)
+				return
+			}
+
+			message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
+
+			msgChan <- message
+		}
+	}()
+
+	c.Stream(func(w io.Writer) bool {
+		if m, ok := <-msgChan; ok {
+			c.SSEvent("message", gin.H{
+				"type":    "message",
+				"content": m,
+			})
+			return true
+		}
+		return false
+	})
 }
 }

+ 7 - 0
docs/guide/config-server.md

@@ -145,3 +145,10 @@ Nginx UI will not create a system initial acme user, this means you can't apply
 - Type: `string`
 - Type: `string`
 
 
 Use this option to customize the name of local server to be displayed in the environment indicator.
 Use this option to customize the name of local server to be displayed in the environment indicator.
+
+## InsecureSkipVerify
+
+- Version:`>= v2.0.0-beta.30`
+- Type: `bool`
+
+This option is used to skip the verification of the certificate of servers when Nginx UI sends requests to them.

+ 19 - 18
docs/guide/env.md

@@ -3,24 +3,25 @@ Applicable for version v2.0.0-beta.23 and above.
 
 
 ## Server
 ## Server
 
 
-| Configuration Setting         | Environment Variable                  |
-| ----------------------------- | ------------------------------------- |
-| HttpPort                      | NGINX_UI_SERVER_HTTP_PORT             |
-| RunMode                       | NGINX_UI_SERVER_RUN_MODE              |
-| JwtSecret                     | NGINX_UI_SERVER_JWT_SECRET            |
-| HTTPChallengePort             | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT   |
-| StartCmd                      | NGINX_UI_SERVER_START_CMD             |
-| Database                      | NGINX_UI_SERVER_DATABASE              |
-| CADir                         | NGINX_UI_SERVER_CA_DIR                |
-| GithubProxy                   | NGINX_UI_SERVER_GITHUB_PROXY          |
-| NodeSecret                    | NGINX_UI_SERVER_NODE_SECRET           |
-| Demo                          | NGINX_UI_SERVER_DEMO                  |
-| PageSize                      | NGINX_UI_SERVER_PAGE_SIZE             |
-| HttpHost                      | NGINX_UI_SERVER_HTTP_HOST             |
-| CertRenewalInterval           | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
-| RecursiveNameservers          | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
-| SkipInstallation              | NGINX_UI_SERVER_SKIP_INSTALLATION     |
-| Name                          | NGINX_UI_SERVER_NAME                  |
+| Configuration Setting | Environment Variable                  |
+|-----------------------|---------------------------------------|
+| HttpPort              | NGINX_UI_SERVER_HTTP_PORT             |
+| RunMode               | NGINX_UI_SERVER_RUN_MODE              |
+| JwtSecret             | NGINX_UI_SERVER_JWT_SECRET            |
+| HTTPChallengePort     | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT   |
+| StartCmd              | NGINX_UI_SERVER_START_CMD             |
+| Database              | NGINX_UI_SERVER_DATABASE              |
+| CADir                 | NGINX_UI_SERVER_CA_DIR                |
+| GithubProxy           | NGINX_UI_SERVER_GITHUB_PROXY          |
+| NodeSecret            | NGINX_UI_SERVER_NODE_SECRET           |
+| Demo                  | NGINX_UI_SERVER_DEMO                  |
+| PageSize              | NGINX_UI_SERVER_PAGE_SIZE             |
+| HttpHost              | NGINX_UI_SERVER_HTTP_HOST             |
+| CertRenewalInterval   | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
+| RecursiveNameservers  | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
+| SkipInstallation      | NGINX_UI_SERVER_SKIP_INSTALLATION     |
+| Name                  | NGINX_UI_SERVER_NAME                  |
+| InsecureSkipVerify    | NGINX_UI_SERVER_INSECURE_SKIP_VERIFY  |
 
 
 ## Nginx
 ## Nginx
 
 

+ 7 - 0
docs/zh_CN/guide/config-server.md

@@ -132,3 +132,10 @@ Nginx UI 将不会创建系统初始的 acme 用户,这意味着您无法在
 - 类型:`string`
 - 类型:`string`
 
 
 使用此选项自定义本地服务器的名称,以在环境指示器中显示。
 使用此选项自定义本地服务器的名称,以在环境指示器中显示。
+
+## InsecureSkipVerify
+
+- 版本:`>= v2.0.0-beta.30`
+- 类型: `bool`
+
+此选项用于配置 Nginx UI 服务器在与其他服务器建立 TLS 连接时是否跳过证书验证。

+ 19 - 18
docs/zh_CN/guide/env.md

@@ -3,24 +3,25 @@
 
 
 ## Server
 ## Server
 
 
-| Configuration Setting         | Environment Variable                  |
-| ----------------------------- | ------------------------------------- |
-| HttpPort                      | NGINX_UI_SERVER_HTTP_PORT             |
-| RunMode                       | NGINX_UI_SERVER_RUN_MODE              |
-| JwtSecret                     | NGINX_UI_SERVER_JWT_SECRET            |
-| HTTPChallengePort             | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT   |
-| StartCmd                      | NGINX_UI_SERVER_START_CMD             |
-| Database                      | NGINX_UI_SERVER_DATABASE              |
-| CADir                         | NGINX_UI_SERVER_CA_DIR                |
-| GithubProxy                   | NGINX_UI_SERVER_GITHUB_PROXY          |
-| NodeSecret                    | NGINX_UI_SERVER_NODE_SECRET           |
-| Demo                          | NGINX_UI_SERVER_DEMO                  |
-| PageSize                      | NGINX_UI_SERVER_PAGE_SIZE             |
-| HttpHost                      | NGINX_UI_SERVER_HTTP_HOST             |
-| CertRenewalInterval           | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
-| RecursiveNameservers          | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
-| SkipInstallation              | NGINX_UI_SERVER_SKIP_INSTALLATION     |
-| Name                          | NGINX_UI_SERVER_NAME                  |
+| Configuration Setting   | Environment Variable                  |
+|-------------------------| ------------------------------------- |
+| HttpPort                | NGINX_UI_SERVER_HTTP_PORT             |
+| RunMode                 | NGINX_UI_SERVER_RUN_MODE              |
+| JwtSecret               | NGINX_UI_SERVER_JWT_SECRET            |
+| HTTPChallengePort       | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT   |
+| StartCmd                | NGINX_UI_SERVER_START_CMD             |
+| Database                | NGINX_UI_SERVER_DATABASE              |
+| CADir                   | NGINX_UI_SERVER_CA_DIR                |
+| GithubProxy             | NGINX_UI_SERVER_GITHUB_PROXY          |
+| NodeSecret              | NGINX_UI_SERVER_NODE_SECRET           |
+| Demo                    | NGINX_UI_SERVER_DEMO                  |
+| PageSize                | NGINX_UI_SERVER_PAGE_SIZE             |
+| HttpHost                | NGINX_UI_SERVER_HTTP_HOST             |
+| CertRenewalInterval     | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
+| RecursiveNameservers    | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
+| SkipInstallation        | NGINX_UI_SERVER_SKIP_INSTALLATION     |
+| Name                    | NGINX_UI_SERVER_NAME                  |
+| InsecureSkipVerify      | NGINX_UI_SERVER_INSECURE_SKIP_VERIFY  |
 
 
 ## Nginx
 ## Nginx
 
 

+ 7 - 0
docs/zh_TW/guide/config-server.md

@@ -133,3 +133,10 @@ Nginx UI 將不會創建系統初始的 acme 使用者,這意味著您無法
 - 類型:`string`
 - 類型:`string`
 
 
 使用此選項自定義本地伺服器的名稱,以在環境指示器中顯示。
 使用此選項自定義本地伺服器的名稱,以在環境指示器中顯示。
+
+## InsecureSkipVerify
+
+- 版本:`>= v2.0.0-beta.30`
+- 類型: `bool`
+
+此選項用於配置 Nginx UI 伺服器在與其他伺服器建立 TLS 連接時是否跳過證書驗證。

+ 19 - 18
docs/zh_TW/guide/env.md

@@ -3,24 +3,25 @@
 
 
 ## Server
 ## Server
 
 
-| Configuration Setting         | Environment Variable                  |
-| ----------------------------- | ------------------------------------- |
-| HttpPort                      | NGINX_UI_SERVER_HTTP_PORT             |
-| RunMode                       | NGINX_UI_SERVER_RUN_MODE              |
-| JwtSecret                     | NGINX_UI_SERVER_JWT_SECRET            |
-| HTTPChallengePort             | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT   |
-| StartCmd                      | NGINX_UI_SERVER_START_CMD             |
-| Database                      | NGINX_UI_SERVER_DATABASE              |
-| CADir                         | NGINX_UI_SERVER_CA_DIR                |
-| GithubProxy                   | NGINX_UI_SERVER_GITHUB_PROXY          |
-| NodeSecret                    | NGINX_UI_SERVER_NODE_SECRET           |
-| Demo                          | NGINX_UI_SERVER_DEMO                  |
-| PageSize                      | NGINX_UI_SERVER_PAGE_SIZE             |
-| HttpHost                      | NGINX_UI_SERVER_HTTP_HOST             |
-| CertRenewalInterval           | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
-| RecursiveNameservers          | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
-| SkipInstallation              | NGINX_UI_SERVER_SKIP_INSTALLATION     |
-| Name                          | NGINX_UI_SERVER_NAME                  |
+| Configuration Setting  | Environment Variable                  |
+|------------------------| ------------------------------------- |
+| HttpPort               | NGINX_UI_SERVER_HTTP_PORT             |
+| RunMode                | NGINX_UI_SERVER_RUN_MODE              |
+| JwtSecret              | NGINX_UI_SERVER_JWT_SECRET            |
+| HTTPChallengePort      | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT   |
+| StartCmd               | NGINX_UI_SERVER_START_CMD             |
+| Database               | NGINX_UI_SERVER_DATABASE              |
+| CADir                  | NGINX_UI_SERVER_CA_DIR                |
+| GithubProxy            | NGINX_UI_SERVER_GITHUB_PROXY          |
+| NodeSecret             | NGINX_UI_SERVER_NODE_SECRET           |
+| Demo                   | NGINX_UI_SERVER_DEMO                  |
+| PageSize               | NGINX_UI_SERVER_PAGE_SIZE             |
+| HttpHost               | NGINX_UI_SERVER_HTTP_HOST             |
+| CertRenewalInterval    | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
+| RecursiveNameservers   | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
+| SkipInstallation       | NGINX_UI_SERVER_SKIP_INSTALLATION     |
+| Name                   | NGINX_UI_SERVER_NAME                  |
+| InsecureSkipVerify     | NGINX_UI_SERVER_INSECURE_SKIP_VERIFY  |
 
 
 ## Nginx
 ## Nginx
 
 

+ 82 - 82
internal/analytic/node.go

@@ -1,44 +1,44 @@
 package analytic
 package analytic
 
 
 import (
 import (
-    "crypto/tls"
-    "encoding/json"
-    "github.com/0xJacky/Nginx-UI/internal/logger"
-    "github.com/0xJacky/Nginx-UI/internal/upgrader"
-    "github.com/0xJacky/Nginx-UI/model"
-    "github.com/0xJacky/Nginx-UI/settings"
-    "github.com/shirou/gopsutil/v3/load"
-    "github.com/shirou/gopsutil/v3/net"
-    "io"
-    "net/http"
-    "net/url"
-    "sync"
-    "time"
+	"crypto/tls"
+	"encoding/json"
+	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"github.com/0xJacky/Nginx-UI/internal/upgrader"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/shirou/gopsutil/v3/load"
+	"github.com/shirou/gopsutil/v3/net"
+	"io"
+	"net/http"
+	"net/url"
+	"sync"
+	"time"
 )
 )
 
 
 type NodeInfo struct {
 type NodeInfo struct {
-    NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
-    Version         string               `json:"version"`
-    CPUNum          int                  `json:"cpu_num"`
-    MemoryTotal     string               `json:"memory_total"`
-    DiskTotal       string               `json:"disk_total"`
+	NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
+	Version         string               `json:"version"`
+	CPUNum          int                  `json:"cpu_num"`
+	MemoryTotal     string               `json:"memory_total"`
+	DiskTotal       string               `json:"disk_total"`
 }
 }
 
 
 type NodeStat struct {
 type NodeStat struct {
-    AvgLoad       *load.AvgStat      `json:"avg_load"`
-    CPUPercent    float64            `json:"cpu_percent"`
-    MemoryPercent float64            `json:"memory_percent"`
-    DiskPercent   float64            `json:"disk_percent"`
-    Network       net.IOCountersStat `json:"network"`
-    Status        bool               `json:"status"`
-    ResponseAt    time.Time          `json:"response_at"`
+	AvgLoad       *load.AvgStat      `json:"avg_load"`
+	CPUPercent    float64            `json:"cpu_percent"`
+	MemoryPercent float64            `json:"memory_percent"`
+	DiskPercent   float64            `json:"disk_percent"`
+	Network       net.IOCountersStat `json:"network"`
+	Status        bool               `json:"status"`
+	ResponseAt    time.Time          `json:"response_at"`
 }
 }
 
 
 type Node struct {
 type Node struct {
-    EnvironmentID int `json:"environment_id,omitempty"`
-    *model.Environment
-    NodeStat
-    NodeInfo
+	EnvironmentID int `json:"environment_id,omitempty"`
+	*model.Environment
+	NodeStat
+	NodeInfo
 }
 }
 
 
 var mutex sync.Mutex
 var mutex sync.Mutex
@@ -48,74 +48,74 @@ type TNodeMap map[int]*Node
 var NodeMap TNodeMap
 var NodeMap TNodeMap
 
 
 func init() {
 func init() {
-    NodeMap = make(TNodeMap)
+	NodeMap = make(TNodeMap)
 }
 }
 
 
 func GetNode(env *model.Environment) (n *Node) {
 func GetNode(env *model.Environment) (n *Node) {
-    if env == nil {
-        // this should never happen
-        logger.Error("env is nil")
-        return
-    }
-    if !env.Enabled {
-        return &Node{
-            Environment: env,
-        }
-    }
-    n, ok := NodeMap[env.ID]
-    if !ok {
-        n = &Node{}
-    }
-    n.Environment = env
-    return n
+	if env == nil {
+		// this should never happen
+		logger.Error("env is nil")
+		return
+	}
+	if !env.Enabled {
+		return &Node{
+			Environment: env,
+		}
+	}
+	n, ok := NodeMap[env.ID]
+	if !ok {
+		n = &Node{}
+	}
+	n.Environment = env
+	return n
 }
 }
 
 
 func InitNode(env *model.Environment) (n *Node) {
 func InitNode(env *model.Environment) (n *Node) {
-    n = &Node{
-        Environment: env,
-    }
+	n = &Node{
+		Environment: env,
+	}
 
 
-    u, err := url.JoinPath(env.URL, "/api/node")
+	u, err := url.JoinPath(env.URL, "/api/node")
 
 
-    if err != nil {
-        logger.Error(err)
-        return
-    }
+	if err != nil {
+		logger.Error(err)
+		return
+	}
 
 
-    client := http.Client{
-        Transport: &http.Transport{
-            TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
-        },
-    }
+	client := http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
+		},
+	}
 
 
-    req, err := http.NewRequest("GET", u, nil)
-    if err != nil {
-        logger.Error(err)
-        return
-    }
+	req, err := http.NewRequest("GET", u, nil)
+	if err != nil {
+		logger.Error(err)
+		return
+	}
 
 
-    req.Header.Set("X-Node-Secret", env.Token)
+	req.Header.Set("X-Node-Secret", env.Token)
 
 
-    resp, err := client.Do(req)
+	resp, err := client.Do(req)
 
 
-    if err != nil {
-        logger.Error(err)
-        return
-    }
+	if err != nil {
+		logger.Error(err)
+		return
+	}
 
 
-    defer resp.Body.Close()
-    bytes, _ := io.ReadAll(resp.Body)
+	defer resp.Body.Close()
+	bytes, _ := io.ReadAll(resp.Body)
 
 
-    if resp.StatusCode != http.StatusOK {
-        logger.Error(string(bytes))
-        return
-    }
+	if resp.StatusCode != http.StatusOK {
+		logger.Error(string(bytes))
+		return
+	}
 
 
-    err = json.Unmarshal(bytes, &n.NodeInfo)
-    if err != nil {
-        logger.Error(err)
-        return
-    }
+	err = json.Unmarshal(bytes, &n.NodeInfo)
+	if err != nil {
+		logger.Error(err)
+		return
+	}
 
 
-    return
+	return
 }
 }

+ 230 - 230
internal/config/sync.go

@@ -1,258 +1,258 @@
 package config
 package config
 
 
 import (
 import (
-    "bytes"
-    "crypto/tls"
-    "encoding/json"
-    "fmt"
-    "github.com/0xJacky/Nginx-UI/internal/helper"
-    "github.com/0xJacky/Nginx-UI/internal/logger"
-    "github.com/0xJacky/Nginx-UI/internal/nginx"
-    "github.com/0xJacky/Nginx-UI/internal/notification"
-    "github.com/0xJacky/Nginx-UI/model"
-    "github.com/0xJacky/Nginx-UI/query"
-    "github.com/0xJacky/Nginx-UI/settings"
-    "github.com/gin-gonic/gin"
-    "io"
-    "net/http"
-    "os"
-    "path/filepath"
-    "strings"
+	"bytes"
+	"crypto/tls"
+	"encoding/json"
+	"fmt"
+	"github.com/0xJacky/Nginx-UI/internal/helper"
+	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/0xJacky/Nginx-UI/internal/notification"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/gin-gonic/gin"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
 )
 )
 
 
 type SyncConfigPayload struct {
 type SyncConfigPayload struct {
-    Name        string `json:"name"`
-    Filepath    string `json:"filepath"`
-    NewFilepath string `json:"new_filepath"`
-    Content     string `json:"content"`
-    Overwrite   bool   `json:"overwrite"`
+	Name        string `json:"name"`
+	Filepath    string `json:"filepath"`
+	NewFilepath string `json:"new_filepath"`
+	Content     string `json:"content"`
+	Overwrite   bool   `json:"overwrite"`
 }
 }
 
 
 func SyncToRemoteServer(c *model.Config, newFilepath string) (err error) {
 func SyncToRemoteServer(c *model.Config, newFilepath string) (err error) {
-    if c.Filepath == "" || len(c.SyncNodeIds) == 0 {
-        return
-    }
-
-    nginxConfPath := nginx.GetConfPath()
-    if !helper.IsUnderDirectory(c.Filepath, nginxConfPath) {
-        return fmt.Errorf("config: %s is not under the nginx conf path: %s",
-            c.Filepath, nginxConfPath)
-    }
-
-    if newFilepath != "" && !helper.IsUnderDirectory(newFilepath, nginxConfPath) {
-        return fmt.Errorf("config: %s is not under the nginx conf path: %s",
-            c.Filepath, nginxConfPath)
-    }
-
-    currentPath := c.Filepath
-    if newFilepath != "" {
-        currentPath = newFilepath
-    }
-    configBytes, err := os.ReadFile(currentPath)
-    if err != nil {
-        return
-    }
-
-    payload := &SyncConfigPayload{
-        Name:        c.Name,
-        Filepath:    c.Filepath,
-        NewFilepath: newFilepath,
-        Content:     string(configBytes),
-        Overwrite:   c.SyncOverwrite,
-    }
-    payloadBytes, err := json.Marshal(payload)
-    if err != nil {
-        return
-    }
-
-    q := query.Environment
-    envs, _ := q.Where(q.ID.In(c.SyncNodeIds...)).Find()
-    for _, env := range envs {
-        go func() {
-            err := payload.deploy(env, c, payloadBytes)
-            if err != nil {
-                logger.Error(err)
-            }
-        }()
-    }
-
-    return
+	if c.Filepath == "" || len(c.SyncNodeIds) == 0 {
+		return
+	}
+
+	nginxConfPath := nginx.GetConfPath()
+	if !helper.IsUnderDirectory(c.Filepath, nginxConfPath) {
+		return fmt.Errorf("config: %s is not under the nginx conf path: %s",
+			c.Filepath, nginxConfPath)
+	}
+
+	if newFilepath != "" && !helper.IsUnderDirectory(newFilepath, nginxConfPath) {
+		return fmt.Errorf("config: %s is not under the nginx conf path: %s",
+			c.Filepath, nginxConfPath)
+	}
+
+	currentPath := c.Filepath
+	if newFilepath != "" {
+		currentPath = newFilepath
+	}
+	configBytes, err := os.ReadFile(currentPath)
+	if err != nil {
+		return
+	}
+
+	payload := &SyncConfigPayload{
+		Name:        c.Name,
+		Filepath:    c.Filepath,
+		NewFilepath: newFilepath,
+		Content:     string(configBytes),
+		Overwrite:   c.SyncOverwrite,
+	}
+	payloadBytes, err := json.Marshal(payload)
+	if err != nil {
+		return
+	}
+
+	q := query.Environment
+	envs, _ := q.Where(q.ID.In(c.SyncNodeIds...)).Find()
+	for _, env := range envs {
+		go func() {
+			err := payload.deploy(env, c, payloadBytes)
+			if err != nil {
+				logger.Error(err)
+			}
+		}()
+	}
+
+	return
 }
 }
 
 
 func SyncRenameOnRemoteServer(origPath, newPath string, syncNodeIds []int) (err error) {
 func SyncRenameOnRemoteServer(origPath, newPath string, syncNodeIds []int) (err error) {
-    if origPath == "" || newPath == "" || len(syncNodeIds) == 0 {
-        return
-    }
-
-    nginxConfPath := nginx.GetConfPath()
-    if !helper.IsUnderDirectory(origPath, nginxConfPath) {
-        return fmt.Errorf("config: %s is not under the nginx conf path: %s",
-            origPath, nginxConfPath)
-    }
-
-    if !helper.IsUnderDirectory(newPath, nginxConfPath) {
-        return fmt.Errorf("config: %s is not under the nginx conf path: %s",
-            newPath, nginxConfPath)
-    }
-
-    payload := &RenameConfigPayload{
-        Filepath:    origPath,
-        NewFilepath: newPath,
-    }
-
-    q := query.Environment
-    envs, _ := q.Where(q.ID.In(syncNodeIds...)).Find()
-    for _, env := range envs {
-        go func() {
-            err := payload.rename(env)
-            if err != nil {
-                logger.Error(err)
-            }
-        }()
-    }
-
-    return
+	if origPath == "" || newPath == "" || len(syncNodeIds) == 0 {
+		return
+	}
+
+	nginxConfPath := nginx.GetConfPath()
+	if !helper.IsUnderDirectory(origPath, nginxConfPath) {
+		return fmt.Errorf("config: %s is not under the nginx conf path: %s",
+			origPath, nginxConfPath)
+	}
+
+	if !helper.IsUnderDirectory(newPath, nginxConfPath) {
+		return fmt.Errorf("config: %s is not under the nginx conf path: %s",
+			newPath, nginxConfPath)
+	}
+
+	payload := &RenameConfigPayload{
+		Filepath:    origPath,
+		NewFilepath: newPath,
+	}
+
+	q := query.Environment
+	envs, _ := q.Where(q.ID.In(syncNodeIds...)).Find()
+	for _, env := range envs {
+		go func() {
+			err := payload.rename(env)
+			if err != nil {
+				logger.Error(err)
+			}
+		}()
+	}
+
+	return
 }
 }
 
 
 type SyncNotificationPayload struct {
 type SyncNotificationPayload struct {
-    StatusCode int    `json:"status_code"`
-    ConfigName string `json:"config_name"`
-    EnvName    string `json:"env_name"`
-    RespBody   string `json:"resp_body"`
+	StatusCode int    `json:"status_code"`
+	ConfigName string `json:"config_name"`
+	EnvName    string `json:"env_name"`
+	RespBody   string `json:"resp_body"`
 }
 }
 
 
 func (p *SyncConfigPayload) deploy(env *model.Environment, c *model.Config, payloadBytes []byte) (err error) {
 func (p *SyncConfigPayload) deploy(env *model.Environment, c *model.Config, payloadBytes []byte) (err error) {
-    client := http.Client{
-        Transport: &http.Transport{
-            TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
-        },
-    }
-    url, err := env.GetUrl("/api/config")
-    if err != nil {
-        return
-    }
-    req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
-    if err != nil {
-        return
-    }
-    req.Header.Set("X-Node-Secret", env.Token)
-    resp, err := client.Do(req)
-    if err != nil {
-        return
-    }
-    defer resp.Body.Close()
-
-    respBody, err := io.ReadAll(resp.Body)
-    if err != nil {
-        return
-    }
-
-    notificationPayload := &SyncNotificationPayload{
-        StatusCode: resp.StatusCode,
-        ConfigName: c.Name,
-        EnvName:    env.Name,
-        RespBody:   string(respBody),
-    }
-
-    notificationPayloadBytes, err := json.Marshal(notificationPayload)
-    if err != nil {
-        return
-    }
-
-    if resp.StatusCode != http.StatusOK {
-        notification.Error("Sync Config Error", string(notificationPayloadBytes))
-        return
-    }
-
-    notification.Success("Sync Config Success", string(notificationPayloadBytes))
-
-    // handle rename
-    if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
-        return
-    }
-
-    payload := &RenameConfigPayload{
-        Filepath:    p.Filepath,
-        NewFilepath: p.NewFilepath,
-    }
-
-    err = payload.rename(env)
-
-    return
+	client := http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
+		},
+	}
+	url, err := env.GetUrl("/api/config")
+	if err != nil {
+		return
+	}
+	req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
+	if err != nil {
+		return
+	}
+	req.Header.Set("X-Node-Secret", env.Token)
+	resp, err := client.Do(req)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	respBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return
+	}
+
+	notificationPayload := &SyncNotificationPayload{
+		StatusCode: resp.StatusCode,
+		ConfigName: c.Name,
+		EnvName:    env.Name,
+		RespBody:   string(respBody),
+	}
+
+	notificationPayloadBytes, err := json.Marshal(notificationPayload)
+	if err != nil {
+		return
+	}
+
+	if resp.StatusCode != http.StatusOK {
+		notification.Error("Sync Config Error", string(notificationPayloadBytes))
+		return
+	}
+
+	notification.Success("Sync Config Success", string(notificationPayloadBytes))
+
+	// handle rename
+	if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
+		return
+	}
+
+	payload := &RenameConfigPayload{
+		Filepath:    p.Filepath,
+		NewFilepath: p.NewFilepath,
+	}
+
+	err = payload.rename(env)
+
+	return
 }
 }
 
 
 type RenameConfigPayload struct {
 type RenameConfigPayload struct {
-    Filepath    string `json:"filepath"`
-    NewFilepath string `json:"new_filepath"`
+	Filepath    string `json:"filepath"`
+	NewFilepath string `json:"new_filepath"`
 }
 }
 
 
 type SyncRenameNotificationPayload struct {
 type SyncRenameNotificationPayload struct {
-    StatusCode int    `json:"status_code"`
-    OrigPath   string `json:"orig_path"`
-    NewPath    string `json:"new_path"`
-    EnvName    string `json:"env_name"`
-    RespBody   string `json:"resp_body"`
+	StatusCode int    `json:"status_code"`
+	OrigPath   string `json:"orig_path"`
+	NewPath    string `json:"new_path"`
+	EnvName    string `json:"env_name"`
+	RespBody   string `json:"resp_body"`
 }
 }
 
 
 func (p *RenameConfigPayload) rename(env *model.Environment) (err error) {
 func (p *RenameConfigPayload) rename(env *model.Environment) (err error) {
-    // handle rename
-    if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
-        return
-    }
-
-    client := http.Client{
-        Transport: &http.Transport{
-            TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
-        },
-    }
-
-    payloadBytes, err := json.Marshal(gin.H{
-        "base_path": strings.ReplaceAll(filepath.Dir(p.Filepath), nginx.GetConfPath(), ""),
-        "orig_name": filepath.Base(p.Filepath),
-        "new_name":  filepath.Base(p.NewFilepath),
-    })
-    if err != nil {
-        return
-    }
-    url, err := env.GetUrl("/api/config_rename")
-    if err != nil {
-        return
-    }
-    req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
-    if err != nil {
-        return
-    }
-    req.Header.Set("X-Node-Secret", env.Token)
-    resp, err := client.Do(req)
-    if err != nil {
-        return
-    }
-    defer resp.Body.Close()
-
-    respBody, err := io.ReadAll(resp.Body)
-    if err != nil {
-        return
-    }
-
-    notificationPayload := &SyncRenameNotificationPayload{
-        StatusCode: resp.StatusCode,
-        OrigPath:   p.Filepath,
-        NewPath:    p.NewFilepath,
-        EnvName:    env.Name,
-        RespBody:   string(respBody),
-    }
-
-    notificationPayloadBytes, err := json.Marshal(notificationPayload)
-    if err != nil {
-        return
-    }
-
-    if resp.StatusCode != http.StatusOK {
-        notification.Error("Rename Remote Config Error", string(notificationPayloadBytes))
-        return
-    }
-
-    notification.Success("Rename Remote Config Success", string(notificationPayloadBytes))
-
-    return
+	// handle rename
+	if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
+		return
+	}
+
+	client := http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
+		},
+	}
+
+	payloadBytes, err := json.Marshal(gin.H{
+		"base_path": strings.ReplaceAll(filepath.Dir(p.Filepath), nginx.GetConfPath(), ""),
+		"orig_name": filepath.Base(p.Filepath),
+		"new_name":  filepath.Base(p.NewFilepath),
+	})
+	if err != nil {
+		return
+	}
+	url, err := env.GetUrl("/api/config_rename")
+	if err != nil {
+		return
+	}
+	req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
+	if err != nil {
+		return
+	}
+	req.Header.Set("X-Node-Secret", env.Token)
+	resp, err := client.Do(req)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	respBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return
+	}
+
+	notificationPayload := &SyncRenameNotificationPayload{
+		StatusCode: resp.StatusCode,
+		OrigPath:   p.Filepath,
+		NewPath:    p.NewFilepath,
+		EnvName:    env.Name,
+		RespBody:   string(respBody),
+	}
+
+	notificationPayloadBytes, err := json.Marshal(notificationPayload)
+	if err != nil {
+		return
+	}
+
+	if resp.StatusCode != http.StatusOK {
+		notification.Error("Rename Remote Config Error", string(notificationPayloadBytes))
+		return
+	}
+
+	notification.Success("Rename Remote Config Success", string(notificationPayloadBytes))
+
+	return
 }
 }