mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-10 18:05:48 +02:00
docs: update docs about insecure skip verify
This commit is contained in:
parent
f1c0f8ddca
commit
6c7b644f60
9 changed files with 467 additions and 443 deletions
|
@ -1,18 +1,18 @@
|
||||||
package openai
|
package openai
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/0xJacky/Nginx-UI/api"
|
"github.com/0xJacky/Nginx-UI/api"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/chatbot"
|
"github.com/0xJacky/Nginx-UI/internal/chatbot"
|
||||||
"github.com/0xJacky/Nginx-UI/settings"
|
"github.com/0xJacky/Nginx-UI/settings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"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 {
|
var json struct {
|
||||||
Filepath string `json:"filepath"`
|
Filepath string `json:"filepath"`
|
||||||
Messages []openai.ChatCompletionMessage `json:"messages"`
|
Messages []openai.ChatCompletionMessage `json:"messages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if !api.BindAndValid(c, &json) {
|
if !api.BindAndValid(c, &json) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
messages := []openai.ChatCompletionMessage{
|
messages := []openai.ChatCompletionMessage{
|
||||||
{
|
{
|
||||||
Role: openai.ChatMessageRoleSystem,
|
Role: openai.ChatMessageRoleSystem,
|
||||||
Content: ChatGPTInitPrompt,
|
Content: ChatGPTInitPrompt,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
messages = append(messages, json.Messages...)
|
messages = append(messages, json.Messages...)
|
||||||
|
|
||||||
if json.Filepath != "" {
|
if json.Filepath != "" {
|
||||||
messages = chatbot.ChatCompletionWithContext(json.Filepath, messages)
|
messages = chatbot.ChatCompletionWithContext(json.Filepath, messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSE server
|
// SSE server
|
||||||
c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
|
c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
|
||||||
c.Writer.Header().Set("Cache-Control", "no-cache")
|
c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||||
c.Writer.Header().Set("Connection", "keep-alive")
|
c.Writer.Header().Set("Connection", "keep-alive")
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
|
||||||
config := openai.DefaultConfig(settings.OpenAISettings.Token)
|
config := openai.DefaultConfig(settings.OpenAISettings.Token)
|
||||||
|
|
||||||
if settings.OpenAISettings.Proxy != "" {
|
if settings.OpenAISettings.Proxy != "" {
|
||||||
proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
|
proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Stream(func(w io.Writer) bool {
|
c.Stream(func(w io.Writer) bool {
|
||||||
c.SSEvent("message", gin.H{
|
c.SSEvent("message", gin.H{
|
||||||
"type": "error",
|
"type": "error",
|
||||||
"content": err.Error(),
|
"content": err.Error(),
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
Proxy: http.ProxyURL(proxyUrl),
|
Proxy: http.ProxyURL(proxyUrl),
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
||||||
}
|
}
|
||||||
config.HTTPClient = &http.Client{
|
config.HTTPClient = &http.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.OpenAISettings.BaseUrl != "" {
|
if settings.OpenAISettings.BaseUrl != "" {
|
||||||
config.BaseURL = settings.OpenAISettings.BaseUrl
|
config.BaseURL = settings.OpenAISettings.BaseUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
openaiClient := openai.NewClientWithConfig(config)
|
openaiClient := openai.NewClientWithConfig(config)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
req := openai.ChatCompletionRequest{
|
req := openai.ChatCompletionRequest{
|
||||||
Model: settings.OpenAISettings.Model,
|
Model: settings.OpenAISettings.Model,
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
Stream: true,
|
Stream: true,
|
||||||
}
|
}
|
||||||
stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
|
stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("CompletionStream error: %v\n", err)
|
fmt.Printf("CompletionStream error: %v\n", err)
|
||||||
c.Stream(func(w io.Writer) bool {
|
c.Stream(func(w io.Writer) bool {
|
||||||
c.SSEvent("message", gin.H{
|
c.SSEvent("message", gin.H{
|
||||||
"type": "error",
|
"type": "error",
|
||||||
"content": err.Error(),
|
"content": err.Error(),
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
msgChan := make(chan string)
|
msgChan := make(chan string)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(msgChan)
|
defer close(msgChan)
|
||||||
for {
|
for {
|
||||||
response, err := stream.Recv()
|
response, err := stream.Recv()
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Stream error: %v\n", err)
|
fmt.Printf("Stream error: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
|
message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
|
||||||
|
|
||||||
msgChan <- message
|
msgChan <- message
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
c.Stream(func(w io.Writer) bool {
|
c.Stream(func(w io.Writer) bool {
|
||||||
if m, ok := <-msgChan; ok {
|
if m, ok := <-msgChan; ok {
|
||||||
c.SSEvent("message", gin.H{
|
c.SSEvent("message", gin.H{
|
||||||
"type": "message",
|
"type": "message",
|
||||||
"content": m,
|
"content": m,
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -3,24 +3,25 @@ Applicable for version v2.0.0-beta.23 and above.
|
||||||
|
|
||||||
## Server
|
## Server
|
||||||
|
|
||||||
| Configuration Setting | Environment Variable |
|
| Configuration Setting | Environment Variable |
|
||||||
| ----------------------------- | ------------------------------------- |
|
|-----------------------|---------------------------------------|
|
||||||
| HttpPort | NGINX_UI_SERVER_HTTP_PORT |
|
| HttpPort | NGINX_UI_SERVER_HTTP_PORT |
|
||||||
| RunMode | NGINX_UI_SERVER_RUN_MODE |
|
| RunMode | NGINX_UI_SERVER_RUN_MODE |
|
||||||
| JwtSecret | NGINX_UI_SERVER_JWT_SECRET |
|
| JwtSecret | NGINX_UI_SERVER_JWT_SECRET |
|
||||||
| HTTPChallengePort | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT |
|
| HTTPChallengePort | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT |
|
||||||
| StartCmd | NGINX_UI_SERVER_START_CMD |
|
| StartCmd | NGINX_UI_SERVER_START_CMD |
|
||||||
| Database | NGINX_UI_SERVER_DATABASE |
|
| Database | NGINX_UI_SERVER_DATABASE |
|
||||||
| CADir | NGINX_UI_SERVER_CA_DIR |
|
| CADir | NGINX_UI_SERVER_CA_DIR |
|
||||||
| GithubProxy | NGINX_UI_SERVER_GITHUB_PROXY |
|
| GithubProxy | NGINX_UI_SERVER_GITHUB_PROXY |
|
||||||
| NodeSecret | NGINX_UI_SERVER_NODE_SECRET |
|
| NodeSecret | NGINX_UI_SERVER_NODE_SECRET |
|
||||||
| Demo | NGINX_UI_SERVER_DEMO |
|
| Demo | NGINX_UI_SERVER_DEMO |
|
||||||
| PageSize | NGINX_UI_SERVER_PAGE_SIZE |
|
| PageSize | NGINX_UI_SERVER_PAGE_SIZE |
|
||||||
| HttpHost | NGINX_UI_SERVER_HTTP_HOST |
|
| HttpHost | NGINX_UI_SERVER_HTTP_HOST |
|
||||||
| CertRenewalInterval | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
|
| CertRenewalInterval | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
|
||||||
| RecursiveNameservers | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
|
| RecursiveNameservers | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
|
||||||
| SkipInstallation | NGINX_UI_SERVER_SKIP_INSTALLATION |
|
| SkipInstallation | NGINX_UI_SERVER_SKIP_INSTALLATION |
|
||||||
| Name | NGINX_UI_SERVER_NAME |
|
| Name | NGINX_UI_SERVER_NAME |
|
||||||
|
| InsecureSkipVerify | NGINX_UI_SERVER_INSECURE_SKIP_VERIFY |
|
||||||
|
|
||||||
## Nginx
|
## Nginx
|
||||||
|
|
||||||
|
|
|
@ -132,3 +132,10 @@ Nginx UI 将不会创建系统初始的 acme 用户,这意味着您无法在
|
||||||
- 类型:`string`
|
- 类型:`string`
|
||||||
|
|
||||||
使用此选项自定义本地服务器的名称,以在环境指示器中显示。
|
使用此选项自定义本地服务器的名称,以在环境指示器中显示。
|
||||||
|
|
||||||
|
## InsecureSkipVerify
|
||||||
|
|
||||||
|
- 版本:`>= v2.0.0-beta.30`
|
||||||
|
- 类型: `bool`
|
||||||
|
|
||||||
|
此选项用于配置 Nginx UI 服务器在与其他服务器建立 TLS 连接时是否跳过证书验证。
|
||||||
|
|
|
@ -3,24 +3,25 @@
|
||||||
|
|
||||||
## Server
|
## Server
|
||||||
|
|
||||||
| Configuration Setting | Environment Variable |
|
| Configuration Setting | Environment Variable |
|
||||||
| ----------------------------- | ------------------------------------- |
|
|-------------------------| ------------------------------------- |
|
||||||
| HttpPort | NGINX_UI_SERVER_HTTP_PORT |
|
| HttpPort | NGINX_UI_SERVER_HTTP_PORT |
|
||||||
| RunMode | NGINX_UI_SERVER_RUN_MODE |
|
| RunMode | NGINX_UI_SERVER_RUN_MODE |
|
||||||
| JwtSecret | NGINX_UI_SERVER_JWT_SECRET |
|
| JwtSecret | NGINX_UI_SERVER_JWT_SECRET |
|
||||||
| HTTPChallengePort | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT |
|
| HTTPChallengePort | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT |
|
||||||
| StartCmd | NGINX_UI_SERVER_START_CMD |
|
| StartCmd | NGINX_UI_SERVER_START_CMD |
|
||||||
| Database | NGINX_UI_SERVER_DATABASE |
|
| Database | NGINX_UI_SERVER_DATABASE |
|
||||||
| CADir | NGINX_UI_SERVER_CA_DIR |
|
| CADir | NGINX_UI_SERVER_CA_DIR |
|
||||||
| GithubProxy | NGINX_UI_SERVER_GITHUB_PROXY |
|
| GithubProxy | NGINX_UI_SERVER_GITHUB_PROXY |
|
||||||
| NodeSecret | NGINX_UI_SERVER_NODE_SECRET |
|
| NodeSecret | NGINX_UI_SERVER_NODE_SECRET |
|
||||||
| Demo | NGINX_UI_SERVER_DEMO |
|
| Demo | NGINX_UI_SERVER_DEMO |
|
||||||
| PageSize | NGINX_UI_SERVER_PAGE_SIZE |
|
| PageSize | NGINX_UI_SERVER_PAGE_SIZE |
|
||||||
| HttpHost | NGINX_UI_SERVER_HTTP_HOST |
|
| HttpHost | NGINX_UI_SERVER_HTTP_HOST |
|
||||||
| CertRenewalInterval | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
|
| CertRenewalInterval | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
|
||||||
| RecursiveNameservers | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
|
| RecursiveNameservers | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
|
||||||
| SkipInstallation | NGINX_UI_SERVER_SKIP_INSTALLATION |
|
| SkipInstallation | NGINX_UI_SERVER_SKIP_INSTALLATION |
|
||||||
| Name | NGINX_UI_SERVER_NAME |
|
| Name | NGINX_UI_SERVER_NAME |
|
||||||
|
| InsecureSkipVerify | NGINX_UI_SERVER_INSECURE_SKIP_VERIFY |
|
||||||
|
|
||||||
## Nginx
|
## Nginx
|
||||||
|
|
||||||
|
|
|
@ -133,3 +133,10 @@ Nginx UI 將不會創建系統初始的 acme 使用者,這意味著您無法
|
||||||
- 類型:`string`
|
- 類型:`string`
|
||||||
|
|
||||||
使用此選項自定義本地伺服器的名稱,以在環境指示器中顯示。
|
使用此選項自定義本地伺服器的名稱,以在環境指示器中顯示。
|
||||||
|
|
||||||
|
## InsecureSkipVerify
|
||||||
|
|
||||||
|
- 版本:`>= v2.0.0-beta.30`
|
||||||
|
- 類型: `bool`
|
||||||
|
|
||||||
|
此選項用於配置 Nginx UI 伺服器在與其他伺服器建立 TLS 連接時是否跳過證書驗證。
|
||||||
|
|
|
@ -3,24 +3,25 @@
|
||||||
|
|
||||||
## Server
|
## Server
|
||||||
|
|
||||||
| Configuration Setting | Environment Variable |
|
| Configuration Setting | Environment Variable |
|
||||||
| ----------------------------- | ------------------------------------- |
|
|------------------------| ------------------------------------- |
|
||||||
| HttpPort | NGINX_UI_SERVER_HTTP_PORT |
|
| HttpPort | NGINX_UI_SERVER_HTTP_PORT |
|
||||||
| RunMode | NGINX_UI_SERVER_RUN_MODE |
|
| RunMode | NGINX_UI_SERVER_RUN_MODE |
|
||||||
| JwtSecret | NGINX_UI_SERVER_JWT_SECRET |
|
| JwtSecret | NGINX_UI_SERVER_JWT_SECRET |
|
||||||
| HTTPChallengePort | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT |
|
| HTTPChallengePort | NGINX_UI_SERVER_HTTP_CHALLENGE_PORT |
|
||||||
| StartCmd | NGINX_UI_SERVER_START_CMD |
|
| StartCmd | NGINX_UI_SERVER_START_CMD |
|
||||||
| Database | NGINX_UI_SERVER_DATABASE |
|
| Database | NGINX_UI_SERVER_DATABASE |
|
||||||
| CADir | NGINX_UI_SERVER_CA_DIR |
|
| CADir | NGINX_UI_SERVER_CA_DIR |
|
||||||
| GithubProxy | NGINX_UI_SERVER_GITHUB_PROXY |
|
| GithubProxy | NGINX_UI_SERVER_GITHUB_PROXY |
|
||||||
| NodeSecret | NGINX_UI_SERVER_NODE_SECRET |
|
| NodeSecret | NGINX_UI_SERVER_NODE_SECRET |
|
||||||
| Demo | NGINX_UI_SERVER_DEMO |
|
| Demo | NGINX_UI_SERVER_DEMO |
|
||||||
| PageSize | NGINX_UI_SERVER_PAGE_SIZE |
|
| PageSize | NGINX_UI_SERVER_PAGE_SIZE |
|
||||||
| HttpHost | NGINX_UI_SERVER_HTTP_HOST |
|
| HttpHost | NGINX_UI_SERVER_HTTP_HOST |
|
||||||
| CertRenewalInterval | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
|
| CertRenewalInterval | NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL |
|
||||||
| RecursiveNameservers | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
|
| RecursiveNameservers | NGINX_UI_SERVER_RECURSIVE_NAMESERVERS |
|
||||||
| SkipInstallation | NGINX_UI_SERVER_SKIP_INSTALLATION |
|
| SkipInstallation | NGINX_UI_SERVER_SKIP_INSTALLATION |
|
||||||
| Name | NGINX_UI_SERVER_NAME |
|
| Name | NGINX_UI_SERVER_NAME |
|
||||||
|
| InsecureSkipVerify | NGINX_UI_SERVER_INSECURE_SKIP_VERIFY |
|
||||||
|
|
||||||
## Nginx
|
## Nginx
|
||||||
|
|
||||||
|
|
|
@ -1,44 +1,44 @@
|
||||||
package analytic
|
package analytic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/logger"
|
"github.com/0xJacky/Nginx-UI/internal/logger"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/upgrader"
|
"github.com/0xJacky/Nginx-UI/internal/upgrader"
|
||||||
"github.com/0xJacky/Nginx-UI/model"
|
"github.com/0xJacky/Nginx-UI/model"
|
||||||
"github.com/0xJacky/Nginx-UI/settings"
|
"github.com/0xJacky/Nginx-UI/settings"
|
||||||
"github.com/shirou/gopsutil/v3/load"
|
"github.com/shirou/gopsutil/v3/load"
|
||||||
"github.com/shirou/gopsutil/v3/net"
|
"github.com/shirou/gopsutil/v3/net"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
|
NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
CPUNum int `json:"cpu_num"`
|
CPUNum int `json:"cpu_num"`
|
||||||
MemoryTotal string `json:"memory_total"`
|
MemoryTotal string `json:"memory_total"`
|
||||||
DiskTotal string `json:"disk_total"`
|
DiskTotal string `json:"disk_total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeStat struct {
|
type NodeStat struct {
|
||||||
AvgLoad *load.AvgStat `json:"avg_load"`
|
AvgLoad *load.AvgStat `json:"avg_load"`
|
||||||
CPUPercent float64 `json:"cpu_percent"`
|
CPUPercent float64 `json:"cpu_percent"`
|
||||||
MemoryPercent float64 `json:"memory_percent"`
|
MemoryPercent float64 `json:"memory_percent"`
|
||||||
DiskPercent float64 `json:"disk_percent"`
|
DiskPercent float64 `json:"disk_percent"`
|
||||||
Network net.IOCountersStat `json:"network"`
|
Network net.IOCountersStat `json:"network"`
|
||||||
Status bool `json:"status"`
|
Status bool `json:"status"`
|
||||||
ResponseAt time.Time `json:"response_at"`
|
ResponseAt time.Time `json:"response_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
EnvironmentID int `json:"environment_id,omitempty"`
|
EnvironmentID int `json:"environment_id,omitempty"`
|
||||||
*model.Environment
|
*model.Environment
|
||||||
NodeStat
|
NodeStat
|
||||||
NodeInfo
|
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 {
|
if env == nil {
|
||||||
// this should never happen
|
// this should never happen
|
||||||
logger.Error("env is nil")
|
logger.Error("env is nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !env.Enabled {
|
if !env.Enabled {
|
||||||
return &Node{
|
return &Node{
|
||||||
Environment: env,
|
Environment: env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n, ok := NodeMap[env.ID]
|
n, ok := NodeMap[env.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
n = &Node{}
|
n = &Node{}
|
||||||
}
|
}
|
||||||
n.Environment = env
|
n.Environment = env
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitNode(env *model.Environment) (n *Node) {
|
func InitNode(env *model.Environment) (n *Node) {
|
||||||
n = &Node{
|
n = &Node{
|
||||||
Environment: env,
|
Environment: env,
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.JoinPath(env.URL, "/api/node")
|
u, err := url.JoinPath(env.URL, "/api/node")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", u, nil)
|
req, err := http.NewRequest("GET", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
bytes, _ := io.ReadAll(resp.Body)
|
bytes, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
logger.Error(string(bytes))
|
logger.Error(string(bytes))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(bytes, &n.NodeInfo)
|
err = json.Unmarshal(bytes, &n.NodeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,258 +1,258 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/logger"
|
"github.com/0xJacky/Nginx-UI/internal/logger"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/notification"
|
"github.com/0xJacky/Nginx-UI/internal/notification"
|
||||||
"github.com/0xJacky/Nginx-UI/model"
|
"github.com/0xJacky/Nginx-UI/model"
|
||||||
"github.com/0xJacky/Nginx-UI/query"
|
"github.com/0xJacky/Nginx-UI/query"
|
||||||
"github.com/0xJacky/Nginx-UI/settings"
|
"github.com/0xJacky/Nginx-UI/settings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SyncConfigPayload struct {
|
type SyncConfigPayload struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Filepath string `json:"filepath"`
|
Filepath string `json:"filepath"`
|
||||||
NewFilepath string `json:"new_filepath"`
|
NewFilepath string `json:"new_filepath"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Overwrite bool `json:"overwrite"`
|
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 {
|
if c.Filepath == "" || len(c.SyncNodeIds) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nginxConfPath := nginx.GetConfPath()
|
nginxConfPath := nginx.GetConfPath()
|
||||||
if !helper.IsUnderDirectory(c.Filepath, nginxConfPath) {
|
if !helper.IsUnderDirectory(c.Filepath, nginxConfPath) {
|
||||||
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
||||||
c.Filepath, nginxConfPath)
|
c.Filepath, nginxConfPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if newFilepath != "" && !helper.IsUnderDirectory(newFilepath, nginxConfPath) {
|
if newFilepath != "" && !helper.IsUnderDirectory(newFilepath, nginxConfPath) {
|
||||||
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
||||||
c.Filepath, nginxConfPath)
|
c.Filepath, nginxConfPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPath := c.Filepath
|
currentPath := c.Filepath
|
||||||
if newFilepath != "" {
|
if newFilepath != "" {
|
||||||
currentPath = newFilepath
|
currentPath = newFilepath
|
||||||
}
|
}
|
||||||
configBytes, err := os.ReadFile(currentPath)
|
configBytes, err := os.ReadFile(currentPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := &SyncConfigPayload{
|
payload := &SyncConfigPayload{
|
||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
Filepath: c.Filepath,
|
Filepath: c.Filepath,
|
||||||
NewFilepath: newFilepath,
|
NewFilepath: newFilepath,
|
||||||
Content: string(configBytes),
|
Content: string(configBytes),
|
||||||
Overwrite: c.SyncOverwrite,
|
Overwrite: c.SyncOverwrite,
|
||||||
}
|
}
|
||||||
payloadBytes, err := json.Marshal(payload)
|
payloadBytes, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
q := query.Environment
|
q := query.Environment
|
||||||
envs, _ := q.Where(q.ID.In(c.SyncNodeIds...)).Find()
|
envs, _ := q.Where(q.ID.In(c.SyncNodeIds...)).Find()
|
||||||
for _, env := range envs {
|
for _, env := range envs {
|
||||||
go func() {
|
go func() {
|
||||||
err := payload.deploy(env, c, payloadBytes)
|
err := payload.deploy(env, c, payloadBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
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 {
|
if origPath == "" || newPath == "" || len(syncNodeIds) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nginxConfPath := nginx.GetConfPath()
|
nginxConfPath := nginx.GetConfPath()
|
||||||
if !helper.IsUnderDirectory(origPath, nginxConfPath) {
|
if !helper.IsUnderDirectory(origPath, nginxConfPath) {
|
||||||
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
||||||
origPath, nginxConfPath)
|
origPath, nginxConfPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !helper.IsUnderDirectory(newPath, nginxConfPath) {
|
if !helper.IsUnderDirectory(newPath, nginxConfPath) {
|
||||||
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
||||||
newPath, nginxConfPath)
|
newPath, nginxConfPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := &RenameConfigPayload{
|
payload := &RenameConfigPayload{
|
||||||
Filepath: origPath,
|
Filepath: origPath,
|
||||||
NewFilepath: newPath,
|
NewFilepath: newPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
q := query.Environment
|
q := query.Environment
|
||||||
envs, _ := q.Where(q.ID.In(syncNodeIds...)).Find()
|
envs, _ := q.Where(q.ID.In(syncNodeIds...)).Find()
|
||||||
for _, env := range envs {
|
for _, env := range envs {
|
||||||
go func() {
|
go func() {
|
||||||
err := payload.rename(env)
|
err := payload.rename(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncNotificationPayload struct {
|
type SyncNotificationPayload struct {
|
||||||
StatusCode int `json:"status_code"`
|
StatusCode int `json:"status_code"`
|
||||||
ConfigName string `json:"config_name"`
|
ConfigName string `json:"config_name"`
|
||||||
EnvName string `json:"env_name"`
|
EnvName string `json:"env_name"`
|
||||||
RespBody string `json:"resp_body"`
|
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{
|
client := http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
url, err := env.GetUrl("/api/config")
|
url, err := env.GetUrl("/api/config")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationPayload := &SyncNotificationPayload{
|
notificationPayload := &SyncNotificationPayload{
|
||||||
StatusCode: resp.StatusCode,
|
StatusCode: resp.StatusCode,
|
||||||
ConfigName: c.Name,
|
ConfigName: c.Name,
|
||||||
EnvName: env.Name,
|
EnvName: env.Name,
|
||||||
RespBody: string(respBody),
|
RespBody: string(respBody),
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationPayloadBytes, err := json.Marshal(notificationPayload)
|
notificationPayloadBytes, err := json.Marshal(notificationPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
notification.Error("Sync Config Error", string(notificationPayloadBytes))
|
notification.Error("Sync Config Error", string(notificationPayloadBytes))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.Success("Sync Config Success", string(notificationPayloadBytes))
|
notification.Success("Sync Config Success", string(notificationPayloadBytes))
|
||||||
|
|
||||||
// handle rename
|
// handle rename
|
||||||
if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
|
if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := &RenameConfigPayload{
|
payload := &RenameConfigPayload{
|
||||||
Filepath: p.Filepath,
|
Filepath: p.Filepath,
|
||||||
NewFilepath: p.NewFilepath,
|
NewFilepath: p.NewFilepath,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = payload.rename(env)
|
err = payload.rename(env)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type RenameConfigPayload struct {
|
type RenameConfigPayload struct {
|
||||||
Filepath string `json:"filepath"`
|
Filepath string `json:"filepath"`
|
||||||
NewFilepath string `json:"new_filepath"`
|
NewFilepath string `json:"new_filepath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncRenameNotificationPayload struct {
|
type SyncRenameNotificationPayload struct {
|
||||||
StatusCode int `json:"status_code"`
|
StatusCode int `json:"status_code"`
|
||||||
OrigPath string `json:"orig_path"`
|
OrigPath string `json:"orig_path"`
|
||||||
NewPath string `json:"new_path"`
|
NewPath string `json:"new_path"`
|
||||||
EnvName string `json:"env_name"`
|
EnvName string `json:"env_name"`
|
||||||
RespBody string `json:"resp_body"`
|
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
|
// handle rename
|
||||||
if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
|
if p.NewFilepath == "" || p.Filepath == p.NewFilepath {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: settings.ServerSettings.InsecureSkipVerify},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
payloadBytes, err := json.Marshal(gin.H{
|
payloadBytes, err := json.Marshal(gin.H{
|
||||||
"base_path": strings.ReplaceAll(filepath.Dir(p.Filepath), nginx.GetConfPath(), ""),
|
"base_path": strings.ReplaceAll(filepath.Dir(p.Filepath), nginx.GetConfPath(), ""),
|
||||||
"orig_name": filepath.Base(p.Filepath),
|
"orig_name": filepath.Base(p.Filepath),
|
||||||
"new_name": filepath.Base(p.NewFilepath),
|
"new_name": filepath.Base(p.NewFilepath),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
url, err := env.GetUrl("/api/config_rename")
|
url, err := env.GetUrl("/api/config_rename")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payloadBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationPayload := &SyncRenameNotificationPayload{
|
notificationPayload := &SyncRenameNotificationPayload{
|
||||||
StatusCode: resp.StatusCode,
|
StatusCode: resp.StatusCode,
|
||||||
OrigPath: p.Filepath,
|
OrigPath: p.Filepath,
|
||||||
NewPath: p.NewFilepath,
|
NewPath: p.NewFilepath,
|
||||||
EnvName: env.Name,
|
EnvName: env.Name,
|
||||||
RespBody: string(respBody),
|
RespBody: string(respBody),
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationPayloadBytes, err := json.Marshal(notificationPayload)
|
notificationPayloadBytes, err := json.Marshal(notificationPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
notification.Error("Rename Remote Config Error", string(notificationPayloadBytes))
|
notification.Error("Rename Remote Config Error", string(notificationPayloadBytes))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.Success("Rename Remote Config Success", string(notificationPayloadBytes))
|
notification.Success("Rename Remote Config Success", string(notificationPayloadBytes))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue