feat(sockets/stellar): WebSocket health checks, offline sync, location broadcast & Soroban RPC setup#75
Open
gregemax wants to merge 4 commits into
Open
Conversation
…up (SwiftChainn#26) - Add socket.io@4.7.2 to dependencies - Create src/sockets/socket.types.ts: strongly-typed interfaces for connection metadata, ping/pong payloads, server/client events, and HealthCheckResult - Create src/sockets/socket.service.ts: SocketService class (business logic layer) with: * Connection registration and in-memory tracking * Ping/pong health-check loop with configurable interval and max-missed-pong threshold (env: SOCKET_PING_INTERVAL_MS, SOCKET_MAX_MISSED_PONGS) * Stale connection eviction after exceeding missed-pong threshold * Room join/leave tracking for clean state on disconnect * Full Winston logging for connect, pong, disconnect, and eviction events - Create src/sockets/connectionHandler.ts: controller layer that initialises the typed Socket.IO server, registers per-socket event handlers, and exposes initializeSocketServer / shutdownSocketServer - Update src/server.ts: wrap Express in http.Server, attach Socket.IO, include Socket.IO in graceful shutdown sequence - Add tests/socket.service.test.ts: 22 unit tests covering all service methods including health-check tick eviction logic Closes SwiftChainn#26
…tChainn#27) Model layer: - Add src/models/LocationUpdate.ts: Mongoose schema with driverId, deliveryId, coordinates (lat/lng), capturedAt, isOfflineSync flag, status (pending|processed|failed), and compound indexes for efficient per-driver chronological queries Service layer: - Add src/sockets/sync.service.ts: SyncService class with processBatch() * Validates each OfflineLocationPoint (capturedAt, lat/lng bounds, deliveryId ObjectId format) * Detects duplicates against DB (driverId + capturedAt) and within the same batch using a Set * Bulk-inserts valid unique points via insertMany(ordered:false) * Returns full LocationSyncAck with per-item SyncItemResult breakdown * Enforces configurable SYNC_BATCH_SIZE_LIMIT (default 500) Controller layer: - Add src/sockets/syncHandler.ts: registerSyncHandler() wires the location_sync socket event, guards unauthenticated sockets, delegates to SyncService, emits location_sync_ack (success or error fallback) - Update src/sockets/connectionHandler.ts: call registerSyncHandler inside io.on('connection') Types: - Extend src/sockets/socket.types.ts with OfflineLocationPoint, LocationSyncPayload, SyncItemResult, LocationSyncAck; add location_sync to ClientToServerEvents and location_sync_ack to ServerToClientEvents Tests: - Add tests/sync.service.test.ts: 23 unit tests using MongoMemoryServer covering persistence, deduplication (DB + within-batch), validation (boundary values, invalid fields), guard rails, and ack shape - Update jest.config.js: set testTimeout=30000 for MongoMemoryServer Closes SwiftChainn#27
…wiftChainn#28) - Install @stellar/stellar-sdk@13.1.0 Config layer (src/config/stellar.ts): * resolveStellarConfig() validates env vars at startup (fast-fail on bad config); resolves SOROBAN_RPC_URL, STELLAR_NETWORK_PASSPHRASE, STELLAR_NETWORK, SOROBAN_RPC_TIMEOUT_MS with sensible defaults * createSorobanRpcClient() factory for rpc.Server instances * Pre-built sorobanRpcClient singleton for production code paths * Supports mainnet | testnet | futurenet Service layer (src/blockchain/soroban.service.ts): * SorobanService.checkConnectivity(): calls getHealth() and getLatestLedger() in parallel; returns typed ConnectivityCheckResult on success or ConnectivityCheckError on failure (never throws) * SorobanService.getLatestLedger(): returns ledger sequence number * SorobanService.getNetworkInfo(): returns raw getNetwork() response * Client injected via constructor for testability Controller layer (src/controllers/stellar.controller.ts): * StellarController.checkHealth(): 200 on healthy, 503 on unhealthy * StellarController.getNetworkInfo(): 200 with passphrase + protocol * StellarController.getLatestLedger(): 200 with sequence number Routes (src/routes/stellar.routes.ts, src/routes/index.ts): * GET /api/v1/stellar/health * GET /api/v1/stellar/network * GET /api/v1/stellar/ledger/latest Tests (tests/soroban.service.test.ts): 11 unit tests * Healthy node: correct fields, parallel calls, latency, ISO dates * Unhealthy node: ECONNREFUSED, timeout, non-Error throws, rpcUrl present * getLatestLedger: success and error propagation * getNetworkInfo: success and error propagation .env.example: added SOROBAN_RPC_URL, STELLAR_NETWORK_PASSPHRASE, STELLAR_NETWORK, SOROBAN_RPC_TIMEOUT_MS with documentation comments Closes SwiftChainn#28
…on broadcasting (SwiftChainn#25) Types (src/sockets/socket.types.ts): * Add DriverLocationUpdatePayload: deliveryId, lat, lng, capturedAt * Add LocationBroadcastPayload: deliveryId, driverId, lat, lng, capturedAt, receivedAt (ISO) * Add LocationUpdateAck: success flag, locationId, optional error * Add driver_location_update to ClientToServerEvents * Add location:update and location_update_ack to ServerToClientEvents Service layer (src/sockets/location.service.ts): * LocationService.processLiveUpdate(): - Validates payload (driverId, deliveryId ObjectId, lat/lng bounds, capturedAt epoch) - Persists to MongoDB via LocationUpdate model (isOfflineSync=false, status=pending) - Broadcasts location:update to delivery:<deliveryId> room via io.to() - Returns typed LocationUpdateAck (never throws) * deliveryRoom() helper for canonical room name construction * DELIVERY_ROOM_PREFIX constant exported for reuse Controller layer (src/sockets/locationHandler.ts): * registerLocationHandler(io, socket): - Auth guard: rejects unauthenticated drivers with ack error - Payload guard: rejects malformed objects - Delegates to LocationService, emits location_update_ack back to driver - Adds delivery-specific validation/logging to join_room for delivery: prefixed rooms Integration (src/sockets/connectionHandler.ts): * Wire registerLocationHandler(io, socket) inside io.on('connection') Tests (tests/location.service.test.ts): 20 unit tests using MongoMemoryServer * deliveryRoom helper: prefix format, uniqueness * processLiveUpdate success: ack shape, DB persistence (isOfflineSync, coordinates, deliveryId), broadcast to correct room, receivedAt ISO, fallback capturedAt, single emit * processLiveUpdate validation: invalid driverId, empty/invalid deliveryId, lat/lng OOB, NaN, capturedAt=0, boundary values, no-broadcast on failure, no-persist on failure Closes SwiftChainn#25
|
@gregemax Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR resolves four issues in a single cohesive branch that builds each feature on top of the previous:
Closes #26 — WebSocket connection health checks and disconnect cleanup
Branch origin:
feat/socket-health-checksFiles added/modified:
src/sockets/socket.types.ts— Strongly-typed interfaces:SocketConnectionMeta,PingPayload,PongPayload,HealthCheckResult, typed server/client event mapssrc/sockets/socket.service.ts—SocketServiceclass: connection registration, periodic ping/pong health-check loop (SOCKET_PING_INTERVAL_MS,SOCKET_MAX_MISSED_PONGS), stale-connection eviction, room join/leave tracking, full Winston loggingsrc/sockets/connectionHandler.ts—initializeSocketServer()wires typed Socket.IO to the HTTP server; registers all per-socket event handlers;shutdownSocketServer()for graceful teardownsrc/server.ts— Wraps Express inhttp.Server, attaches Socket.IO, includes Socket.IO in graceful shutdown sequencetests/socket.service.test.ts— 22 unit tests covering all service methods including health-check tick evictionKey behaviours:
pingevery 25 s (configurable); missing pongs increment a counter; afterMAX_MISSED_PONGSconsecutive misses the socket is force-disconnectedhandleDisconnectremoves the connection record, logs duration, rooms, and reasonCloses #27 — Offline data sync/catch-up for drivers reconnecting to WebSockets
Branch origin:
feat/socket-offline-syncFiles added/modified:
src/models/LocationUpdate.ts— Mongoose schema withdriverId,deliveryId,coordinates (lat/lng),capturedAt,isOfflineSyncflag,status(pending|processed|failed), compound indexessrc/sockets/socket.types.ts— Extended withOfflineLocationPoint,LocationSyncPayload,SyncItemResult,LocationSyncAck;location_syncclient event,location_sync_ackserver eventsrc/sockets/sync.service.ts—SyncService.processBatch(): validates each point, deduplicates against DB + within batch, bulk-inserts viainsertMany(ordered:false), returns full per-item ack;SYNC_BATCH_SIZE_LIMITenv guardsrc/sockets/syncHandler.ts—registerSyncHandler(): auth guard, payload guard, delegates to service, emitslocation_sync_ackwith error fallbacksrc/sockets/connectionHandler.ts— WiredregisterSyncHandlerintoconnectionhandlertests/sync.service.test.ts— 23 unit tests (MongoMemoryServer): persistence, DB dedup, within-batch dedup, validation boundaries, guard rails, ack shapeCloses #28 — Install Stellar SDK and configure Soroban RPC client
Branch origin:
feat/soroban-rpc-setupFiles added/modified:
package.json— Added@stellar/stellar-sdk@13.1.0.env.example— AddedSOROBAN_RPC_URL,STELLAR_NETWORK_PASSPHRASE,STELLAR_NETWORK,SOROBAN_RPC_TIMEOUT_MSwith documentation commentssrc/config/stellar.ts—resolveStellarConfig()validates env at startup (fast-fail);createSorobanRpcClient()factory;sorobanRpcClientsingleton; supports mainnet/testnet/futurenetsrc/blockchain/soroban.service.ts—SorobanService.checkConnectivity(): callsgetHealth()+getLatestLedger()in parallel, returns typed result or error (never throws);getLatestLedger(),getNetworkInfo()src/controllers/stellar.controller.ts—checkHealth(200/503),getNetworkInfo(200),getLatestLedger(200)src/routes/stellar.routes.ts+src/routes/index.ts— MountsGET /api/v1/stellar/health,GET /api/v1/stellar/network,GET /api/v1/stellar/ledger/latesttests/soroban.service.test.ts— 11 unit tests (fully offline, RPC client mocked via constructor injection)Closes #25 — WebSocket events for real-time driver location broadcasting
Branch origin:
feat/socket-location-broadcastFiles added/modified:
src/sockets/socket.types.ts— AddedDriverLocationUpdatePayload,LocationBroadcastPayload,LocationUpdateAck;driver_location_updateclient event;location:update+location_update_ackserver eventssrc/sockets/location.service.ts—LocationService.processLiveUpdate(): validates payload, persists to MongoDB (isOfflineSync=false), broadcastslocation:updatetodelivery:<id>room, returns typed ack (never throws);deliveryRoom()helper;DELIVERY_ROOM_PREFIXconstantsrc/sockets/locationHandler.ts—registerLocationHandler(io, socket): auth guard, payload guard, delegates to service, emitslocation_update_ackback to driversrc/sockets/connectionHandler.ts— WiredregisterLocationHandler(io, socket)intoconnectionhandlertests/location.service.test.ts— 20 unit tests (MongoMemoryServer): ack shape, DB persistence, room broadcast correctness,receivedAtISO, fallbackcapturedAt, single-emit, validation boundaries, no-persist/no-broadcast on failureArchitecture
All features follow the mandated Controller → Service → Model layered pattern:
LocationUpdate— all data retrieved from/persisted to MongoDBTesting
socket.service.test.tssync.service.test.tssoroban.service.test.tslocation.service.test.tshealth.test.tsAll 76 tests pass. TypeScript build is clean (
pnpm buildexits 0).Environment Variables Added