fix: audio cleanup srcObject, diagnostics rate limit, orphan cleanup race (BUG-107, BUG-121, BUG-132)

BUG-107: cleanupAllAudioElements now calls pause() and sets
srcObject = null before removing elements from DOM, ensuring streams
are fully released during reconnection cleanup.

BUG-121: Diagnostics endpoint now has 5 req/min rate limit as
documented, preventing enumeration of internal topology.

BUG-132: DeleteOrphanedAttachments uses DELETE ... RETURNING stored_as
(atomic) instead of separate SELECT then DELETE, eliminating the race
where a file could be deleted after its attachment was linked.
This commit is contained in:
J3vb
2026-04-02 13:45:41 +02:00
parent e9543ce6d6
commit 827b370ac1
3 changed files with 26 additions and 19 deletions
+10 -15
View File
@@ -159,16 +159,21 @@ func (d *DB) GetAttachmentsByMessageIDs(msgIDs []int64) (map[int64][]AttachmentI
return result, nil
}
// DeleteOrphanedAttachments removes attachment records where message_id IS NULL
// and uploaded_at is older than the given cutoff time string (ISO 8601).
// Returns the stored_as filenames of deleted records so the caller can remove files.
// DeleteOrphanedAttachments atomically removes attachment records where
// message_id IS NULL and uploaded_at is older than the given cutoff time
// string (ISO 8601). Returns the stored_as filenames of deleted records
// so the caller can remove files.
//
// BUG-132: Uses DELETE ... RETURNING to make select+delete atomic,
// preventing a race where an attachment linked between SELECT and DELETE
// would have its file deleted while the DB row survives.
func (d *DB) DeleteOrphanedAttachments(cutoff string) ([]string, error) {
rows, err := d.sqlDB.Query(
`SELECT stored_as FROM attachments WHERE message_id IS NULL AND uploaded_at < ?`,
`DELETE FROM attachments WHERE message_id IS NULL AND uploaded_at < ? RETURNING stored_as`,
cutoff,
)
if err != nil {
return nil, fmt.Errorf("DeleteOrphanedAttachments query: %w", err)
return nil, fmt.Errorf("DeleteOrphanedAttachments: %w", err)
}
defer rows.Close() //nolint:errcheck
@@ -184,15 +189,5 @@ func (d *DB) DeleteOrphanedAttachments(cutoff string) ([]string, error) {
return nil, fmt.Errorf("DeleteOrphanedAttachments rows: %w", rows.Err())
}
if len(files) > 0 {
_, err = d.sqlDB.Exec(
`DELETE FROM attachments WHERE message_id IS NULL AND uploaded_at < ?`,
cutoff,
)
if err != nil {
return nil, fmt.Errorf("DeleteOrphanedAttachments delete: %w", err)
}
}
return files, nil
}