mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-22 20:58:14 -05:00
feat: multi user template
This commit is contained in:
parent
8755b65ac5
commit
9b0697491d
@ -27,7 +27,7 @@
|
|||||||
| 用户前台 [@hamster1963](https://github.com/hamster1963) | 管理后台 [@uubulb](https://github.com/uubulb) |
|
| 用户前台 [@hamster1963](https://github.com/hamster1963) | 管理后台 [@uubulb](https://github.com/uubulb) |
|
||||||
|---|---|
|
|---|---|
|
||||||
| ![user](.github/user-frontend.20241128.png) | ![admin](.github/admin-frontend.20241128.png) |
|
| ![user](.github/user-frontend.20241128.png) | ![admin](.github/admin-frontend.20241128.png) |
|
||||||
| [hamster1963/nezha-dash-react](https://github.com/hamster1963/nezha-dash-react) | [nezhahq/admin-frontend](https://github.com/nezhahq/admin-frontend) |
|
| [hamster1963/nezha-dash](https://github.com/hamster1963/nezha-dash) | [nezhahq/admin-frontend](https://github.com/nezhahq/admin-frontend) |
|
||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
|
@ -249,11 +249,11 @@ func fallbackToFrontend(adminFrontend, userFrontend fs.FS) func(*gin.Context) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
localFilePath := path.Join("user-dist", c.Request.URL.Path)
|
localFilePath := path.Join(singleton.Conf.UserTemplate, c.Request.URL.Path)
|
||||||
if checkLocalFileOrFs(c, userFrontend, localFilePath) {
|
if checkLocalFileOrFs(c, userFrontend, localFilePath) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !checkLocalFileOrFs(c, userFrontend, "user-dist/index.html") {
|
if !checkLocalFileOrFs(c, userFrontend, singleton.Conf.UserTemplate+"/index.html") {
|
||||||
c.JSON(http.StatusOK, newErrorResponse(errors.New("404 Not Found")))
|
c.JSON(http.StatusOK, newErrorResponse(errors.New("404 Not Found")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/nezhahq/nezha/model"
|
"github.com/nezhahq/nezha/model"
|
||||||
@ -23,6 +25,7 @@ func listConfig(c *gin.Context) (model.SettingResponse, error) {
|
|||||||
conf := model.SettingResponse{
|
conf := model.SettingResponse{
|
||||||
Config: *singleton.Conf,
|
Config: *singleton.Conf,
|
||||||
Version: singleton.Version,
|
Version: singleton.Version,
|
||||||
|
UserTemplates: singleton.UserTemplates,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !authorized {
|
if !authorized {
|
||||||
@ -55,7 +58,16 @@ func updateConfig(c *gin.Context) (any, error) {
|
|||||||
if err := c.ShouldBindJSON(&sf); err != nil {
|
if err := c.ShouldBindJSON(&sf); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var userTemplateValid bool
|
||||||
|
for _, v := range singleton.UserTemplates {
|
||||||
|
if v.Path == sf.UserTemplate {
|
||||||
|
userTemplateValid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !userTemplateValid {
|
||||||
|
return nil, errors.New("invalid user template")
|
||||||
|
}
|
||||||
singleton.Conf.Language = sf.Language
|
singleton.Conf.Language = sf.Language
|
||||||
singleton.Conf.EnableIPChangeNotification = sf.EnableIPChangeNotification
|
singleton.Conf.EnableIPChangeNotification = sf.EnableIPChangeNotification
|
||||||
singleton.Conf.EnablePlainIPInNotification = sf.EnablePlainIPInNotification
|
singleton.Conf.EnablePlainIPInNotification = sf.EnablePlainIPInNotification
|
||||||
@ -69,6 +81,7 @@ func updateConfig(c *gin.Context) (any, error) {
|
|||||||
singleton.Conf.CustomCodeDashboard = sf.CustomCodeDashboard
|
singleton.Conf.CustomCodeDashboard = sf.CustomCodeDashboard
|
||||||
singleton.Conf.RealIPHeader = sf.RealIPHeader
|
singleton.Conf.RealIPHeader = sf.RealIPHeader
|
||||||
singleton.Conf.TLS = sf.TLS
|
singleton.Conf.TLS = sf.TLS
|
||||||
|
singleton.Conf.UserTemplate = sf.UserTemplate
|
||||||
|
|
||||||
if err := singleton.Conf.Save(); err != nil {
|
if err := singleton.Conf.Save(); err != nil {
|
||||||
return nil, newGormError("%v", err)
|
return nil, newGormError("%v", err)
|
||||||
|
@ -82,7 +82,7 @@ func updateProfile(c *gin.Context) (any, error) {
|
|||||||
// @Router /user [get]
|
// @Router /user [get]
|
||||||
func listUser(c *gin.Context) ([]model.User, error) {
|
func listUser(c *gin.Context) ([]model.User, error) {
|
||||||
var users []model.User
|
var users []model.User
|
||||||
if err := singleton.DB.Find(&users).Error; err != nil {
|
if err := singleton.DB.Omit("password").Find(&users).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return users, nil
|
return users, nil
|
||||||
|
@ -27,6 +27,7 @@ type Config struct {
|
|||||||
|
|
||||||
Language string `mapstructure:"language" json:"language"` // 系统语言,默认 zh_CN
|
Language string `mapstructure:"language" json:"language"` // 系统语言,默认 zh_CN
|
||||||
SiteName string `mapstructure:"site_name" json:"site_name"`
|
SiteName string `mapstructure:"site_name" json:"site_name"`
|
||||||
|
UserTemplate string `mapstructure:"user_template" json:"user_template,omitempty"`
|
||||||
JWTSecretKey string `mapstructure:"jwt_secret_key" json:"jwt_secret_key,omitempty"`
|
JWTSecretKey string `mapstructure:"jwt_secret_key" json:"jwt_secret_key,omitempty"`
|
||||||
AgentSecretKey string `mapstructure:"agent_secret_key" json:"agent_secret_key,omitempty"`
|
AgentSecretKey string `mapstructure:"agent_secret_key" json:"agent_secret_key,omitempty"`
|
||||||
ListenPort uint `mapstructure:"listen_port" json:"listen_port,omitempty"`
|
ListenPort uint `mapstructure:"listen_port" json:"listen_port,omitempty"`
|
||||||
@ -55,7 +56,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read 读取配置文件并应用
|
// Read 读取配置文件并应用
|
||||||
func (c *Config) Read(path string) error {
|
func (c *Config) Read(path string, userTemplates []UserTemplate) error {
|
||||||
c.k = koanf.New(".")
|
c.k = koanf.New(".")
|
||||||
c.filePath = path
|
c.filePath = path
|
||||||
|
|
||||||
@ -87,6 +88,16 @@ func (c *Config) Read(path string) error {
|
|||||||
if c.Location == "" {
|
if c.Location == "" {
|
||||||
c.Location = "Asia/Shanghai"
|
c.Location = "Asia/Shanghai"
|
||||||
}
|
}
|
||||||
|
var userTemplateValid bool
|
||||||
|
for _, v := range userTemplates {
|
||||||
|
if v.Path == c.UserTemplate {
|
||||||
|
userTemplateValid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.UserTemplate == "" || !userTemplateValid {
|
||||||
|
c.UserTemplate = "user-dist"
|
||||||
|
}
|
||||||
if c.AvgPingCount == 0 {
|
if c.AvgPingCount == 0 {
|
||||||
c.AvgPingCount = 2
|
c.AvgPingCount = 2
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,24 @@ type SettingForm struct {
|
|||||||
CustomCode string `json:"custom_code,omitempty" validate:"optional"`
|
CustomCode string `json:"custom_code,omitempty" validate:"optional"`
|
||||||
CustomCodeDashboard string `json:"custom_code_dashboard,omitempty" validate:"optional"`
|
CustomCodeDashboard string `json:"custom_code_dashboard,omitempty" validate:"optional"`
|
||||||
RealIPHeader string `json:"real_ip_header,omitempty" validate:"optional"` // 真实IP
|
RealIPHeader string `json:"real_ip_header,omitempty" validate:"optional"` // 真实IP
|
||||||
|
UserTemplate string `json:"user_template,omitempty" validate:"optional"`
|
||||||
|
|
||||||
TLS bool `json:"tls,omitempty" validate:"optional"`
|
TLS bool `json:"tls,omitempty" validate:"optional"`
|
||||||
EnableIPChangeNotification bool `json:"enable_ip_change_notification,omitempty" validate:"optional"`
|
EnableIPChangeNotification bool `json:"enable_ip_change_notification,omitempty" validate:"optional"`
|
||||||
EnablePlainIPInNotification bool `json:"enable_plain_ip_in_notification,omitempty" validate:"optional"`
|
EnablePlainIPInNotification bool `json:"enable_plain_ip_in_notification,omitempty" validate:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserTemplate struct {
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
GitHub string `json:"github,omitempty"`
|
||||||
|
Author string `json:"author,omitempty"`
|
||||||
|
Community bool `json:"community,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type SettingResponse struct {
|
type SettingResponse struct {
|
||||||
Config
|
Config
|
||||||
|
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
|
UserTemplates []UserTemplate `json:"user_templates,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,20 @@ var (
|
|||||||
Cache *cache.Cache
|
Cache *cache.Cache
|
||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
Loc *time.Location
|
Loc *time.Location
|
||||||
|
UserTemplates = []model.UserTemplate{
|
||||||
|
{
|
||||||
|
Path: "user-dist",
|
||||||
|
Name: "Official",
|
||||||
|
GitHub: "https://github.com/hamster1963/nezha-dash",
|
||||||
|
Author: "hamster1963",
|
||||||
|
}, {
|
||||||
|
Path: "nazhua-dist",
|
||||||
|
Name: "Nazhua",
|
||||||
|
GitHub: "https://github.com/hi2shark/nazhua",
|
||||||
|
Author: "hi2hi",
|
||||||
|
Community: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitTimezoneAndCache() {
|
func InitTimezoneAndCache() {
|
||||||
@ -44,7 +58,7 @@ func LoadSingleton() {
|
|||||||
// InitConfigFromPath 从给出的文件路径中加载配置
|
// InitConfigFromPath 从给出的文件路径中加载配置
|
||||||
func InitConfigFromPath(path string) {
|
func InitConfigFromPath(path string) {
|
||||||
Conf = &model.Config{}
|
Conf = &model.Config{}
|
||||||
err := Conf.Read(path)
|
err := Conf.Read(path, UserTemplates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user