mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-08-07 01:13:11 +02:00
merge: reconcile companion cleanup with fix-general-tidy
Merge the maintainer's fix-general-tidy branch (neighbor discovery, keygen, API endpoints, web-asset rebuild, HTTP server config/control commands, and an independent #286 room-server push/ACK/guest fix) into the companion cleanup branch. Both branches fixed #286 in parallel with byte-identical push-ACK CRC logic. In the six overlapping files the maintainer's implementation is kept (ACL replay-detection/session helpers, encoded path-len with legacy fallback, expected_crc/ack_timeout_s injector API, dispatcher ACK helpers); this branch's unique companion work is preserved on top (sender_prefix persistence + migration, boot-state hardening / CompanionStateLoadError, MessageQueue.max_size, older-core fallbacks). Conflict resolution took the maintainer's side across the overlap, then fixed two integration seams the merge introduced and updated this branch's tests to the maintainer's API: - room_server: timeout used undefined `hops`; aligned to `path_len`. - packet_router: PATH helper was invoked twice (maintainer's unconditional call plus this branch's conditional local-identity call); dropped the now-redundant conditional block. Pin openhop_core to @dev (was @feature/publish-workflow-message-handling) so this can merge to the repeater's dev; core dev carries the required sender_prefix and PathUtils.is_valid_path_len APIs. Full suite green (1040 passed) against openhop_core dev and refactor/companion-housekeeping; ruff clean.
This commit is contained in:
@@ -696,7 +696,7 @@ class SQLiteHandler:
|
||||
except Exception:
|
||||
fwd_path_val = str(fwd_path)
|
||||
|
||||
conn.execute(
|
||||
cursor = conn.execute(
|
||||
"""
|
||||
INSERT INTO packets (
|
||||
timestamp, type, route, length, rssi, snr, score,
|
||||
@@ -739,6 +739,7 @@ class SQLiteHandler:
|
||||
),
|
||||
)
|
||||
self._invalidate_hot_caches()
|
||||
return cursor.lastrowid
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store packet in SQLite: {e}")
|
||||
@@ -1017,6 +1018,7 @@ class SQLiteHandler:
|
||||
packets = conn.execute(
|
||||
"""
|
||||
SELECT
|
||||
id,
|
||||
timestamp, type, route, length, rssi, snr, score,
|
||||
transmitted, is_duplicate, drop_reason, src_hash, dst_hash, path_hash,
|
||||
transport_codes, payload, payload_length,
|
||||
@@ -1069,6 +1071,7 @@ class SQLiteHandler:
|
||||
|
||||
base_query = """
|
||||
SELECT
|
||||
id,
|
||||
timestamp, type, route, length, rssi, snr, score,
|
||||
transmitted, is_duplicate, drop_reason, src_hash, dst_hash, path_hash,
|
||||
transport_codes, payload, payload_length,
|
||||
@@ -1201,6 +1204,7 @@ class SQLiteHandler:
|
||||
packet = conn.execute(
|
||||
"""
|
||||
SELECT
|
||||
id,
|
||||
timestamp, type, route, length, rssi, snr, score,
|
||||
transmitted, is_duplicate, drop_reason, src_hash, dst_hash, path_hash,
|
||||
header, transport_codes, payload, payload_length,
|
||||
@@ -1218,6 +1222,32 @@ class SQLiteHandler:
|
||||
logger.error(f"Failed to get packet by hash: {e}")
|
||||
return None
|
||||
|
||||
def get_packet_by_id(self, packet_id: int) -> Optional[dict]:
|
||||
try:
|
||||
with self._connect() as conn:
|
||||
conn.row_factory = sqlite3.Row
|
||||
|
||||
packet = conn.execute(
|
||||
"""
|
||||
SELECT
|
||||
id,
|
||||
timestamp, type, route, length, rssi, snr, score,
|
||||
transmitted, is_duplicate, drop_reason, src_hash, dst_hash, path_hash,
|
||||
header, transport_codes, payload, payload_length,
|
||||
tx_delay_ms, packet_hash, original_path, forwarded_path, raw_packet,
|
||||
lbt_attempts, lbt_backoff_delays_ms, lbt_channel_busy
|
||||
FROM packets
|
||||
WHERE id = ?
|
||||
""",
|
||||
(packet_id,),
|
||||
).fetchone()
|
||||
|
||||
return dict(packet) if packet else None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get packet by id: {e}")
|
||||
return None
|
||||
|
||||
def get_packet_type_stats(self, hours: int = 24) -> dict:
|
||||
try:
|
||||
now = time.time()
|
||||
@@ -2164,29 +2194,32 @@ class SQLiteHandler:
|
||||
return []
|
||||
|
||||
def upsert_client_sync(self, room_hash: str, client_pubkey: str, **kwargs) -> bool:
|
||||
"""Insert or update client sync state using single upsert operation."""
|
||||
"""Insert or update client sync state without clobbering unspecified fields."""
|
||||
try:
|
||||
with self._connect() as conn:
|
||||
now = time.time()
|
||||
kwargs["updated_at"] = now
|
||||
update_fields = dict(kwargs)
|
||||
update_fields["updated_at"] = now
|
||||
|
||||
# Set defaults for insert path
|
||||
kwargs.setdefault("sync_since", 0)
|
||||
kwargs.setdefault("pending_ack_crc", 0)
|
||||
kwargs.setdefault("push_post_timestamp", 0)
|
||||
kwargs.setdefault("ack_timeout_time", 0)
|
||||
kwargs.setdefault("push_failures", 0)
|
||||
kwargs.setdefault("last_activity", now)
|
||||
# INSERT must satisfy NOT NULL columns (last_activity), while
|
||||
# ON CONFLICT updates should only touch supplied fields.
|
||||
insert_fields = dict(update_fields)
|
||||
if insert_fields.get("last_activity") is None:
|
||||
insert_fields["last_activity"] = now
|
||||
|
||||
columns = ["room_hash", "client_pubkey"] + list(kwargs.keys())
|
||||
columns = ["room_hash", "client_pubkey"] + list(insert_fields.keys())
|
||||
placeholders = ["?"] * len(columns)
|
||||
values = [room_hash, client_pubkey] + list(kwargs.values())
|
||||
values = [room_hash, client_pubkey] + list(insert_fields.values())
|
||||
|
||||
# Use INSERT OR REPLACE for single atomic upsert
|
||||
# Update only supplied columns on conflict so partial updates don't
|
||||
# reset counters/state such as push_failures.
|
||||
update_set = ", ".join(f"{col}=excluded.{col}" for col in update_fields.keys())
|
||||
conn.execute(
|
||||
f"""
|
||||
INSERT OR REPLACE INTO room_client_sync ({", ".join(columns)})
|
||||
INSERT INTO room_client_sync ({", ".join(columns)})
|
||||
VALUES ({", ".join(placeholders)})
|
||||
ON CONFLICT(room_hash, client_pubkey)
|
||||
DO UPDATE SET {update_set}
|
||||
""",
|
||||
values,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user