diff --git a/cmd/dashboard/controller/common_page.go b/cmd/dashboard/controller/common_page.go index 70d64d6..4da4208 100644 --- a/cmd/dashboard/controller/common_page.go +++ b/cmd/dashboard/controller/common_page.go @@ -489,7 +489,7 @@ func (cp *commonPage) createTerminal(c *gin.Context) { useSSL: createTerminalReq.Protocol == "https:", } - c.HTML(http.StatusOK, "dashboard/terminal", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/terminal", mygin.CommonEnvironment(c, gin.H{ "SessionID": id, "ServerName": server.Name, })) diff --git a/cmd/dashboard/controller/guest_page.go b/cmd/dashboard/controller/guest_page.go index ed5c171..b8fd768 100644 --- a/cmd/dashboard/controller/guest_page.go +++ b/cmd/dashboard/controller/guest_page.go @@ -46,7 +46,7 @@ func (gp *guestPage) login(c *gin.Context) { LoginType = "Jihulab" RegistrationLink = "https://jihulab.com/users/sign_up" } - c.HTML(http.StatusOK, "dashboard/login", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/login", mygin.CommonEnvironment(c, gin.H{ "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}), "LoginType": LoginType, "RegistrationLink": RegistrationLink, diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go index c5863c3..0f3702a 100644 --- a/cmd/dashboard/controller/member_api.go +++ b/cmd/dashboard/controller/member_api.go @@ -702,6 +702,7 @@ type settingForm struct { Admin string Language string Theme string + DashboardTheme string CustomCode string ViewPassword string IgnoredIPNotification string @@ -722,6 +723,23 @@ func (ma *memberAPI) updateSetting(c *gin.Context) { }) return } + + if yes, err := utils.IsDirEmpty("resource/template/theme-" + sf.Theme); err != nil || yes { + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusBadRequest, + Message: fmt.Sprintf("前台主题文件异常:%s", err), + }) + return + } + + if yes, err := utils.IsDirEmpty("resource/template/dashboard-" + sf.DashboardTheme); err != nil || yes { + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusBadRequest, + Message: fmt.Sprintf("后台主题文件异常:%s", err), + }) + return + } + singleton.Conf.Language = sf.Language singleton.Conf.EnableIPChangeNotification = sf.EnableIPChangeNotification == "on" singleton.Conf.EnablePlainIPInNotification = sf.EnablePlainIPInNotification == "on" @@ -731,6 +749,7 @@ func (ma *memberAPI) updateSetting(c *gin.Context) { singleton.Conf.IPChangeNotificationTag = sf.IPChangeNotificationTag singleton.Conf.Site.Brand = sf.Title singleton.Conf.Site.Theme = sf.Theme + singleton.Conf.Site.DashboardTheme = sf.DashboardTheme singleton.Conf.Site.CustomCode = sf.CustomCode singleton.Conf.Site.ViewPassword = sf.ViewPassword singleton.Conf.Oauth2.Admin = sf.Admin diff --git a/cmd/dashboard/controller/member_page.go b/cmd/dashboard/controller/member_page.go index 7f18823..83c1115 100644 --- a/cmd/dashboard/controller/member_page.go +++ b/cmd/dashboard/controller/member_page.go @@ -34,7 +34,7 @@ func (mp *memberPage) serve() { func (mp *memberPage) api(c *gin.Context) { singleton.ApiLock.RLock() defer singleton.ApiLock.RUnlock() - c.HTML(http.StatusOK, "dashboard/api", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/api", mygin.CommonEnvironment(c, gin.H{ "title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ApiManagement"}), "Tokens": singleton.ApiTokenList, })) @@ -43,14 +43,14 @@ func (mp *memberPage) api(c *gin.Context) { func (mp *memberPage) server(c *gin.Context) { singleton.SortedServerLock.RLock() defer singleton.SortedServerLock.RUnlock() - c.HTML(http.StatusOK, "dashboard/server", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/server", mygin.CommonEnvironment(c, gin.H{ "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServersManagement"}), "Servers": singleton.SortedServerList, })) } func (mp *memberPage) monitor(c *gin.Context) { - c.HTML(http.StatusOK, "dashboard/monitor", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/monitor", mygin.CommonEnvironment(c, gin.H{ "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesManagement"}), "Monitors": singleton.ServiceSentinelShared.Monitors(), })) @@ -59,7 +59,7 @@ func (mp *memberPage) monitor(c *gin.Context) { func (mp *memberPage) cron(c *gin.Context) { var crons []model.Cron singleton.DB.Find(&crons) - c.HTML(http.StatusOK, "dashboard/cron", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/cron", mygin.CommonEnvironment(c, gin.H{ "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ScheduledTasks"}), "Crons": crons, })) @@ -70,7 +70,7 @@ func (mp *memberPage) notification(c *gin.Context) { singleton.DB.Find(&nf) var ar []model.AlertRule singleton.DB.Find(&ar) - c.HTML(http.StatusOK, "dashboard/notification", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/notification", mygin.CommonEnvironment(c, gin.H{ "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Notification"}), "Notifications": nf, "AlertRules": ar, @@ -78,9 +78,10 @@ func (mp *memberPage) notification(c *gin.Context) { } func (mp *memberPage) setting(c *gin.Context) { - c.HTML(http.StatusOK, "dashboard/setting", mygin.CommonEnvironment(c, gin.H{ - "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}), - "Languages": model.Languages, - "Themes": model.Themes, + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/setting", mygin.CommonEnvironment(c, gin.H{ + "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}), + "Languages": model.Languages, + "Themes": model.Themes, + "DashboardThemes": model.DashboardThemes, })) } diff --git a/cmd/dashboard/controller/oauth2.go b/cmd/dashboard/controller/oauth2.go index 63fa4c6..f53d70c 100644 --- a/cmd/dashboard/controller/oauth2.go +++ b/cmd/dashboard/controller/oauth2.go @@ -85,7 +85,7 @@ func (oa *oauth2controller) login(c *gin.Context) { singleton.Cache.Set(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, stateKey), state, cache.DefaultExpiration) url := oa.getCommonOauth2Config(c).AuthCodeURL(state, oauth2.AccessTypeOnline) c.SetCookie(singleton.Conf.Site.CookieName+"-sk", stateKey, 60*5, "", "", false, false) - c.HTML(http.StatusOK, "dashboard/redirect", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/redirect", mygin.CommonEnvironment(c, gin.H{ "URL": url, })) } @@ -172,7 +172,7 @@ func (oa *oauth2controller) callback(c *gin.Context) { user.IssueNewToken() singleton.DB.Save(&user) c.SetCookie(singleton.Conf.Site.CookieName, user.Token, 60*60*24, "", "", false, false) - c.HTML(http.StatusOK, "dashboard/redirect", mygin.CommonEnvironment(c, gin.H{ + c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/redirect", mygin.CommonEnvironment(c, gin.H{ "URL": "/", })) } diff --git a/model/config.go b/model/config.go index 3809c20..8bc2a93 100644 --- a/model/config.go +++ b/model/config.go @@ -21,7 +21,12 @@ var Themes = map[string]string{ "daynight": "JackieSung DayNight", "mdui": "Neko Mdui", "hotaru": "Hotaru", - "custom": "Custom(third-party)", + "custom": "Custom(local)", +} + +var DashboardThemes = map[string]string{ + "default": "Default", + "custom": "Custom(local)", } const ( @@ -70,11 +75,12 @@ type Config struct { Debug bool // debug模式开关 Language string // 系统语言,默认 zh-CN Site struct { - Brand string // 站点名称 - CookieName string // 浏览器 Cookie 名称 - Theme string - CustomCode string - ViewPassword string // 前台查看密码 + Brand string // 站点名称 + CookieName string // 浏览器 Cookie 名称 + Theme string + DashboardTheme string + CustomCode string + ViewPassword string // 前台查看密码 } Oauth2 struct { Type string @@ -117,6 +123,9 @@ func (c *Config) Read(path string) error { if c.Site.Theme == "" { c.Site.Theme = "default" } + if c.Site.DashboardTheme == "" { + c.Site.DashboardTheme = "default" + } if c.Language == "" { c.Language = "zh-CN" } diff --git a/pkg/mygin/error.go b/pkg/mygin/error.go index a849622..cabf99d 100644 --- a/pkg/mygin/error.go +++ b/pkg/mygin/error.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" "github.com/naiba/nezha/model" + "github.com/naiba/nezha/service/singleton" ) type ErrInfo struct { @@ -18,7 +19,7 @@ type ErrInfo struct { func ShowErrorPage(c *gin.Context, i ErrInfo, isPage bool) { if isPage { - c.HTML(i.Code, "dashboard/error", CommonEnvironment(c, gin.H{ + c.HTML(i.Code, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/error", CommonEnvironment(c, gin.H{ "Code": i.Code, "Title": i.Title, "Msg": i.Msg, diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 64adc44..6d5c109 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -3,6 +3,7 @@ package utils import ( "crypto/md5" // #nosec "encoding/hex" + "io" "math/rand" "os" "regexp" @@ -93,3 +94,17 @@ func SplitIPAddr(v4v6Bundle string) (string, string, string) { } return ipv4, ipv6, validIP } + +func IsDirEmpty(name string) (bool, error) { + f, err := os.Open(name) + if err != nil { + return false, err + } + defer f.Close() + + _, err = f.Readdirnames(1) + if err == io.EOF { + return true, nil + } + return false, err +} diff --git a/resource/l10n/zh-CN.toml b/resource/l10n/zh-CN.toml index 40818ec..d6d434f 100644 --- a/resource/l10n/zh-CN.toml +++ b/resource/l10n/zh-CN.toml @@ -470,19 +470,19 @@ other = "服务监控" other = "计划任务" [ApiManagement] -other="API" +other = "API" [IssueNewApiToken] -other="添加Token" +other = "添加Token" [Token] -other="Token" +other = "Token" [DeleteToken] -other="删除Token" +other = "删除Token" [ConfirmToDeleteThisToken] -other="确认删除Token" +other = "确认删除Token" [YouAreNotAuthorized] other = "此页面需要登录" @@ -531,3 +531,6 @@ other = "IP变更" [Transleft] other = "流量剩余" + +[DashboardTheme] +other = "管理后台主题" diff --git a/resource/template/dashboard/api.html b/resource/template/dashboard/api.html index b9b7795..cf92564 100644 --- a/resource/template/dashboard/api.html +++ b/resource/template/dashboard/api.html @@ -1,4 +1,4 @@ -{{define "dashboard/api"}} +{{define "dashboard-default/api"}} {{template "common/header" .}} {{template "common/menu" .}}