From 9e0b7e72ead4685d9e74f96216dcd9ce9fcdcd2f Mon Sep 17 00:00:00 2001 From: Mark Mennell Date: Mon, 24 Aug 2026 19:14:17 +0800 Subject: [PATCH] Make thread attachment cache keys unique --- internal/handlers/thread.go | 8 ++++---- internal/handlers/thread_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/handlers/thread.go b/internal/handlers/thread.go index e120458..7a164d8 100644 --- a/internal/handlers/thread.go +++ b/internal/handlers/thread.go @@ -72,14 +72,14 @@ type threadMessagesResponse struct { Messages []threadMessage `json:"messages"` } -func partCacheKey(messageHash, kind string, position int) string { +func partCacheKey(messageHash, kind string, position int, filename string) string { if messageHash == "" { return "" } if kind == "body" { return "sha256:" + messageHash + ":body" } - return "sha256:" + messageHash + ":attachment:" + strconv.Itoa(position) + return "sha256:" + messageHash + ":attachment:" + strconv.Itoa(position) + ":" + url.PathEscape(filename) } func threadDownloadPath(id int64, filename string) string { @@ -96,7 +96,7 @@ func populateThreadBodies(messages []threadMessage, dataDir string, maxTextBytes if !m.Visible { continue } - key := partCacheKey(m.MessageSHA256, "body", 0) + key := partCacheKey(m.MessageSHA256, "body", 0, "") m.Body = &threadBody{Type: m.Type, Size: m.Size, Download: threadDownloadPath(m.ID, ""), CacheKey: key, Cacheable: key != ""} if !isTextType(m.Type) { continue @@ -203,7 +203,7 @@ func loadThreadRelations(ctx context.Context, tx pgx.Tx, messages []threadMessag } m := byID[msgID] a.Download = threadDownloadPath(msgID, a.Filename) - a.CacheKey = partCacheKey(m.MessageSHA256, "attachment", a.Position) + a.CacheKey = partCacheKey(m.MessageSHA256, "attachment", a.Position, a.Filename) a.Cacheable = a.CacheKey != "" m.Attachments = append(m.Attachments, a) } diff --git a/internal/handlers/thread_test.go b/internal/handlers/thread_test.go index 3189aaa..c2a5337 100644 --- a/internal/handlers/thread_test.go +++ b/internal/handlers/thread_test.go @@ -85,10 +85,10 @@ func TestThreadMessagesJSONDoesNotExposeInternalPath(t *testing.T) { } func TestThreadAttachmentCacheKey(t *testing.T) { - if got := partCacheKey("deadbeef", "attachment", 4); got != "sha256:deadbeef:attachment:4" { + if got := partCacheKey("deadbeef", "attachment", 4, "report final.pdf"); got != "sha256:deadbeef:attachment:4:report%20final.pdf" { t.Fatalf("got %q", got) } - if got := partCacheKey("", "attachment", 4); got != "" { + if got := partCacheKey("", "attachment", 4, "report.pdf"); got != "" { t.Fatalf("hashless message must not be cacheable: %q", got) } }