mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-05 13:02:58 +02:00
Document and patch historical prefix claiming, and handle reconnect interval better
This commit is contained in:
@@ -362,6 +362,16 @@ packet, `packet_processor.py` handles the complete flow:
|
||||
export, unknown contact), `on_contact_message` handles DMs from the MeshCore library's
|
||||
`CONTACT_MSG_RECV` event. DB deduplication prevents double-storage when both paths fire.
|
||||
|
||||
**Prefix-stored DMs (edge case)**: A rare scenario can occur when the radio can decrypt
|
||||
a DM (contact is on the radio) but the server cannot (private key not exported or
|
||||
contact not yet known server-side). The `CONTACT_MSG_RECV` payload may only include a
|
||||
pubkey prefix. If the full key isn't known yet, the message is stored with the prefix
|
||||
as `conversation_key`. When a full contact key becomes known (via advertisement or
|
||||
radio sync), the server attempts to **claim** those prefix messages and upgrade them
|
||||
to the full key. Claims only occur when the prefix matches exactly one contact to
|
||||
avoid mis-attribution in large contact sets. Until claimed, these DMs will not show
|
||||
in the UI because conversations are keyed by full public keys.
|
||||
|
||||
**Outgoing DMs**: Outgoing direct messages are only sent via the app's REST API
|
||||
(`POST /api/messages/direct`), which stores the plaintext directly in the database.
|
||||
No decryption is needed for outgoing DMs. The real-time packet processor may also see
|
||||
|
||||
@@ -993,3 +993,4 @@ async def _migrate_015_fix_null_sender_timestamp(conn: aiosqlite.Connection) ->
|
||||
)
|
||||
|
||||
await conn.commit()
|
||||
|
||||
|
||||
@@ -685,6 +685,13 @@ async def _process_advertisement(
|
||||
}
|
||||
|
||||
await ContactRepository.upsert(contact_data)
|
||||
claimed = await MessageRepository.claim_prefix_messages(advert.public_key.lower())
|
||||
if claimed > 0:
|
||||
logger.info(
|
||||
"Claimed %d prefix DM message(s) for contact %s",
|
||||
claimed,
|
||||
advert.public_key[:12],
|
||||
)
|
||||
|
||||
# Broadcast contact update to connected clients
|
||||
broadcast_event(
|
||||
|
||||
@@ -18,7 +18,7 @@ from meshcore import EventType
|
||||
|
||||
from app.models import Contact
|
||||
from app.radio import radio_manager
|
||||
from app.repository import AppSettingsRepository, ChannelRepository, ContactRepository
|
||||
from app.repository import AppSettingsRepository, ChannelRepository, ContactRepository, MessageRepository
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -95,6 +95,13 @@ async def sync_and_offload_contacts() -> dict:
|
||||
await ContactRepository.upsert(
|
||||
Contact.from_radio_dict(public_key, contact_data, on_radio=False)
|
||||
)
|
||||
claimed = await MessageRepository.claim_prefix_messages(public_key.lower())
|
||||
if claimed > 0:
|
||||
logger.info(
|
||||
"Claimed %d prefix DM message(s) for contact %s",
|
||||
claimed,
|
||||
public_key[:12],
|
||||
)
|
||||
synced += 1
|
||||
|
||||
# Remove from radio
|
||||
|
||||
@@ -390,7 +390,11 @@ class MessageRepository:
|
||||
cursor = await db.conn.execute(
|
||||
"""UPDATE messages SET conversation_key = ?
|
||||
WHERE type = 'PRIV' AND length(conversation_key) < 64
|
||||
AND ? LIKE conversation_key || '%'""",
|
||||
AND ? LIKE conversation_key || '%'
|
||||
AND (
|
||||
SELECT COUNT(*) FROM contacts
|
||||
WHERE public_key LIKE messages.conversation_key || '%'
|
||||
) = 1""",
|
||||
(lower_key, lower_key),
|
||||
)
|
||||
await db.conn.commit()
|
||||
|
||||
@@ -60,6 +60,10 @@ export function useWebSocket(options: UseWebSocketOptions) {
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket connected');
|
||||
setConnected(true);
|
||||
if (reconnectTimeoutRef.current) {
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
reconnectTimeoutRef.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
@@ -68,6 +72,9 @@ export function useWebSocket(options: UseWebSocketOptions) {
|
||||
wsRef.current = null;
|
||||
|
||||
// Reconnect after 3 seconds
|
||||
if (reconnectTimeoutRef.current) {
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
}
|
||||
reconnectTimeoutRef.current = window.setTimeout(() => {
|
||||
console.log('Attempting WebSocket reconnect...');
|
||||
connect();
|
||||
@@ -146,6 +153,7 @@ export function useWebSocket(options: UseWebSocketOptions) {
|
||||
clearInterval(pingInterval);
|
||||
if (reconnectTimeoutRef.current) {
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
reconnectTimeoutRef.current = null;
|
||||
}
|
||||
if (wsRef.current) {
|
||||
wsRef.current.close();
|
||||
|
||||
Reference in New Issue
Block a user