Skip to content

feat: p2p wifi hotspot sync - server apis and webapp integration - #10893

Open
paulpascal wants to merge 15 commits into
medic:masterfrom
paulpascal:feat/p2p-sync
Open

feat: p2p wifi hotspot sync - server apis and webapp integration#10893
paulpascal wants to merge 15 commits into
medic:masterfrom
paulpascal:feat/p2p-sync

Conversation

@paulpascal

@paulpascal paulpascal commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds P2P WiFi Hotspot sync capability to CHT, enabling CHWs in offline areas to push health data to a Supervisor's phone over a local WiFi connection. The Supervisor relays data to the msain server on next online sync.

This PR covers the server-side APIs and webapp integration. The related Android PR is at medic/cht-android#431.

What's included

Server API endpoints (api/src/p2p/):

  • POST /api/v1/p2p/authorize : Issues JWT token with ECDSA P-256 for offline device authentication
  • GET /api/v1/p2p/config/:facility_id : Returns P2P configuration for a facility
  • GET /api/v1/p2p/revocation-list : Returns revoked devices/users
  • POST /api/v1/p2p/telemetry : Receives session telemetry (fire-and-forget)

Webapp P2P module (webapp/src/ts/modules/p2p/):

  • Status page with host/peer controls, QR code display, sync progress
  • History page showing past sync sessions
  • Config service reads p2p_sync from app_settings
  • Transit filter service hides relayed docs from UI (contacts, reports, search, tasks, targets)
  • Transit purge service soft-deletes relayed docs after server delivery confirmed

Key design decisions:

  • Roles are fully config-driven via host_roles and peer_roles in app_settings : no hardcoded role names
  • Transit docs use soft-delete (_deleted: true + purged: true) with replication filter, not db.remove() which would create destructive tombstones
  • Documents written with new_edits: false for automatic CouchDB deduplication
  • Supports both new sidebar nav and old header nav
  • 86 built-in translation keys

Eg. of configuration:

{
  "p2p_sync": {
    "enabled": true,
    "host_roles": ["supervisor"],
    "peer_roles": ["chw"]
  }
}

Files changed

  • 6 new API endpoint files + routing
  • 6 new Angular components/services
  • 3 new P2P services (config, transit filter, transit purge)
  • 86 translation keys
  • Append-only modifications to: routing.js, sidebar-menu, header, contacts, reports, search, rules-engine, target-aggregates, db-sync, old-nav.less
  • 5 test spec files

Test plan

  • P2P menu visible for users with configured host/peer roles
  • P2P menu works in both old nav (dropdown) and new nav (sidebar)
  • Authorize endpoint returns JWT for users with matching roles
  • Config endpoint returns p2p_sync settings
  • Transit docs filtered from contacts, reports, search, tasks, targets
  • Purge triggers after successful server sync
  • Unit tests pass: npx mocha api/tests/p2p/**/*.spec.js

Code review checklist

  • UI/UX backwards compatible: Test it works for the new design (enabled by default). And test it works in the old design, enable can_view_old_navigation permission to see the old design. Test it has appropriate design for RTL languages.
  • Readable: Concise, well named, follows the style guide
  • Documented: Configuration and user documentation on cht-docs
  • Tested: Unit and/or e2e where appropriate
  • Internationalised: All user facing text
  • Backwards compatible: Works with existing data and configuration or includes a migration. Any breaking changes documented in the release notes.
  • AI disclosure: Please disclose use of AI per the guidelines.

License

The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.

dianabarsan and others added 9 commits April 2, 2026 20:06
…ization service (medic#10799)

Signed-off-by: Diana Barsan <barsan@medic.org>
…dic#10811)

(cherry picked from commit 6a5867b)

medic#10802

Co-authored-by: 1yuv <yrimal@outlook.com>
Wave 1 server APIs: POST /api/v1/p2p/authorize (JWT issuance),
GET /api/v1/p2p/config/:facility_id, POST /api/v1/p2p/telemetry,
GET /api/v1/p2p/revocation-list, scope-manifest generator.
Route registration in api/src/routing.js. Full test coverage.
Wave 3 webapp: Angular P2P module with status and history components.
P2P config service, transit filter service (excludes transit docs from
UI), transit purge service (uses db.purge, never db.remove). P2P styles.
…ations)

