fix(janitor): protect dm_channels.icon files from cleanup (owner + receiver)

getProfileReferencedFilenames() didn't include dm_channels.icon, so the
storage janitor deleted group DM icons within ~1 hour:

- Owner instance: PATCH /api/dm/:id leaves an attachments row with
  messageId=null and dmMessageId=null. After 1h, getUnlinkedAttachments
  flags it and cleanupStorage phase 2 deletes the file because the
  filename isn't in profileReferenced.

- Receiver instance: downloadProfileAsset writes the icon directly to
  uploadDir with no attachments row. cleanupStorage phase 1 treats it
  as orphaned and deletes it.

Fix: include dm_channels.icon (non-null, not soft-deleted, not http://)
in the profile-referenced set. Soft-deleted DMs are excluded so their
files still get reaped by cleanupSoftDeletedDmChannels. Absolute URLs
are skipped because they live on a remote instance.

Also mirror the avatar precedent at the PATCH endpoint by deleting the
new icon's standalone attachment row — the file is now protected via
dm_channels.icon, matching users.ts:473.
This commit is contained in:
Jannis Braun
2026-05-10 21:18:58 +02:00
parent 0acdbbc4eb
commit 24240f24f4
3 changed files with 235 additions and 0 deletions
+6
View File
@@ -1606,6 +1606,12 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
deleteUploadFile(oldIcon);
deleteAttachmentByFilename(oldIcon);
}
// Clean up the attachment record for the newly-set icon — the file is
// now referenced via dm_channels.icon (protected by the storage janitor),
// so the standalone attachment row is unnecessary. Mirrors users.ts:473.
if (iconChanged && nextIcon && !nextIcon.startsWith('http')) {
deleteAttachmentByFilename(nextIcon);
}
return reply.code(200).send({
id,