feat: complete Kra admin backend migration
This commit is contained in:
@@ -45,15 +45,28 @@ func registerAuditRoutes(group, public *gin.RouterGroup, svc *service.AuditServi
|
||||
writeResult(c, codeSuccess, gin.H{"resysOperationRecord": item}, "查询成功")
|
||||
})
|
||||
operations.DELETE("/deleteSysOperationRecord", func(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
|
||||
if err := svc.DeleteOperations(c.Request.Context(), []uint{uint(id)}); err != nil {
|
||||
var req struct {
|
||||
ID uint `json:"ID"`
|
||||
}
|
||||
if c.ShouldBindJSON(&req) != nil || req.ID == 0 {
|
||||
fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteOperations(c.Request.Context(), []uint{req.ID}); err != nil {
|
||||
fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
ok(c)
|
||||
})
|
||||
operations.DELETE("/deleteSysOperationRecordByIds", func(c *gin.Context) {
|
||||
if err := svc.DeleteOperations(c.Request.Context(), idsFromQuery(c)); err != nil {
|
||||
var req struct {
|
||||
IDs []uint `json:"ids"`
|
||||
}
|
||||
if c.ShouldBindJSON(&req) != nil || len(req.IDs) == 0 {
|
||||
fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteOperations(c.Request.Context(), req.IDs); err != nil {
|
||||
fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
@@ -80,15 +93,28 @@ func registerAuditRoutes(group, public *gin.RouterGroup, svc *service.AuditServi
|
||||
writeResult(c, codeSuccess, item, "查询成功")
|
||||
})
|
||||
logins.DELETE("/deleteLoginLog", func(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
|
||||
if err := svc.DeleteLogins(c.Request.Context(), []uint{uint(id)}); err != nil {
|
||||
var req struct {
|
||||
ID uint `json:"ID"`
|
||||
}
|
||||
if c.ShouldBindJSON(&req) != nil || req.ID == 0 {
|
||||
fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteLogins(c.Request.Context(), []uint{req.ID}); err != nil {
|
||||
fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
ok(c)
|
||||
})
|
||||
logins.DELETE("/deleteLoginLogByIds", func(c *gin.Context) {
|
||||
if err := svc.DeleteLogins(c.Request.Context(), idsFromQuery(c)); err != nil {
|
||||
var req struct {
|
||||
IDs []uint `json:"ids"`
|
||||
}
|
||||
if c.ShouldBindJSON(&req) != nil || len(req.IDs) == 0 {
|
||||
fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteLogins(c.Request.Context(), req.IDs); err != nil {
|
||||
fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
|
||||
+71
-22
@@ -34,14 +34,6 @@ func NewGinServer(c *conf.Server, config *conf.AdminBackend, svc *service.System
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
engine := gin.New()
|
||||
engine.Use(ginRequestMeta(), emailErrorAlert(emails, logger), gin.Recovery(), securityRateLimit(svc, settings), operationAudit(audit))
|
||||
localStorage := config == nil || config.Storage == nil || config.Storage.Type == "" || config.Storage.Type == "local"
|
||||
if localStorage && config != nil && config.Local != nil && config.Local.StorePath != "" {
|
||||
pathPrefix := "/" + strings.Trim(config.Local.PathPrefix, "/")
|
||||
if pathPrefix != "/" {
|
||||
engine.StaticFS(pathPrefix, filesOnly{FileSystem: http.Dir(config.Local.StorePath)})
|
||||
}
|
||||
}
|
||||
|
||||
prefix := ""
|
||||
if config != nil {
|
||||
prefix = strings.TrimSuffix(config.RouterPrefix, "/")
|
||||
@@ -50,13 +42,13 @@ func NewGinServer(c *conf.Server, config *conf.AdminBackend, svc *service.System
|
||||
public.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
})
|
||||
registerPublicRoutes(public, engine, config, svc, settings, access, audit)
|
||||
registerPublicRoutes(public, engine, config, svc, settings, access, audit, scheduler)
|
||||
private := engine.Group(prefix)
|
||||
private.Use(jwtAuth(config, settings), mustChangePasswordGuard(), accessControl(access, audit))
|
||||
registerPrivateRoutes(private, svc)
|
||||
registerPrivateRoutes(private, svc, settings)
|
||||
registerAccessRoutes(private, engine, access)
|
||||
registerSettingsRoutes(private, public, settings)
|
||||
registerSystemConfigRoutes(private, config, svc, settings)
|
||||
registerSystemConfigRoutes(private, config, svc, settings, scheduler)
|
||||
registerVersionRoutes(private, versions)
|
||||
registerExportRoutes(private, public, svc, exports)
|
||||
registerAuditRoutes(private, public, audit)
|
||||
@@ -66,6 +58,9 @@ func NewGinServer(c *conf.Server, config *conf.AdminBackend, svc *service.System
|
||||
registerEmailRoutes(private, emails)
|
||||
|
||||
engine.NoRoute(func(c *gin.Context) {
|
||||
if serveLocalStorage(c, config) {
|
||||
return
|
||||
}
|
||||
fail(c, "请求的接口不存在")
|
||||
})
|
||||
|
||||
@@ -88,6 +83,23 @@ func NewGinServer(c *conf.Server, config *conf.AdminBackend, svc *service.System
|
||||
}
|
||||
}
|
||||
|
||||
// serveLocalStorage resolves the local path for every request so a config
|
||||
// reload takes effect without rebuilding the Gin engine.
|
||||
func serveLocalStorage(c *gin.Context, config *conf.AdminBackend) bool {
|
||||
if config == nil || config.Local == nil || config.Local.StorePath == "" {
|
||||
return false
|
||||
}
|
||||
if config.Storage != nil && config.Storage.Type != "" && config.Storage.Type != "local" {
|
||||
return false
|
||||
}
|
||||
prefix := "/" + strings.Trim(config.Local.PathPrefix, "/")
|
||||
if prefix == "/" || (c.Request.URL.Path != prefix && !strings.HasPrefix(c.Request.URL.Path, prefix+"/")) {
|
||||
return false
|
||||
}
|
||||
http.StripPrefix(prefix, http.FileServer(filesOnly{FileSystem: http.Dir(config.Local.StorePath)})).ServeHTTP(c.Writer, c.Request)
|
||||
return true
|
||||
}
|
||||
|
||||
func jwtAuth(config *conf.AdminBackend, settings *service.SettingsService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.GetHeader("x-token")
|
||||
@@ -107,6 +119,27 @@ func jwtAuth(config *conf.AdminBackend, settings *service.SettingsService) gin.H
|
||||
noAuth(c, "登录状态已失效")
|
||||
return
|
||||
}
|
||||
if claims.ExpiresAt != nil && claims.BufferTime > 0 && time.Until(claims.ExpiresAt.Time) < time.Duration(claims.BufferTime)*time.Second {
|
||||
expires, buffer := 7*24*time.Hour, time.Duration(claims.BufferTime)*time.Second
|
||||
issuer := claims.Issuer
|
||||
if config != nil && config.Jwt != nil {
|
||||
if config.Jwt.ExpiresTime != nil {
|
||||
expires = config.Jwt.ExpiresTime.AsDuration()
|
||||
}
|
||||
if config.Jwt.BufferTime != nil {
|
||||
buffer = config.Jwt.BufferTime.AsDuration()
|
||||
}
|
||||
if config.Jwt.Issuer != "" {
|
||||
issuer = config.Jwt.Issuer
|
||||
}
|
||||
}
|
||||
newToken, newClaims, refreshErr := adminauth.Generate(secret, issuer, expires, buffer, claims.ID, claims.AuthorityID, claims.UUID, claims.Username, claims.NickName, claims.MustChangePwd)
|
||||
if refreshErr == nil {
|
||||
c.Header("new-token", newToken)
|
||||
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10))
|
||||
http.SetCookie(c.Writer, &http.Cookie{Name: "x-token", Value: newToken, Path: "/", MaxAge: int(expires.Seconds()), HttpOnly: true, SameSite: http.SameSiteStrictMode})
|
||||
}
|
||||
}
|
||||
c.Set("admin_claims", claims)
|
||||
c.Next()
|
||||
}
|
||||
@@ -134,7 +167,7 @@ func mustChangePasswordGuard() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func registerPublicRoutes(group *gin.RouterGroup, engine *gin.Engine, config *conf.AdminBackend, svc *service.SystemService, settings *service.SettingsService, access *service.AccessService, audit *service.AuditService) {
|
||||
func registerPublicRoutes(group *gin.RouterGroup, engine *gin.Engine, config *conf.AdminBackend, svc *service.SystemService, settings *service.SettingsService, access *service.AccessService, audit *service.AuditService, scheduler *TaskScheduler) {
|
||||
expiration := 3 * time.Minute
|
||||
keyLong, width, height := 6, 240, 80
|
||||
if config != nil && config.Captcha != nil {
|
||||
@@ -238,22 +271,31 @@ func registerPublicRoutes(group *gin.RouterGroup, engine *gin.Engine, config *co
|
||||
writeResult(c, codeSuccess, gin.H{"needInit": !initialized}, message)
|
||||
})
|
||||
initGroup.POST("/initdb", func(c *gin.Context) {
|
||||
initialized, err := svc.IsInitialized(c.Request.Context())
|
||||
if err != nil {
|
||||
fail(c, "数据库状态检查失败")
|
||||
return
|
||||
}
|
||||
if initialized {
|
||||
fail(c, "数据库已初始化,无需重复初始化")
|
||||
return
|
||||
}
|
||||
var input service.DatabaseInit
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
fail(c, "数据库初始化参数无效")
|
||||
return
|
||||
}
|
||||
if err := svc.Initialize(c.Request.Context(), &input); err != nil {
|
||||
fail(c, "自动创建数据库失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
routes := engine.Routes()
|
||||
apis := make([]*biz.API, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
apis = append(apis, &biz.API{Path: route.Path, Method: route.Method, APIGroup: routeGroup(route.Path)})
|
||||
}
|
||||
if err := access.ApplyAPISync(c.Request.Context(), apis, nil); err != nil {
|
||||
fail(c, "数据库已初始化,但 API 权限数据写入失败")
|
||||
if err := svc.Initialize(c.Request.Context(), &input, apis); err != nil {
|
||||
fail(c, "自动创建数据库失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
if err := scheduler.Reload(c.Request.Context()); err != nil {
|
||||
fail(c, "数据库已初始化,但定时任务加载失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
writeResult(c, codeSuccess, gin.H{}, "自动创建数据库成功")
|
||||
@@ -277,9 +319,8 @@ func cachedInt(ctx context.Context, svc *service.SystemService, key string) (int
|
||||
return result, err
|
||||
}
|
||||
func incrementCached(ctx context.Context, svc *service.SystemService, key string, ttl time.Duration) (int, error) {
|
||||
value, _ := cachedInt(ctx, svc, key)
|
||||
value++
|
||||
return value, svc.CacheSet(ctx, key, strconv.Itoa(value), ttl)
|
||||
value, err := svc.CacheIncrement(ctx, key, ttl)
|
||||
return int(value), err
|
||||
}
|
||||
func securityRateLimit(svc *service.SystemService, settings *service.SettingsService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
@@ -356,7 +397,7 @@ func (f filesOnly) Open(name string) (http.File, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func registerPrivateRoutes(group *gin.RouterGroup, svc *service.SystemService) {
|
||||
func registerPrivateRoutes(group *gin.RouterGroup, svc *service.SystemService, settings *service.SettingsService) {
|
||||
user := group.Group("/user")
|
||||
user.POST("/getUserList", func(c *gin.Context) {
|
||||
var req struct {
|
||||
@@ -572,6 +613,14 @@ func registerPrivateRoutes(group *gin.RouterGroup, svc *service.SystemService) {
|
||||
})
|
||||
jwt := group.Group("/jwt")
|
||||
jwt.POST("/jsonInBlacklist", func(c *gin.Context) {
|
||||
token := c.GetHeader("x-token")
|
||||
if token == "" {
|
||||
token, _ = c.Cookie("x-token")
|
||||
}
|
||||
if err := settings.BlacklistToken(c.Request.Context(), token); err != nil {
|
||||
fail(c, "退出失败")
|
||||
return
|
||||
}
|
||||
http.SetCookie(c.Writer, &http.Cookie{Name: "x-token", Value: "", Path: "/", MaxAge: -1, HttpOnly: true})
|
||||
ok(c)
|
||||
})
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
v1 "kra/api/kratos/admin/v1"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/service"
|
||||
"kra/pkg/validate"
|
||||
|
||||
"github.com/go-kratos/kratos/v3/middleware/recovery"
|
||||
"github.com/go-kratos/kratos/v3/transport/grpc"
|
||||
)
|
||||
|
||||
// NewGRPCServer new a gRPC server.
|
||||
func NewGRPCServer(c *conf.Server, admin *service.AdminService) *grpc.Server {
|
||||
var opts = []grpc.ServerOption{
|
||||
grpc.Middleware(
|
||||
recovery.Recovery(),
|
||||
validate.Middleware(),
|
||||
),
|
||||
}
|
||||
if c.Grpc.Network != "" {
|
||||
opts = append(opts, grpc.Network(c.Grpc.Network))
|
||||
}
|
||||
if c.Grpc.Addr != "" {
|
||||
opts = append(opts, grpc.Address(c.Grpc.Addr))
|
||||
}
|
||||
if c.Grpc.Timeout != nil {
|
||||
opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration()))
|
||||
}
|
||||
srv := grpc.NewServer(opts...)
|
||||
v1.RegisterAdminServiceServer(srv, admin)
|
||||
return srv
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
v1 "kra/api/kratos/admin/v1"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/service"
|
||||
"kra/pkg/auth"
|
||||
"kra/pkg/validate"
|
||||
|
||||
"github.com/go-kratos/kratos/v3/middleware/recovery"
|
||||
"github.com/go-kratos/kratos/v3/transport/http"
|
||||
)
|
||||
|
||||
// NewHTTPServer new an HTTP server.
|
||||
func NewHTTPServer(c *conf.Server, admin *service.AdminService) *http.Server {
|
||||
var opts = []http.ServerOption{
|
||||
http.Filter(
|
||||
auth.Middleware(),
|
||||
),
|
||||
http.Middleware(
|
||||
recovery.Recovery(),
|
||||
validate.Middleware(),
|
||||
),
|
||||
}
|
||||
if c.Http.Network != "" {
|
||||
opts = append(opts, http.Network(c.Http.Network))
|
||||
}
|
||||
if c.Http.Addr != "" {
|
||||
opts = append(opts, http.Address(c.Http.Addr))
|
||||
}
|
||||
if c.Http.Timeout != nil {
|
||||
opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
|
||||
}
|
||||
srv := http.NewServer(opts...)
|
||||
v1.RegisterAdminServiceHTTPServer(srv, admin)
|
||||
return srv
|
||||
}
|
||||
@@ -31,10 +31,22 @@ type menuRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"desc"`
|
||||
} `json:"menuBtn"`
|
||||
Parameters []struct {
|
||||
ID uint `json:"ID"`
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
} `json:"parameters"`
|
||||
}
|
||||
|
||||
func menuDomain(v menuRequest) *biz.Menu {
|
||||
return &biz.Menu{ID: v.ID, ParentID: v.ParentID, Path: v.Path, Name: v.Name, Hidden: v.Hidden, Component: v.Component, Sort: v.Sort, ActiveName: v.Meta.ActiveName, KeepAlive: v.Meta.KeepAlive, DefaultMenu: v.Meta.DefaultMenu, Title: v.Meta.Title, Icon: v.Meta.Icon, CloseTab: v.Meta.CloseTab, TransitionType: v.Meta.TransitionType}
|
||||
menu := &biz.Menu{ID: v.ID, ParentID: v.ParentID, Path: v.Path, Name: v.Name, Hidden: v.Hidden, Component: v.Component, Sort: v.Sort, ActiveName: v.Meta.ActiveName, KeepAlive: v.Meta.KeepAlive, DefaultMenu: v.Meta.DefaultMenu, Title: v.Meta.Title, Icon: v.Meta.Icon, CloseTab: v.Meta.CloseTab, TransitionType: v.Meta.TransitionType}
|
||||
menu.Buttons = menuButtons(v, v.ID)
|
||||
menu.Parameters = make([]*biz.MenuParameter, 0, len(v.Parameters))
|
||||
for _, parameter := range v.Parameters {
|
||||
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{ID: parameter.ID, MenuID: v.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
return menu
|
||||
}
|
||||
|
||||
func menuButtons(v menuRequest, menuID uint) []*biz.MenuButton {
|
||||
@@ -74,10 +86,6 @@ func registerMenuRoutes(group *gin.RouterGroup, svc *service.AccessService) {
|
||||
fail(c, "新增失败")
|
||||
return
|
||||
}
|
||||
if err := svc.ReplaceMenuButtons(c.Request.Context(), menu.ID, menuButtons(req, menu.ID)); err != nil {
|
||||
fail(c, "按钮保存失败")
|
||||
return
|
||||
}
|
||||
ok(c)
|
||||
})
|
||||
router.POST("/updateBaseMenu", func(c *gin.Context) {
|
||||
@@ -90,10 +98,6 @@ func registerMenuRoutes(group *gin.RouterGroup, svc *service.AccessService) {
|
||||
fail(c, "更新失败")
|
||||
return
|
||||
}
|
||||
if err := svc.ReplaceMenuButtons(c.Request.Context(), req.ID, menuButtons(req, req.ID)); err != nil {
|
||||
fail(c, "按钮保存失败")
|
||||
return
|
||||
}
|
||||
ok(c)
|
||||
})
|
||||
router.POST("/deleteBaseMenu", func(c *gin.Context) {
|
||||
|
||||
@@ -5,4 +5,4 @@ import (
|
||||
)
|
||||
|
||||
// ProviderSet is server providers.
|
||||
var ProviderSet = wire.NewSet(NewGRPCServer, NewGinServer, NewTaskScheduler)
|
||||
var ProviderSet = wire.NewSet(NewGinServer, NewTaskScheduler)
|
||||
|
||||
@@ -328,26 +328,24 @@ func registerParameterRoutes(group *gin.RouterGroup, svc *service.SettingsServic
|
||||
ok(c)
|
||||
})
|
||||
router.DELETE("/deleteSysParams", func(c *gin.Context) {
|
||||
var req parameterRequest
|
||||
if c.ShouldBindJSON(&req) != nil {
|
||||
id, err := strconv.ParseUint(c.Query("ID"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteParameters(c.Request.Context(), []uint{req.ID}); err != nil {
|
||||
if err := svc.DeleteParameters(c.Request.Context(), []uint{uint(id)}); err != nil {
|
||||
fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
ok(c)
|
||||
})
|
||||
router.DELETE("/deleteSysParamsByIds", func(c *gin.Context) {
|
||||
var req struct {
|
||||
IDs []uint `json:"ids"`
|
||||
}
|
||||
if c.ShouldBindJSON(&req) != nil {
|
||||
ids := idsFromQuery(c)
|
||||
if len(ids) == 0 {
|
||||
fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteParameters(c.Request.Context(), req.IDs); err != nil {
|
||||
if err := svc.DeleteParameters(c.Request.Context(), ids); err != nil {
|
||||
fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
@@ -368,7 +366,7 @@ func registerParameterRoutes(group *gin.RouterGroup, svc *service.SettingsServic
|
||||
fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
writeResult(c, codeSuccess, gin.H{"data": item}, "查询成功")
|
||||
writeResult(c, codeSuccess, item, "查询成功")
|
||||
})
|
||||
router.GET("/getSysParamsList", func(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBackend, systemService *service.SystemService, settings *service.SettingsService) {
|
||||
func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBackend, systemService *service.SystemService, settings *service.SettingsService, scheduler *TaskScheduler) {
|
||||
security := group.Group("/securityConfig")
|
||||
security.GET("/getSecurityConfig", func(c *gin.Context) {
|
||||
value, err := settings.Security(c.Request.Context())
|
||||
@@ -54,6 +54,9 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
||||
if config.Local != nil {
|
||||
admin["local"] = gin.H{"storePath": config.Local.StorePath, "pathPrefix": config.Local.PathPrefix}
|
||||
}
|
||||
if config.Media != nil {
|
||||
admin["media"] = gin.H{"sessionTtl": config.Media.SessionTtl}
|
||||
}
|
||||
if config.Email != nil {
|
||||
email = gin.H{"to": config.Email.To, "from": config.Email.From, "host": config.Email.Host, "secret": "******", "nickname": config.Email.Nickname, "port": config.Email.Port, "is-ssl": config.Email.IsSsl, "is-loginauth": config.Email.IsLoginAuth}
|
||||
}
|
||||
@@ -78,6 +81,9 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
||||
StorePath string `json:"storePath"`
|
||||
PathPrefix string `json:"pathPrefix"`
|
||||
} `json:"local"`
|
||||
Media struct {
|
||||
SessionTTL int32 `json:"sessionTtl"`
|
||||
} `json:"media"`
|
||||
Storage *conf.AdminBackend_Storage `json:"storage"`
|
||||
} `json:"admin"`
|
||||
Email *struct {
|
||||
@@ -97,48 +103,60 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
||||
return
|
||||
}
|
||||
if config != nil {
|
||||
config.RouterPrefix = req.Config.Admin.RouterPrefix
|
||||
if config.Jwt != nil {
|
||||
next := proto.Clone(config).(*conf.AdminBackend)
|
||||
next.RouterPrefix = req.Config.Admin.RouterPrefix
|
||||
if next.Jwt != nil {
|
||||
if req.Config.Admin.JWT.SigningKey != "" && req.Config.Admin.JWT.SigningKey != "******" {
|
||||
config.Jwt.SigningKey = req.Config.Admin.JWT.SigningKey
|
||||
next.Jwt.SigningKey = req.Config.Admin.JWT.SigningKey
|
||||
}
|
||||
if req.Config.Admin.JWT.Issuer != "" {
|
||||
config.Jwt.Issuer = req.Config.Admin.JWT.Issuer
|
||||
next.Jwt.Issuer = req.Config.Admin.JWT.Issuer
|
||||
}
|
||||
}
|
||||
if config.Local != nil {
|
||||
if next.Local != nil {
|
||||
if req.Config.Admin.Local.StorePath != "" {
|
||||
config.Local.StorePath = req.Config.Admin.Local.StorePath
|
||||
next.Local.StorePath = req.Config.Admin.Local.StorePath
|
||||
}
|
||||
if req.Config.Admin.Local.PathPrefix != "" {
|
||||
config.Local.PathPrefix = req.Config.Admin.Local.PathPrefix
|
||||
next.Local.PathPrefix = req.Config.Admin.Local.PathPrefix
|
||||
}
|
||||
}
|
||||
if next.Media != nil && req.Config.Admin.Media.SessionTTL > 0 {
|
||||
next.Media.SessionTtl = req.Config.Admin.Media.SessionTTL
|
||||
}
|
||||
if req.Config.Admin.Storage != nil {
|
||||
preserveStorageSecrets(req.Config.Admin.Storage, config.Storage)
|
||||
config.Storage = req.Config.Admin.Storage
|
||||
preserveStorageSecrets(req.Config.Admin.Storage, next.Storage)
|
||||
next.Storage = req.Config.Admin.Storage
|
||||
}
|
||||
if config.Email != nil && req.Config.Email != nil {
|
||||
config.Email.To = req.Config.Email.To
|
||||
config.Email.From = req.Config.Email.From
|
||||
config.Email.Host = req.Config.Email.Host
|
||||
config.Email.Nickname = req.Config.Email.Nickname
|
||||
config.Email.Port = req.Config.Email.Port
|
||||
config.Email.IsSsl = req.Config.Email.IsSSL
|
||||
config.Email.IsLoginAuth = req.Config.Email.IsLoginAuth
|
||||
if next.Email != nil && req.Config.Email != nil {
|
||||
next.Email.To = req.Config.Email.To
|
||||
next.Email.From = req.Config.Email.From
|
||||
next.Email.Host = req.Config.Email.Host
|
||||
next.Email.Nickname = req.Config.Email.Nickname
|
||||
next.Email.Port = req.Config.Email.Port
|
||||
next.Email.IsSsl = req.Config.Email.IsSSL
|
||||
next.Email.IsLoginAuth = req.Config.Email.IsLoginAuth
|
||||
if req.Config.Email.Secret != "" && req.Config.Email.Secret != "******" {
|
||||
config.Email.Secret = req.Config.Email.Secret
|
||||
next.Email.Secret = req.Config.Email.Secret
|
||||
}
|
||||
}
|
||||
if err := systemService.PersistAdminConfig(c.Request.Context(), next); err != nil {
|
||||
fail(c, "配置保存失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := systemService.PersistConfig(c.Request.Context()); err != nil {
|
||||
fail(c, "配置保存失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
writeResult(c, codeSuccess, gin.H{"restartRequired": true}, "配置已保存,重启服务后完整生效")
|
||||
writeResult(c, codeSuccess, gin.H{}, "设置成功")
|
||||
})
|
||||
system.POST("/reloadSystem", func(c *gin.Context) {
|
||||
writeResult(c, codeSuccess, gin.H{"reload": false, "restartRequired": true}, "配置已在当前进程部分生效;路由与存储配置需重启服务")
|
||||
if err := systemService.ReloadConfig(c.Request.Context()); err != nil {
|
||||
fail(c, "重载系统失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
if err := scheduler.Reload(c.Request.Context()); err != nil {
|
||||
fail(c, "系统配置已重载,但定时任务重载失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
writeResult(c, codeSuccess, gin.H{}, "重载系统成功")
|
||||
})
|
||||
system.POST("/getServerInfo", func(c *gin.Context) {
|
||||
cpuPercent, _ := cpu.Percent(200*time.Millisecond, false)
|
||||
|
||||
@@ -69,9 +69,15 @@ func registerTaskRoutes(group *gin.RouterGroup, svc *service.TaskService, schedu
|
||||
ok(c)
|
||||
})
|
||||
router.DELETE("/deleteTimedTask", func(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
|
||||
scheduler.Remove(uint(id))
|
||||
if err := svc.Delete(c.Request.Context(), uint(id)); err != nil {
|
||||
var req struct {
|
||||
ID uint `json:"ID"`
|
||||
}
|
||||
if c.ShouldBindJSON(&req) != nil || req.ID == 0 {
|
||||
fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
scheduler.Remove(req.ID)
|
||||
if err := svc.Delete(c.Request.Context(), req.ID); err != nil {
|
||||
fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -76,6 +76,35 @@ func (s *TaskScheduler) Remove(id uint) {
|
||||
delete(s.entries, id)
|
||||
}
|
||||
}
|
||||
func (s *TaskScheduler) Reload(ctx context.Context) error {
|
||||
s.mu.Lock()
|
||||
for id, old := range s.entries {
|
||||
if old.seconds {
|
||||
s.seconds.Remove(old.entry)
|
||||
} else {
|
||||
s.standard.Remove(old.entry)
|
||||
}
|
||||
delete(s.entries, id)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
items, _, err := s.service.Tasks(ctx, 1, 100000, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, raw := range items {
|
||||
id, _ := raw["ID"].(uint)
|
||||
task, findErr := s.service.Task(ctx, id)
|
||||
if findErr != nil {
|
||||
return findErr
|
||||
}
|
||||
if task.Enabled {
|
||||
if err = s.Schedule(task); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *TaskScheduler) Schedule(task *biz.TimedTask) error {
|
||||
s.Remove(task.ID)
|
||||
if !task.Enabled {
|
||||
|
||||
Reference in New Issue
Block a user