Wave 3 integration: inject transit filter into contacts, search,
rules-engine, and target-aggregates services. Pause/resume replication
during P2P sync in db-sync service. Add P2P bridge methods to
android-api service. P2P sidebar menu entry and route. Translation
strings for all P2P UI elements.
The authorize endpoint was checking a non-existent allowed_roles field,
falling back to hardcoded ['chw', 'chw_supervisor']. This caused 403 for
any deployment using different role names. Now reads host_roles and
peer_roles from p2p_sync config. Default fallbacks changed to empty
arrays — if roles are not configured, P2P simply does not activate.
- Add P2P menu item to old nav header dropdown (header.component)
- Add &.p2p to old-nav.less page list for proper padding
- Add wrapper div in P2P templates to match About page card layout
- Show sync history only on peer (CHW) side
Update test expectations after removing hardcoded role defaults.
Tests now use host_roles/peer_roles instead of allowed_roles.
- Add curly braces to single-line if statements
- Rename unused catch vars to _e/_err pattern
- Break long lines (max 120 chars)
- Use array.includes() for isSyncing check
- Use node:crypto imports (S7772)
- Reduce cognitive complexity by extracting helpers (S3776)
- Use optional chaining (S6582)
- Use Set for role lookups (S7776)
- Flip negated ternary conditions (S7735)
- Name arrow function export (S7726)
- Mark injected services as readonly (S2933)
- Add logging in catch blocks (S2486)
- Replace display-only <label> with <span> for accessibility (S6853)
- Extract type aliases and constants (S4323, S1192)
- Reduce cognitive complexity in transit-purge, transit-filter,
  p2p-status, and db-sync by extracting helpers (S3776)
- Mark services as readonly (S2933)
- Flip negated conditions in p2p-config (S7735)
- Use optional chaining and direct undefined comparison (S6582, S7741)
- Replace Promise.resolve() with direct return (S7746)
- Remove 'any' from union types (S6571)
- Add logging in empty catch blocks (S2486)
@paulpascal paulpascal changed the title feat: P2P WiFi Hotspot sync — server APIs and webapp integration feat: p2p wifi hotspot sync - server apis and webapp integration Apr 18, 2026
Add mock providers for P2pConfigService, P2pTransitFilterService, and
P2pTransitPurgeService to test files that instantiate services where
P2P dependencies were injected. Mocks return safe defaults (P2P
disabled, no transit docs) so existing tests pass unchanged.
@paulpascal
paulpascal force-pushed the feat/p2p-sync branch 6 times, most recently from e8705d7 to 37261a8 Compare April 18, 2026 22:34
- Extract isP2pPausingSync helper from shouldSkipSync (S3776)
- Extract purgeOneDoc helper from purgeDocBatch (S3776)
- Return fresh object literals in purgeConfirmedTransitDocs (S3516)
- Extract handleQrScanResult from callback (S3776)
- Extract executePostP2pSyncAttempt and retryPostP2pSyncIfPossible (S3776)
@mrjones-plip

Copy link
Copy Markdown
Contributor

wow!! what an epic PR @paulpascal - impressive!!

I've added back in a Code review checklist and License section above which should be on every PR per our template. Can you please edit the checklise to be accurate for this PR?

Thanks!

@paulpascal

Copy link
Copy Markdown
Contributor Author

Thanks @mrjones-plip , well noted, will promptly updated the checklist accordingly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🔍 In Review
Status: 🔍 In Review

Development

Successfully merging this pull request may close these issues.

4 participants