feat(*): 短剧创作工作台 - 完善章节分镜、资产管理、技能运行与生成任务恢复
This commit is contained in:
@@ -697,6 +697,18 @@ func RegisterProjectRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
}
|
||||
ok(c, gin.H{"shot": shot, "revision": revision})
|
||||
})
|
||||
r.DELETE("/projects/:id/shots/:shotId", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteProjectShot(user.ID, c.Param("id"), c.Param("shotId")); err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"deleted": true})
|
||||
})
|
||||
r.POST("/projects/:id/shots/:shotId/assets", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
@@ -716,6 +728,18 @@ func RegisterProjectRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
}
|
||||
ok(c, gin.H{"reference": reference})
|
||||
})
|
||||
r.DELETE("/projects/:id/shots/:shotId/assets/:referenceId", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
if err := svc.UnlinkShotAsset(user.ID, c.Param("id"), c.Param("shotId"), c.Param("referenceId")); err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"unlinked": true})
|
||||
})
|
||||
r.POST("/projects/:id/asset-candidates", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"infinite-canvas/backend/internal/service"
|
||||
|
||||
@@ -9,6 +13,62 @@ import (
|
||||
)
|
||||
|
||||
func RegisterSkillRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
r.POST("/skills/install", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, service.SkillPackageUploadMaxBytes)
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
failService(c, service.BadAuthRequest("请选择 Markdown 或 ZIP 技能文件"))
|
||||
return
|
||||
}
|
||||
isPrivate, err := parseOptionalBool(c.PostForm("is_private"))
|
||||
if err != nil {
|
||||
failService(c, service.BadAuthRequest("is_private 必须是布尔值"))
|
||||
return
|
||||
}
|
||||
sourceType := strings.ToLower(strings.TrimSpace(c.PostForm("source_type")))
|
||||
if sourceType == "" {
|
||||
switch strings.ToLower(filepath.Ext(file.Filename)) {
|
||||
case ".md", ".markdown":
|
||||
sourceType = "markdown"
|
||||
case ".zip":
|
||||
sourceType = "zip"
|
||||
}
|
||||
}
|
||||
skill, err := svc.InstallSkillUpload(user.ID, sourceType, file, service.SkillInstallRequest{
|
||||
Name: c.PostForm("name"), Description: c.PostForm("description"), Tag: c.PostForm("tag"), IsPrivate: isPrivate,
|
||||
})
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"skill": skill})
|
||||
})
|
||||
|
||||
r.POST("/skills/install/github", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 32<<10)
|
||||
var req service.SkillGitHubInstallRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
failService(c, service.BadAuthRequest("GitHub 技能数据格式无效"))
|
||||
return
|
||||
}
|
||||
skill, err := svc.InstallGitHubSkill(user.ID, req)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"skill": skill})
|
||||
})
|
||||
|
||||
r.GET("/skills", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
@@ -56,6 +116,95 @@ func RegisterSkillRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
ok(c, gin.H{"skill": skill})
|
||||
})
|
||||
|
||||
r.GET("/skills/:id/files", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
files, err := svc.SkillPackageFiles(user.ID, c.Param("id"))
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"files": files})
|
||||
})
|
||||
|
||||
r.GET("/skills/:id/file", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
file, err := svc.SkillPackageFile(user.ID, c.Param("id"), c.Query("path"))
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"file": file})
|
||||
})
|
||||
|
||||
r.GET("/skills/:id/file/raw", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
data, mimeType, fileName, err := svc.SkillPackageRawFile(user.ID, c.Param("id"), c.Query("path"))
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
c.Header("Cache-Control", "private, no-store")
|
||||
c.Header("X-Content-Type-Options", "nosniff")
|
||||
c.Header("Content-Security-Policy", "default-src 'none'; sandbox")
|
||||
c.Header("Content-Disposition", mime.FormatMediaType("inline", map[string]string{"filename": fileName}))
|
||||
c.Data(http.StatusOK, mimeType, data)
|
||||
})
|
||||
|
||||
r.GET("/skills/:id/bundle", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
bundle, err := svc.SkillPackageBundle(user.ID, c.Param("id"))
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
c.Header("Cache-Control", "private, no-store")
|
||||
ok(c, gin.H{"bundle": bundle})
|
||||
})
|
||||
|
||||
r.GET("/skills/:id/search", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
results, err := svc.SearchSkillPackage(user.ID, c.Param("id"), c.Query("q"))
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"results": results})
|
||||
})
|
||||
|
||||
r.POST("/skills/:id/sync", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
skill, err := svc.SyncGitHubSkill(user.ID, c.Param("id"))
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"skill": skill})
|
||||
})
|
||||
|
||||
r.POST("/skills", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
@@ -113,6 +262,14 @@ func RegisterSkillRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
r.DELETE("/skills/:id/like", setSkillLiked(svc, false))
|
||||
}
|
||||
|
||||
func parseOptionalBool(value string) (bool, error) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return false, nil
|
||||
}
|
||||
return strconv.ParseBool(value)
|
||||
}
|
||||
|
||||
func setSkillAdded(svc *service.Service, added bool) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
|
||||
Reference in New Issue
Block a user