Merge pull request #42 from yimizilu/fix/http-image-media-identifiers

fix: prevent HTTP image media ID collisions across conversations
This commit is contained in:
qingmao
2026-09-15 14:57:26 +08:00
committed by GitHub
6 changed files with 241 additions and 16 deletions
+29 -2
View File
@@ -1,3 +1,4 @@
import { createHash, randomUUID } from 'node:crypto'
import { WechatDb, WechatMessage } from '../wechat-db'
import {
parseImageBufferDataUrlFromRow,
@@ -156,7 +157,9 @@ export interface ImageMessageReference {
}
const imageMessageReferences = new Map<string, ImageMessageReference | null>()
let imageReferenceScope = randomUUID()
/** Replace the active database and invalidate connection-scoped lookup caches. */
export function setChatDb(db: WechatDb | null): boolean {
if (shutdownRequested) {
db?.close()
@@ -166,6 +169,7 @@ export function setChatDb(db: WechatDb | null): boolean {
dbRef = db
contactSearchIndexCache = null
imageMessageReferences.clear()
imageReferenceScope = randomUUID()
return true
}
@@ -367,6 +371,7 @@ export async function getContactAvatars(
return client.getAvatarUrlsAsync(normalized)
}
/** Format source rows and register image handles without merging recall archives. */
function listSourceMessages(
userMd5: string,
startTime?: number,
@@ -556,11 +561,28 @@ function listSourceMessages(
: msg.mesLocalID || Math.random().toString()
)
const imageContent = contentData?.type === 'image' ? contentData : undefined
// Local ids repeat across conversations. Scope media handles to this database
// connection and image without changing the message id used by other clients.
const mediaId = imageContent
? `image:${createHash('sha256')
.update(
JSON.stringify([
imageReferenceScope,
userMd5,
messageId,
String(msg.serverId || ''),
createTime,
imageContent.md5 || '',
imageContent.datName || ''
])
)
.digest('hex')}`
: ''
const media = imageContent
? {
type: 'image' as const,
available: Boolean(imageContent.md5 || imageContent.datName),
url: `/api/v1/media/${encodeURIComponent(messageId)}`
url: `/api/v1/media/${encodeURIComponent(mediaId)}`
}
: undefined
if (imageContent && media) {
@@ -571,6 +593,8 @@ function listSourceMessages(
imageDatName: imageContent.datName,
createTime
}
imageMessageReferences.set(mediaId, reference)
// Keep old bare-id URLs working only while they are unambiguous.
const previous = imageMessageReferences.get(messageId)
if (
previous &&
@@ -598,7 +622,10 @@ function listSourceMessages(
senderId,
sessionId: username,
localId,
serverId: typeof msg.serverId === 'string' ? msg.serverId : undefined,
serverId:
typeof msg.serverId === 'string' || typeof msg.serverId === 'bigint'
? String(msg.serverId)
: undefined,
createTime,
recoveredFromRecallJournal,
contentData,