feat(plugins): 插件管理 - 区分系统与官方插件权限 (#329)

This commit is contained in:
MasterChiefCN
2026-08-27 11:22:00 +08:00
committed by GitHub
parent 23b4628c81
commit f111d57fc6
32 changed files with 1137 additions and 143 deletions

View File

@@ -16,11 +16,56 @@ import (
func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
statusRoutes := r.Group("/plugins")
statusRoutes.GET("/status", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
user, err := currentUser(c, svc)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"statuses": svc.WorkflowPluginStatuses()})
states, err := svc.PluginStatesForUser(user)
if err != nil {
failService(c, err)
return
}
statuses, err := svc.WorkflowPluginStatusesForUser(user.ID)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"statuses": statuses, "states": states})
})
adminRoutes := r.Group("/admin/plugins")
adminRoutes.GET("", func(c *gin.Context) {
user, err := currentUser(c, svc)
if err != nil {
failService(c, err)
return
}
states, err := svc.AdminPluginStates(user)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"plugins": svc.Plugins(), "states": states})
})
adminRoutes.PUT("/:id/availability", func(c *gin.Context) {
user, err := currentUser(c, svc)
if err != nil {
failService(c, err)
return
}
var request struct {
Available bool `json:"available"`
}
if err := c.ShouldBindJSON(&request); err != nil {
fail(c, http.StatusBadRequest, err)
return
}
state, err := svc.SetPluginPlatformAvailability(user, c.Param("id"), request.Available)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"state": state})
})
pluginRoutes := r.Group("/plugins")
pluginRoutes.Use(requirePluginCenterAccess(svc))
@@ -37,7 +82,12 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
failService(c, err)
return
}
ok(c, gin.H{"plugins": plugins})
states, err := svc.PluginStatesForUser(user)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"plugins": plugins, "states": states})
})
pluginRoutes.GET("/catalog", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
@@ -77,7 +127,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
fail(c, http.StatusBadRequest, err)
return
}
plugin, err := svc.InstallPlugin(data, fileName)
plugin, err := svc.InstallPluginForAdmin(user, data, fileName)
if err != nil {
failService(c, err)
return
@@ -85,10 +135,27 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
ok(c, gin.H{"plugin": plugin})
})
pluginRoutes.GET("/:id/package", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
user, err := currentUser(c, svc)
if err != nil {
failService(c, err)
return
}
visiblePlugins, err := svc.PluginsForUser(user)
if err != nil {
failService(c, err)
return
}
visible := false
for _, plugin := range visiblePlugins {
if plugin.Manifest.ID == c.Param("id") {
visible = true
break
}
}
if !visible {
failService(c, service.Forbidden("无权访问该插件包"))
return
}
data, fileName, err := svc.PluginPackage(c.Param("id"))
if err != nil {
failService(c, err)
@@ -103,6 +170,26 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
})
pluginRoutes.POST("/:id/enable", pluginToggle(svc, true))
pluginRoutes.POST("/:id/disable", pluginToggle(svc, false))
pluginRoutes.PUT("/:id/activation", func(c *gin.Context) {
user, err := currentUser(c, svc)
if err != nil {
failService(c, err)
return
}
var request struct {
Enabled bool `json:"enabled"`
}
if err := c.ShouldBindJSON(&request); err != nil {
fail(c, http.StatusBadRequest, err)
return
}
state, err := svc.SetUserPluginEnabled(user, c.Param("id"), request.Enabled)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"state": state})
})
pluginRoutes.DELETE("/:id", func(c *gin.Context) {
user, err := currentUser(c, svc)
if err != nil {
@@ -113,7 +200,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
failService(c, err)
return
}
if err := svc.UninstallPlugin(c.Param("id")); err != nil {
if err := svc.UninstallPluginForAdmin(user, c.Param("id")); err != nil {
failService(c, err)
return
}
@@ -121,8 +208,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
})
pluginRoutes.GET("/eagle/library", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
if _, allowed := requireEnabledPlugin(c, svc, service.PluginEagleAssetConnector); !allowed {
return
}
library, err := svc.EagleLibrary(c.Query("baseUrl"))
@@ -134,8 +220,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
ok(c, gin.H{"library": library})
})
pluginRoutes.GET("/eagle/items", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
if _, allowed := requireEnabledPlugin(c, svc, service.PluginEagleAssetConnector); !allowed {
return
}
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "60"))
@@ -148,8 +233,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
ok(c, gin.H{"items": items})
})
pluginRoutes.GET("/eagle/items/:itemId/file", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
if _, allowed := requireEnabledPlugin(c, svc, service.PluginEagleAssetConnector); !allowed {
return
}
file, err := svc.OpenEagleItemFile(c.Query("baseUrl"), c.Param("itemId"))
@@ -164,8 +248,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
c.DataFromReader(http.StatusOK, file.Size, file.MimeType, file.Body, nil)
})
pluginRoutes.GET("/eagle/items/:itemId/thumbnail", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
if _, allowed := requireEnabledPlugin(c, svc, service.PluginEagleAssetConnector); !allowed {
return
}
file, err := svc.OpenEagleItemThumbnail(c.Query("baseUrl"), c.Param("itemId"))
@@ -179,8 +262,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
c.DataFromReader(http.StatusOK, file.Size, file.MimeType, file.Body, nil)
})
pluginRoutes.POST("/eagle/items", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
if _, allowed := requireEnabledPlugin(c, svc, service.PluginEagleAssetConnector); !allowed {
return
}
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 160<<20)
@@ -197,8 +279,7 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
ok(c, gin.H{"item": item})
})
pluginRoutes.POST("/eagle/folders", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
if _, allowed := requireEnabledPlugin(c, svc, service.PluginEagleAssetConnector); !allowed {
return
}
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 16<<10)
@@ -250,11 +331,23 @@ func pluginToggle(svc *service.Service, enabled bool) gin.HandlerFunc {
failService(c, err)
return
}
plugin, err := svc.SetPluginEnabled(c.Param("id"), enabled)
state, err := svc.SetPluginPlatformAvailability(user, c.Param("id"), enabled)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"plugin": plugin})
ok(c, gin.H{"state": state})
}
}
func requireEnabledPlugin(c *gin.Context, svc *service.Service, pluginID string) (*model.User, bool) {
user, err := currentUser(c, svc)
if err == nil {
err = svc.RequirePluginForUser(user.ID, pluginID)
}
if err != nil {
failService(c, err)
return nil, false
}
return user, true
}