diff --git a/backend/internal/backend/handlers_image.go b/backend/internal/backend/handlers_image.go index f6ba684..c6de202 100644 --- a/backend/internal/backend/handlers_image.go +++ b/backend/internal/backend/handlers_image.go @@ -559,6 +559,11 @@ func (s *Server) handleAdminImageGenAsync(w http.ResponseWriter, r *http.Request go func() { defer deregisterCancelJob(jobID) defer jobCancel() + defer func() { + if r := recover(); r != nil { + logger.Error("admin: image-gen goroutine panic", "job_id", jobID, "recover", r) + } + }() if jobCtx.Err() != nil { _ = store.UpdateAIJob(context.Background(), jobID, map[string]any{ @@ -625,13 +630,19 @@ func (s *Server) handleAdminImageGenAsync(w http.ResponseWriter, r *http.Request Guidance: capturedReq.Guidance, }) - _ = store.UpdateAIJob(context.Background(), jobID, map[string]any{ - "status": string(domain.TaskStatusDone), - "items_done": 1, + if err := store.UpdateAIJob(context.Background(), jobID, map[string]any{ + "status": string(domain.TaskStatusDone), + "items_done": 1, "items_total": 1, - "payload": string(resultJSON), - "finished": time.Now().Format(time.RFC3339), - }) + "finished": time.Now().Format(time.RFC3339), + }); err != nil { + logger.Error("admin: image-gen failed to mark job done", "job_id", jobID, "err", err) + } + if err := store.UpdateAIJob(context.Background(), jobID, map[string]any{ + "payload": string(resultJSON), + }); err != nil { + logger.Error("admin: image-gen failed to write job payload", "job_id", jobID, "err", err) + } logger.Info("admin: image-gen async done", "job_id", jobID, "slug", capturedReq.Slug, diff --git a/backend/internal/backend/handlers_textgen.go b/backend/internal/backend/handlers_textgen.go index 6cde3a3..f5f2be4 100644 --- a/backend/internal/backend/handlers_textgen.go +++ b/backend/internal/backend/handlers_textgen.go @@ -516,6 +516,11 @@ func (s *Server) handleAdminTextGenChapterNamesAsync(w http.ResponseWriter, r *h go func() { defer deregisterCancelJob(jobID) defer jobCancel() + defer func() { + if r := recover(); r != nil { + logger.Error("admin: text-gen chapter-names goroutine panic", "job_id", jobID, "recover", r) + } + }() var allResults []proposedChapterTitle chaptersDone := 0 @@ -574,12 +579,18 @@ func (s *Server) handleAdminTextGenChapterNamesAsync(w http.ResponseWriter, r *h if jobCtx.Err() != nil { status = domain.TaskStatusCancelled } - _ = store.UpdateAIJob(context.Background(), jobID, map[string]any{ + if err := store.UpdateAIJob(context.Background(), jobID, map[string]any{ "status": string(status), "items_done": chaptersDone, "finished": time.Now().Format(time.RFC3339), - "payload": finalPayload, - }) + }); err != nil { + logger.Error("admin: text-gen chapter-names failed to mark job done", "job_id", jobID, "err", err) + } + if err := store.UpdateAIJob(context.Background(), jobID, map[string]any{ + "payload": finalPayload, + }); err != nil { + logger.Error("admin: text-gen chapter-names failed to write job payload", "job_id", jobID, "err", err) + } logger.Info("admin: text-gen chapter-names async done", "job_id", jobID, "slug", capturedSlug, "results", len(allResults), "status", string(status)) @@ -902,6 +913,11 @@ func (s *Server) handleAdminTextGenDescriptionAsync(w http.ResponseWriter, r *ht go func() { defer deregisterCancelJob(jobID) defer jobCancel() + defer func() { + if r := recover(); r != nil { + logger.Error("admin: text-gen description goroutine panic", "job_id", jobID, "recover", r) + } + }() if jobCtx.Err() != nil { _ = store.UpdateAIJob(context.Background(), jobID, map[string]any{ @@ -955,13 +971,19 @@ func (s *Server) handleAdminTextGenDescriptionAsync(w http.ResponseWriter, r *ht NewDescription: strings.TrimSpace(newDesc), }) - _ = store.UpdateAIJob(context.Background(), jobID, map[string]any{ + if err := store.UpdateAIJob(context.Background(), jobID, map[string]any{ "status": string(domain.TaskStatusDone), "items_done": 1, "items_total": 1, - "payload": string(resultJSON), "finished": time.Now().Format(time.RFC3339), - }) + }); err != nil { + logger.Error("admin: text-gen description failed to mark job done", "job_id", jobID, "err", err) + } + if err := store.UpdateAIJob(context.Background(), jobID, map[string]any{ + "payload": string(resultJSON), + }); err != nil { + logger.Error("admin: text-gen description failed to write job payload", "job_id", jobID, "err", err) + } logger.Info("admin: text-gen description async done", "job_id", jobID, "slug", capturedMeta.Slug) }()