Files
NianAIGC/backend/internal/httpapi/layer_compositions.go

125 lines
3.9 KiB
Go

package httpapi
import (
"errors"
"net/http"
"strings"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/jobs"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/layercompositions"
)
type LayerCompositionsConfig struct {
MaxJSONBytes int64
}
type layerCompositionsHandler struct {
platform *PlatformAuthorizer
jobs *jobs.Service
compositions *layercompositions.Service
maxJSONBytes int64
}
func NewLayerCompositionsHandler(platform *PlatformAuthorizer, jobService *jobs.Service, compositions *layercompositions.Service, config LayerCompositionsConfig) (http.Handler, error) {
if platform == nil || jobService == nil || compositions == nil {
return nil, errors.New("layer composition HTTP dependencies are not configured")
}
if config.MaxJSONBytes <= 0 {
config.MaxJSONBytes = 1 << 20
}
return &layerCompositionsHandler{platform: platform, jobs: jobService, compositions: compositions, maxJSONBytes: config.MaxJSONBytes}, nil
}
func (handler *layerCompositionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
jobID, matched := matchLayerCompositionRoute(r.URL.Path)
if !matched {
http.NotFound(w, r)
return
}
if r.Method == http.MethodOptions {
w.Header().Set("Allow", "GET, PUT")
w.WriteHeader(http.StatusNoContent)
return
}
if r.Method != http.MethodGet && r.Method != http.MethodPut {
w.Header().Set("Allow", "GET, PUT")
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
session, err := handler.platform.Authorize(r, PlatformApp)
if err != nil {
writeLayerCompositionError(w, err)
return
}
job, err := handler.jobs.Get(r.Context(), jobs.Scope{OwnerID: session.User.ID}, jobID)
if err != nil || job.Capability != "image.generate" {
writeJSON(w, http.StatusNotFound, map[string]string{"error": "图层任务不存在"})
return
}
if r.Method == http.MethodGet {
composition, found, err := handler.compositions.Get(r.Context(), session.User.ID, jobID)
if err != nil {
writeLayerCompositionError(w, err)
return
}
if !found {
writeJSON(w, http.StatusOK, map[string]any{"composition": nil})
return
}
writeJSON(w, http.StatusOK, map[string]any{"composition": composition})
return
}
input := struct {
Version int64 `json:"version"`
BaseAssetID string `json:"baseAssetId"`
BaseVisible bool `json:"baseVisible"`
Layers []layercompositions.Layer `json:"layers"`
}{}
if !decodeJobJSON(w, r, handler.maxJSONBytes, &input, false) {
return
}
if !containsString(job.OutputAssetIDs, input.BaseAssetID) {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "底图必须来自当前图层拆分任务"})
return
}
saved, err := handler.compositions.Save(r.Context(), session.User.ID, jobID, input.Version, layercompositions.Composition{
BaseAssetID: input.BaseAssetID,
BaseVisible: input.BaseVisible,
Layers: input.Layers,
})
if err != nil {
writeLayerCompositionError(w, err)
return
}
writeJSON(w, http.StatusOK, map[string]any{"composition": saved})
}
func matchLayerCompositionRoute(path string) (string, bool) {
const prefix = "/api/layer-compositions/"
if !strings.HasPrefix(path, prefix) {
return "", false
}
jobID := strings.TrimPrefix(path, prefix)
return jobID, jobID != "" && !strings.Contains(jobID, "/")
}
func writeLayerCompositionError(w http.ResponseWriter, err error) {
switch {
case errors.Is(err, layercompositions.ErrConflict):
writeJSON(w, http.StatusConflict, map[string]string{"error": "图层工程已在其他页面更新,请重试"})
case errors.Is(err, layercompositions.ErrInvalid), errors.Is(err, layercompositions.ErrAssetNotFound):
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
default:
writeJobError(w, err, false)
}
}
func containsString(values []string, wanted string) bool {
for _, value := range values {
if value == wanted {
return true
}
}
return false
}