fix: add panic recovery to search-local handler, remove unused event emission

Added defer/recover to SearchLocalHandler to prevent panics from
crashing the app. Removed the now-unused Wails event emission from
Search() and the runtime import — local results are served via the
HTTP endpoint exclusively.
This commit is contained in:
2026-03-29 17:52:33 -04:00
parent c3e16a9fd2
commit 33d42febf1
3 changed files with 22 additions and 42 deletions
+15 -42
View File
@@ -11,8 +11,6 @@ import (
"sync"
"time"
"github.com/wailsapp/wails/v2/pkg/runtime"
"yellowjacket/backend/database"
)
@@ -162,20 +160,29 @@ func (e *Service) SearchLocal(query string) *MBSearchResult {
// serialization queue, ensuring sub-millisecond response times.
func (e *Service) SearchLocalHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rv := recover(); rv != nil {
e.logger.Error("search-local handler panic", "recover", rv)
http.Error(w, "internal error", http.StatusInternalServerError)
}
}()
query := r.URL.Query().Get("q")
if query == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
result := e.SearchLocal(query)
if result == nil {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte("null"))
return
}
result := e.SearchLocal(query)
w.Header().Set("Content-Type", "application/json")
if result == nil {
_, _ = w.Write([]byte("null"))
return
}
_ = json.NewEncoder(w).Encode(result)
})
}
@@ -363,40 +370,6 @@ func (e *Service) Search(query string) (*MBSearchResult, error) {
"elapsed", p0Dur.Round(time.Millisecond),
)
// Emit local results immediately via event so the frontend can
// render them while the full pipeline runs. This avoids the
// Wails RPC serialization bottleneck that blocks SearchLocal.
if len(indexHits) > 0 {
var localResult MBSearchResult
mergeIndexHits(&localResult, indexHits)
// Remove SPAs from local results.
if len(localResult.Artists) > 0 {
filtered := localResult.Artists[:0]
for _, a := range localResult.Artists {
if !mbSpecialPurposeArtists[a.MBID] {
filtered = append(filtered, a)
}
}
localResult.Artists = filtered
}
if len(localResult.Artists) > maxResults {
localResult.Artists = localResult.Artists[:maxResults]
}
if len(localResult.ReleaseGroups) > maxResults {
localResult.ReleaseGroups = localResult.ReleaseGroups[:maxResults]
}
if len(localResult.Recordings) > maxResults {
localResult.Recordings = localResult.Recordings[:maxResults]
}
runtime.EventsEmit(e.ctx, "search:local-results", localResult)
}
// Phase 1: concurrent MB search (3 goroutines) with a deadline
// so a slow MusicBrainz server doesn't hold up the whole search.
p1Start := time.Now()
+3
View File
@@ -1,6 +1,7 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {explore} from '../models';
import {http} from '../models';
import {context} from '../models';
export function BrowseReleaseGroups(arg1:string):Promise<Array<explore.MBReleaseGroup>>;
@@ -37,6 +38,8 @@ export function SearchArtists(arg1:string):Promise<Array<explore.MBArtist>>;
export function SearchLocal(arg1:string):Promise<explore.MBSearchResult>;
export function SearchLocalHandler():Promise<http.Handler>;
export function SearchRecordings(arg1:string):Promise<Array<explore.MBRecording>>;
export function SearchReleaseGroups(arg1:string):Promise<Array<explore.MBReleaseGroup>>;
+4
View File
@@ -70,6 +70,10 @@ export function SearchLocal(arg1) {
return window['go']['explore']['Service']['SearchLocal'](arg1);
}
export function SearchLocalHandler() {
return window['go']['explore']['Service']['SearchLocalHandler']();
}
export function SearchRecordings(arg1) {
return window['go']['explore']['Service']['SearchRecordings'](arg1);
}