fix: cover_url bug, add save-cover endpoint, fix svelte-check CI failure
All checks were successful
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Successful in 44s
Release / Docker / caddy (push) Successful in 42s
Release / Docker / backend (push) Successful in 2m50s
Release / Docker / runner (push) Successful in 2m42s
Release / Docker / ui (push) Successful in 2m9s
Release / Gitea Release (push) Successful in 38s
All checks were successful
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Successful in 44s
Release / Docker / caddy (push) Successful in 42s
Release / Docker / backend (push) Successful in 2m50s
Release / Docker / runner (push) Successful in 2m42s
Release / Docker / ui (push) Successful in 2m9s
Release / Gitea Release (push) Successful in 38s
- Fix handlers_image.go: cover_url now uses /api/cover/novelfire.net/{slug} (was 'local')
- Add POST /api/admin/image-gen/save-cover: persists pre-generated base64 directly to
MinIO via PutCover without re-calling Cloudflare AI
- Add SvelteKit proxy route api/admin/image-gen/save-cover/+server.ts
- Update UI saveAsCover(): send existing image_b64 to save-cover instead of re-generating
- Run npm run paraglide to compile admin_nav_image_gen message; force-add gitignored files
- svelte-check: 0 errors, 21 warnings (all pre-existing)
This commit is contained in:
@@ -195,7 +195,7 @@ func (s *Server) handleAdminImageGen(w http.ResponseWriter, r *http.Request) {
|
||||
// Non-fatal: still return the image
|
||||
} else {
|
||||
saved = true
|
||||
coverURL = fmt.Sprintf("/api/cover/local/%s", req.Slug)
|
||||
coverURL = fmt.Sprintf("/api/cover/novelfire.net/%s", req.Slug)
|
||||
s.deps.Log.Info("admin: generated cover saved", "slug", req.Slug, "bytes", len(imgData))
|
||||
}
|
||||
}
|
||||
@@ -213,6 +213,62 @@ func (s *Server) handleAdminImageGen(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// saveCoverRequest is the JSON body for POST /api/admin/image-gen/save-cover.
|
||||
type saveCoverRequest struct {
|
||||
// Slug is the book slug whose cover should be overwritten.
|
||||
Slug string `json:"slug"`
|
||||
// ImageB64 is the base64-encoded image bytes (PNG or JPEG).
|
||||
ImageB64 string `json:"image_b64"`
|
||||
}
|
||||
|
||||
// handleAdminImageGenSaveCover handles POST /api/admin/image-gen/save-cover.
|
||||
//
|
||||
// Accepts a pre-generated image as base64 and stores it as the book cover in
|
||||
// MinIO, replacing the existing one. Does not call Cloudflare AI at all.
|
||||
func (s *Server) handleAdminImageGenSaveCover(w http.ResponseWriter, r *http.Request) {
|
||||
if s.deps.CoverStore == nil {
|
||||
jsonError(w, http.StatusServiceUnavailable, "cover store not configured")
|
||||
return
|
||||
}
|
||||
|
||||
var req saveCoverRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
jsonError(w, http.StatusBadRequest, "parse body: "+err.Error())
|
||||
return
|
||||
}
|
||||
if req.Slug == "" {
|
||||
jsonError(w, http.StatusBadRequest, "slug is required")
|
||||
return
|
||||
}
|
||||
if req.ImageB64 == "" {
|
||||
jsonError(w, http.StatusBadRequest, "image_b64 is required")
|
||||
return
|
||||
}
|
||||
|
||||
imgData, err := base64.StdEncoding.DecodeString(req.ImageB64)
|
||||
if err != nil {
|
||||
imgData, err = base64.RawStdEncoding.DecodeString(req.ImageB64)
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadRequest, "decode image_b64: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
contentType := sniffImageContentType(imgData)
|
||||
if err := s.deps.CoverStore.PutCover(r.Context(), req.Slug, imgData, contentType); err != nil {
|
||||
s.deps.Log.Error("admin: save-cover failed", "slug", req.Slug, "err", err)
|
||||
jsonError(w, http.StatusInternalServerError, "save cover: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.deps.Log.Info("admin: cover saved via image-gen", "slug", req.Slug, "bytes", len(imgData))
|
||||
writeJSON(w, 0, map[string]any{
|
||||
"saved": true,
|
||||
"cover_url": fmt.Sprintf("/api/cover/novelfire.net/%s", req.Slug),
|
||||
"bytes": len(imgData),
|
||||
})
|
||||
}
|
||||
|
||||
// sniffImageContentType returns the MIME type of the image bytes.
|
||||
func sniffImageContentType(data []byte) string {
|
||||
if len(data) >= 4 {
|
||||
|
||||
@@ -189,6 +189,7 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
|
||||
// Admin image generation endpoints
|
||||
mux.HandleFunc("GET /api/admin/image-gen/models", s.handleAdminImageGenModels)
|
||||
mux.HandleFunc("POST /api/admin/image-gen", s.handleAdminImageGen)
|
||||
mux.HandleFunc("POST /api/admin/image-gen/save-cover", s.handleAdminImageGenSaveCover)
|
||||
|
||||
// Voices list
|
||||
mux.HandleFunc("GET /api/voices", s.handleVoices)
|
||||
|
||||
Reference in New Issue
Block a user