mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-08-06 16:53:21 +02:00
fix(dm): prevent orphaned DMs on contact deletion, improve relinking
- Stop deleting contacts from DB on device removal (preserves DM history) - Filter NULL contact_pubkey from DM conversations list - Match outgoing DMs by contact name in raw_json during relinking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+19
-9
@@ -344,6 +344,7 @@ class Database:
|
||||
ORDER BY d3.timestamp DESC LIMIT 1) AS last_direction
|
||||
FROM direct_messages dm
|
||||
LEFT JOIN contacts c ON dm.contact_pubkey = c.public_key
|
||||
WHERE dm.contact_pubkey IS NOT NULL
|
||||
GROUP BY dm.contact_pubkey
|
||||
ORDER BY last_message_timestamp DESC"""
|
||||
).fetchall()
|
||||
@@ -382,22 +383,31 @@ class Database:
|
||||
).fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
def relink_orphaned_dms(self, public_key: str) -> int:
|
||||
def relink_orphaned_dms(self, public_key: str, name: str = '') -> int:
|
||||
"""Re-link DMs with NULL contact_pubkey back to this contact.
|
||||
|
||||
When a contact is deleted, ON DELETE SET NULL nullifies contact_pubkey.
|
||||
When the contact is re-added, re-link those orphaned DMs.
|
||||
Uses raw_json to match by pubkey_prefix.
|
||||
Matches by pubkey prefix in raw_json (incoming) or contact name (outgoing).
|
||||
"""
|
||||
public_key = public_key.lower()
|
||||
prefix = public_key[:12] # Short prefix used in pubkey_prefix field
|
||||
prefix = public_key[:12]
|
||||
with self._connect() as conn:
|
||||
cursor = conn.execute(
|
||||
"""UPDATE direct_messages SET contact_pubkey = ?
|
||||
WHERE contact_pubkey IS NULL
|
||||
AND (raw_json LIKE ? OR raw_json IS NULL)""",
|
||||
(public_key, f'%{prefix}%')
|
||||
)
|
||||
if name:
|
||||
cursor = conn.execute(
|
||||
"""UPDATE direct_messages SET contact_pubkey = ?
|
||||
WHERE contact_pubkey IS NULL
|
||||
AND (raw_json LIKE ? OR raw_json LIKE ?
|
||||
OR raw_json IS NULL)""",
|
||||
(public_key, f'%{prefix}%', f'%"name": "{name}"%')
|
||||
)
|
||||
else:
|
||||
cursor = conn.execute(
|
||||
"""UPDATE direct_messages SET contact_pubkey = ?
|
||||
WHERE contact_pubkey IS NULL
|
||||
AND (raw_json LIKE ? OR raw_json IS NULL)""",
|
||||
(public_key, f'%{prefix}%')
|
||||
)
|
||||
if cursor.rowcount > 0:
|
||||
logger.info(f"Re-linked {cursor.rowcount} orphaned DMs to {public_key[:12]}...")
|
||||
return cursor.rowcount
|
||||
|
||||
@@ -1000,13 +1000,14 @@ class DeviceManager:
|
||||
return self.db.get_contacts() # return cached
|
||||
|
||||
def delete_contact(self, pubkey: str) -> Dict:
|
||||
"""Delete a contact from device and database."""
|
||||
"""Delete a contact from device. Keep DB record to preserve DM history."""
|
||||
if not self.is_connected:
|
||||
return {'success': False, 'error': 'Device not connected'}
|
||||
|
||||
try:
|
||||
self.execute(self.mc.commands.remove_contact(pubkey))
|
||||
self.db.delete_contact(pubkey)
|
||||
# Don't delete from DB — ON DELETE SET NULL would orphan all DMs.
|
||||
# Contact stays in DB for historical reference; upsert updates on re-add.
|
||||
# Also remove from in-memory contacts cache
|
||||
if self.mc.contacts and pubkey in self.mc.contacts:
|
||||
del self.mc.contacts[pubkey]
|
||||
@@ -1187,7 +1188,8 @@ class DeviceManager:
|
||||
source='device',
|
||||
)
|
||||
# Re-link orphaned DMs (from previous ON DELETE SET NULL)
|
||||
self.db.relink_orphaned_dms(pubkey)
|
||||
contact_name = contact.get('adv_name', contact.get('name', ''))
|
||||
self.db.relink_orphaned_dms(pubkey, name=contact_name)
|
||||
|
||||
# Remove from pending list after successful approval
|
||||
self.mc.pending_contacts.pop(pubkey, None)
|
||||
|
||||
Reference in New Issue
Block a user