handler.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. # -*- coding: utf-8 -*-
  2. """
  3. SyncHandler — Server-seitige Verarbeitung von Sync-Nachrichten.
  4. Registriert sich als Message-Handler bei NetworkService.
  5. Verarbeitet SyncPush und SyncRequest von Standalone-Clients.
  6. """
  7. from __future__ import annotations
  8. from typing import TYPE_CHECKING
  9. from trixy_core.sync.models import SyncItem, _now_iso
  10. from trixy_core.network.cmd.sync import SyncPush, SyncRequest, SyncResponse, SyncAck
  11. from trixy_core.network.protocol import ProtocolMessage
  12. from trixy_core.utils.debug import pinfo, pdebug, perror
  13. if TYPE_CHECKING:
  14. from trixy_core.server import ServerApplication
  15. from trixy_core.satellite.satellite import Satellite
  16. class SyncHandler:
  17. """
  18. Server-seitiger Handler fuer Sync-Nachrichten.
  19. Verarbeitet:
  20. - SyncPush: Items vom Standalone empfangen und lokal mergen
  21. - SyncRequest: Aenderungen seit Zeitpunkt X zuruecksenden
  22. """
  23. def __init__(self, application: ServerApplication) -> None:
  24. """
  25. Initialisiert den SyncHandler.
  26. Args:
  27. application: Die Server-Anwendung
  28. """
  29. self._app = application
  30. async def on_message(self, satellite: Satellite, message: ProtocolMessage) -> None:
  31. """
  32. Verarbeitet eine eingehende Sync-Nachricht.
  33. Wird als Message-Handler bei NetworkService registriert.
  34. Args:
  35. satellite: Der sendende Satellite
  36. message: Die empfangene Nachricht
  37. """
  38. class_name = message.class_name
  39. if class_name == "SyncPush":
  40. await self._handle_sync_push(satellite, message)
  41. elif class_name == "SyncRequest":
  42. await self._handle_sync_request(satellite, message)
  43. async def _handle_sync_push(self, satellite: Satellite, message: ProtocolMessage) -> None:
  44. """Verarbeitet empfangene SyncPush-Nachrichten."""
  45. store = self._app.sync_store
  46. if store is None:
  47. perror("SyncHandler: Kein SyncStore verfuegbar")
  48. return
  49. data = message.data
  50. items: list[dict] = []
  51. if isinstance(data, SyncPush):
  52. items = data.items
  53. elif isinstance(data, dict):
  54. items = data.get("items", [])
  55. else:
  56. perror(f"SyncHandler: Ungueltiges SyncPush-Format: {type(data)}")
  57. return
  58. merged_count = 0
  59. for item_dict in items:
  60. item = SyncItem.from_dict(item_dict)
  61. if store.merge_incoming(item):
  62. merged_count += 1
  63. # Bestaetigung senden
  64. ack = SyncAck(accepted_count=merged_count)
  65. try:
  66. await self._app.network.send_to_satellite(satellite.id, ack)
  67. except Exception as e:
  68. perror(f"SyncHandler: SyncAck senden fehlgeschlagen: {e}")
  69. if merged_count > 0:
  70. pdebug(f"SyncHandler: {merged_count} Items von {satellite.alias} gemerged")
  71. # Event emittieren (damit Server-Plugins reagieren koennen)
  72. await self._app.events.emit("sync_item_received", {
  73. "satellite_id": satellite.id,
  74. "merged_count": merged_count,
  75. })
  76. async def _handle_sync_request(self, satellite: Satellite, message: ProtocolMessage) -> None:
  77. """Verarbeitet SyncRequest-Nachrichten."""
  78. store = self._app.sync_store
  79. if store is None:
  80. perror("SyncHandler: Kein SyncStore verfuegbar")
  81. return
  82. data = message.data
  83. since = ""
  84. if isinstance(data, SyncRequest):
  85. since = data.since
  86. elif isinstance(data, dict):
  87. since = data.get("since", "")
  88. # Aenderungen seit 'since' sammeln
  89. if since:
  90. changes = store.get_changes_since(since)
  91. else:
  92. # Erster Sync: Alle Items senden
  93. changes = store.get_changes_since("")
  94. # Antwort senden
  95. server_time = _now_iso()
  96. response = SyncResponse(
  97. items=[item.to_dict() for item in changes],
  98. server_time=server_time,
  99. )
  100. try:
  101. await self._app.network.send_to_satellite(satellite.id, response)
  102. except Exception as e:
  103. perror(f"SyncHandler: SyncResponse senden fehlgeschlagen: {e}")
  104. pdebug(f"SyncHandler: {len(changes)} Items an {satellite.alias} gesendet (seit: {since or 'Anfang'})")