refactor(conflicts): one ConflictDetail builder for the three row shapes

ListConflicts, GetConflict, and the SSR media-detail active-conflict
lookup each hand-mapped their sqlc row (list-by-user, get-by-id,
list-by-media-item) into ConflictDetailResponse, triplicating the
optional resolution tail — including the silently-swallowed
ResolutionData unmarshal error, three times. NewConflictDetail is the
single mapper; a malformed resolution blob now logs instead of
vanishing.

Also extracts koreaderSyncEndpoints (the KOReader URL map duplicated
verbatim between token rotation and device approval; kobo's two URL
sets stay local because they deliberately differ).

Verified in the test-runner container (host conflict tests fail
environmentally on the container-only uploads path — baseline behavior).
This commit is contained in:
John O'Keefe committed 2026-10-17 16:42:37 -04:00
1 parent 58fbe372e2
commit b47a2b86a7
3 files changed
+62 -55

No files matched your search

+5 -9
View File
@@ -1590,15 +1590,11 @@ func registerFrontendRoutes(cfg *Config) {
if conf.ResolutionStatus.Valid && conf.ResolutionStatus.String == "unresolved" {
var conflictData map[string]handlers.ConflictSourceData
if err := json.Unmarshal(conf.ConflictData, &conflictData); err == nil {
activeConflict = &handlers.ConflictDetailResponse{
ID: uuid.UUID(conf.ID.Bytes).String(),
MediaItemID: uuid.UUID(conf.MediaItemID.Bytes).String(),
MediaItemTitle: mediaItem.Title,
ConflictType: conf.ConflictType,
ConflictData: conflictData,
ResolutionStatus: conf.ResolutionStatus.String,
CreatedAt: conf.CreatedAt.Time,
}
detail := handlers.NewConflictDetail(conf.ID, conf.MediaItemID,
mediaItem.Title, conf.ConflictType, conf.ResolutionStatus.String,
conflictData, conf.CreatedAt, conf.ResolvedAt, conf.ResolvedBy,
conf.ResolutionData)
activeConflict = &detail
}
break
}