mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
feat: use settings file to predefine nodes #169
This commit is contained in:
parent
b429c15893
commit
a689608bdb
7 changed files with 191 additions and 48 deletions
|
@ -1,45 +1,46 @@
|
||||||
|
; suppress inspection "DuplicateKeyInSection" for whole file
|
||||||
[server]
|
[server]
|
||||||
HttpPort = 9000
|
HttpPort = 9000
|
||||||
RunMode = debug
|
RunMode = debug
|
||||||
JwtSecret =
|
JwtSecret =
|
||||||
Email =
|
Email =
|
||||||
HTTPChallengePort = 9180
|
HTTPChallengePort = 9180
|
||||||
StartCmd = bash
|
StartCmd = bash
|
||||||
Database = database
|
Database = database
|
||||||
CADir =
|
CADir =
|
||||||
GithubProxy =
|
GithubProxy =
|
||||||
NodeSecret =
|
NodeSecret =
|
||||||
Demo = false
|
Demo = false
|
||||||
PageSize = 10
|
PageSize = 10
|
||||||
HttpHost = 0.0.0.0
|
HttpHost = 0.0.0.0
|
||||||
CertRenewalInterval = 7
|
CertRenewalInterval = 7
|
||||||
RecursiveNameservers =
|
RecursiveNameservers =
|
||||||
SkipInstallation = false
|
SkipInstallation = false
|
||||||
Name =
|
Name =
|
||||||
|
|
||||||
[nginx]
|
[nginx]
|
||||||
AccessLogPath = /var/log/nginx/access.log
|
AccessLogPath = /var/log/nginx/access.log
|
||||||
ErrorLogPath = /var/log/nginx/error.log
|
ErrorLogPath = /var/log/nginx/error.log
|
||||||
ConfigDir =
|
ConfigDir =
|
||||||
PIDPath =
|
PIDPath =
|
||||||
TestConfigCmd =
|
TestConfigCmd =
|
||||||
ReloadCmd =
|
ReloadCmd =
|
||||||
RestartCmd =
|
RestartCmd =
|
||||||
|
|
||||||
[openai]
|
[openai]
|
||||||
Model =
|
Model =
|
||||||
BaseUrl =
|
BaseUrl =
|
||||||
Proxy =
|
Proxy =
|
||||||
Token =
|
Token =
|
||||||
|
|
||||||
[casdoor]
|
[casdoor]
|
||||||
Endpoint =
|
Endpoint =
|
||||||
ClientId =
|
ClientId =
|
||||||
ClientSecret =
|
ClientSecret =
|
||||||
Certificate =
|
Certificate =
|
||||||
Organization =
|
Organization =
|
||||||
Application =
|
Application =
|
||||||
RedirectUri =
|
RedirectUri =
|
||||||
|
|
||||||
[logrotate]
|
[logrotate]
|
||||||
Enabled = false
|
Enabled = false
|
||||||
|
@ -47,4 +48,6 @@ CMD = logrotate /etc/logrotate.d/nginx
|
||||||
Interval = 1440
|
Interval = 1440
|
||||||
|
|
||||||
[cluster]
|
[cluster]
|
||||||
Node =
|
Node = http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true
|
||||||
|
Node = http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=true
|
||||||
|
Node = http://10.0.0.3?name=node3&node_secret=my-node-secret&enabled=true
|
||||||
|
|
|
@ -44,6 +44,7 @@ func InitAfterDatabase() {
|
||||||
registerPredefinedUser,
|
registerPredefinedUser,
|
||||||
cert.InitRegister,
|
cert.InitRegister,
|
||||||
InitCronJobs,
|
InitCronJobs,
|
||||||
|
registerPredefinedClusterNodes,
|
||||||
analytic.RetrieveNodesStatus,
|
analytic.RetrieveNodesStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
internal/kernal/cluster.go
Normal file
71
internal/kernal/cluster.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package kernal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/0xJacky/Nginx-UI/internal/logger"
|
||||||
|
"github.com/0xJacky/Nginx-UI/model"
|
||||||
|
"github.com/0xJacky/Nginx-UI/query"
|
||||||
|
"github.com/0xJacky/Nginx-UI/settings"
|
||||||
|
"gorm.io/gen/field"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerPredefinedClusterNodes() {
|
||||||
|
if len(settings.ClusterSettings.Node) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q := query.Environment
|
||||||
|
for _, nodeUrl := range settings.ClusterSettings.Node {
|
||||||
|
func() {
|
||||||
|
node, err := parseNodeUrl(nodeUrl)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(nodeUrl, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Name == "" {
|
||||||
|
logger.Error(nodeUrl, "Node name is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.URL == "" {
|
||||||
|
logger.Error(nodeUrl, "Node URL is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Token == "" {
|
||||||
|
logger.Error(nodeUrl, "Node Token is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = q.Where(q.URL.Eq(node.URL)).
|
||||||
|
Attrs(field.Attrs(node)).
|
||||||
|
FirstOrCreate()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(node.URL, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNodeUrl(nodeUrl string) (env *model.Environment, err error) {
|
||||||
|
u, err := url.Parse(nodeUrl)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteString(u.Scheme)
|
||||||
|
sb.WriteString("://")
|
||||||
|
sb.WriteString(u.Host)
|
||||||
|
sb.WriteString(u.Path)
|
||||||
|
|
||||||
|
env = &model.Environment{
|
||||||
|
Name: u.Query().Get("name"),
|
||||||
|
URL: sb.String(),
|
||||||
|
Token: u.Query().Get("node_secret"),
|
||||||
|
Enabled: u.Query().Get("enabled") == "true",
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
47
internal/kernal/cluster_test.go
Normal file
47
internal/kernal/cluster_test.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package kernal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/0xJacky/Nginx-UI/settings"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_parseNodeUrl(t *testing.T) {
|
||||||
|
settings.Init("../../app.example.ini")
|
||||||
|
t.Log(settings.ClusterSettings.Node)
|
||||||
|
node := settings.ClusterSettings.Node[0]
|
||||||
|
|
||||||
|
env, err := parseNodeUrl(node)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "node1", env.Name)
|
||||||
|
assert.Equal(t, "http://10.0.0.1:9000", env.URL)
|
||||||
|
assert.Equal(t, "my-node-secret", env.Token)
|
||||||
|
assert.Equal(t, true, env.Enabled)
|
||||||
|
|
||||||
|
node = settings.ClusterSettings.Node[1]
|
||||||
|
|
||||||
|
env, err = parseNodeUrl(node)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "node2", env.Name)
|
||||||
|
assert.Equal(t, "http://10.0.0.2:9000", env.URL)
|
||||||
|
assert.Equal(t, "my-node-secret", env.Token)
|
||||||
|
assert.Equal(t, true, env.Enabled)
|
||||||
|
|
||||||
|
node = settings.ClusterSettings.Node[2]
|
||||||
|
|
||||||
|
env, err = parseNodeUrl(node)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "node3", env.Name)
|
||||||
|
assert.Equal(t, "http://10.0.0.3", env.URL)
|
||||||
|
assert.Equal(t, "my-node-secret", env.Token)
|
||||||
|
assert.Equal(t, true, env.Enabled)
|
||||||
|
}
|
|
@ -3,3 +3,7 @@ package settings
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
Node []string `ini:",,allowshadow"`
|
Node []string `ini:",,allowshadow"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ClusterSettings = Cluster{
|
||||||
|
Node: []string{},
|
||||||
|
}
|
||||||
|
|
15
settings/cluster_test.go
Normal file
15
settings/cluster_test.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCluster(t *testing.T) {
|
||||||
|
Init("../app.example.ini")
|
||||||
|
|
||||||
|
assert.Equal(t, []string{
|
||||||
|
"http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true",
|
||||||
|
"http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=true",
|
||||||
|
}, ClusterSettings.Node)
|
||||||
|
}
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ var sections = map[string]interface{}{
|
||||||
"openai": &OpenAISettings,
|
"openai": &OpenAISettings,
|
||||||
"casdoor": &CasdoorSettings,
|
"casdoor": &CasdoorSettings,
|
||||||
"logrotate": &LogrotateSettings,
|
"logrotate": &LogrotateSettings,
|
||||||
|
"cluster": &ClusterSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -40,10 +41,15 @@ func Init(confPath string) {
|
||||||
|
|
||||||
func Setup() {
|
func Setup() {
|
||||||
var err error
|
var err error
|
||||||
Conf, err = ini.LooseLoad(ConfPath)
|
Conf, err = ini.LoadSources(ini.LoadOptions{
|
||||||
|
Loose: true,
|
||||||
|
AllowShadows: true,
|
||||||
|
}, ConfPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("settings.Setup: %v\n", err)
|
log.Fatalf("settings.Setup: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
MapTo()
|
MapTo()
|
||||||
|
|
||||||
parseEnv(&ServerSettings, "SERVER_")
|
parseEnv(&ServerSettings, "SERVER_")
|
||||||
|
@ -70,8 +76,6 @@ func MapTo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func Save() (err error) {
|
func Save() (err error) {
|
||||||
for k, v := range sections {
|
for k, v := range sections {
|
||||||
reflectFrom(k, v)
|
reflectFrom(k, v)
|
||||||
|
@ -85,30 +89,30 @@ func Save() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProtectedFill(targetSettings interface{}, newSettings interface{}) {
|
func ProtectedFill(targetSettings interface{}, newSettings interface{}) {
|
||||||
s := reflect.TypeOf(targetSettings).Elem()
|
s := reflect.TypeOf(targetSettings).Elem()
|
||||||
vt := reflect.ValueOf(targetSettings).Elem()
|
vt := reflect.ValueOf(targetSettings).Elem()
|
||||||
vn := reflect.ValueOf(newSettings).Elem()
|
vn := reflect.ValueOf(newSettings).Elem()
|
||||||
|
|
||||||
// copy the values from new to target settings if it is not protected
|
// copy the values from new to target settings if it is not protected
|
||||||
for i := 0; i < s.NumField(); i++ {
|
for i := 0; i < s.NumField(); i++ {
|
||||||
if s.Field(i).Tag.Get("protected") != "true" {
|
if s.Field(i).Tag.Get("protected") != "true" {
|
||||||
vt.Field(i).Set(vn.Field(i))
|
vt.Field(i).Set(vn.Field(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapTo(section string, v interface{}) {
|
func mapTo(section string, v interface{}) {
|
||||||
err := Conf.Section(section).MapTo(v)
|
err := Conf.Section(section).MapTo(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Cfg.MapTo %s err: %v", section, err)
|
log.Fatalf("Cfg.MapTo %s err: %v", section, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reflectFrom(section string, v interface{}) {
|
func reflectFrom(section string, v interface{}) {
|
||||||
err := Conf.Section(section).ReflectFrom(v)
|
err := Conf.Section(section).ReflectFrom(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Cfg.ReflectFrom %s err: %v", section, err)
|
log.Fatalf("Cfg.ReflectFrom %s err: %v", section, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEnv(ptr interface{}, prefix string) {
|
func parseEnv(ptr interface{}, prefix string) {
|
||||||
|
@ -121,5 +125,3 @@ func parseEnv(ptr interface{}, prefix string) {
|
||||||
log.Fatalf("settings.parseEnv: %v\n", err)
|
log.Fatalf("settings.parseEnv: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue