feat(platform): 插件协议系统 - 支持声明式渠道、工作流与参考媒体映射

This commit is contained in:
ddCat
2026-08-26 13:11:10 +08:00
parent 054e3bbbff
commit 2f3a813ccb
58 changed files with 2074 additions and 706 deletions

View File

@@ -7,6 +7,7 @@ import (
"strings"
"infinite-canvas/backend/internal/model"
"infinite-canvas/backend/internal/protocol"
"infinite-canvas/backend/internal/service"
"github.com/gin-gonic/gin"
@@ -30,6 +31,15 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
}
ok(c, gin.H{"plugins": plugins})
})
pluginRoutes.GET("/catalog", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
return
}
scope := strings.TrimSpace(c.DefaultQuery("scope", "user.custom-channel"))
capability := strings.TrimSpace(c.Query("capability"))
ok(c, gin.H{"providers": svc.PluginProviderCatalog(scope, capability, false)})
})
pluginRoutes.POST("", func(c *gin.Context) {
user, err := currentUser(c, svc)
if err != nil {
@@ -40,30 +50,49 @@ func RegisterPluginRoutes(r *gin.RouterGroup, svc *service.Service) {
failService(c, err)
return
}
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 512<<10)
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, protocol.PluginPackageMaxBytes)
var data []byte
fileName := ""
if strings.HasPrefix(strings.ToLower(c.GetHeader("Content-Type")), "multipart/form-data") {
file, _, fileErr := c.Request.FormFile("file")
file, header, fileErr := c.Request.FormFile("file")
if fileErr != nil {
fail(c, http.StatusBadRequest, fileErr)
return
}
defer file.Close()
data, err = io.ReadAll(io.LimitReader(file, 512<<10+1))
fileName = header.Filename
data, err = io.ReadAll(io.LimitReader(file, protocol.PluginPackageMaxBytes+1))
} else {
data, err = io.ReadAll(io.LimitReader(c.Request.Body, 512<<10+1))
data, err = io.ReadAll(io.LimitReader(c.Request.Body, protocol.PluginPackageMaxBytes+1))
}
if err != nil {
fail(c, http.StatusBadRequest, err)
return
}
plugin, err := svc.InstallPlugin(data)
plugin, err := svc.InstallPlugin(data, fileName)
if err != nil {
failService(c, err)
return
}
ok(c, gin.H{"plugin": plugin})
})
pluginRoutes.GET("/:id/package", func(c *gin.Context) {
if _, err := currentUser(c, svc); err != nil {
failService(c, err)
return
}
data, fileName, err := svc.PluginPackage(c.Param("id"))
if err != nil {
failService(c, err)
return
}
if fileName == "" {
fileName = c.Param("id") + ".yingce-plugin"
}
c.Header("Cache-Control", "private, no-store")
c.Header("Content-Disposition", "attachment; filename=\""+strings.ReplaceAll(fileName, "\"", "")+"\"")
c.Data(http.StatusOK, "application/zip", data)
})
pluginRoutes.POST("/:id/enable", pluginToggle(svc, true))
pluginRoutes.POST("/:id/disable", pluginToggle(svc, false))
pluginRoutes.DELETE("/:id", func(c *gin.Context) {