• English
  • Operations

    Build configuration

    Environment URLs (API_BASE_URL, BOOKING_BASE_URL) are injected at build time via --dart-define-from-file:

    Config fileEnvironment
    config/dev.jsonDevelopment (gitignored — copy from config/dev.example.json)
    config/uat.jsonUser acceptance testing
    config/prod.jsonProduction

    config/dev.example.json:

    {
      "API_BASE_URL": "https://<your-lan-ip>:8080",
      "BOOKING_BASE_URL": "https://booking.example.com"
    }

    Build scripts

    # Linux/macOS
    bash scripts/build_apk.sh          # dev
    bash scripts/build_apk.sh uat      # UAT
    bash scripts/build_apk.sh prod     # production
    
    # Windows
    powershell scripts/build_apk.ps1

    The scripts handle flutter clean, flutter pub get, build_runner, and flutter build apk with the correct --dart-define-from-file.

    Cloud sync pipeline

    Upload flow

    Mutation (DB write)
      → Drift trigger → sync_outbox row
      → CloudSyncCollector reads outbox, coalesces by (table, pk)
      → CloudSyncClient.postBatch() → POST /v1/sync/batch
      → CloudSyncWorker.runOnce() (50 batches × 200 rows per tick)
      → CloudSyncScheduler (30-second timer)

    Key files:

    • lib/core/sync/cloud_sync_worker.dart — main worker loop
    • lib/core/sync/cloud_sync_collector.dart — outbox reading and coalescing
    • lib/core/sync/cloud_sync_client.dart — Dio HTTP wrapper
    • lib/core/sync/cloud_sync_scheduler.dart — 30-second periodic trigger
    • lib/core/sync/cloud_sync_backoff.dart — exponential backoff with jitter

    Restore flow

    • lib/core/sync/cloud_restore_applier.dart — upserts rows, resolves conflicts
    • lib/core/sync/restore_gate.dart — gates restore to protect live data
    • Paginated, cursor-based: GET /v1/restore/rows

    Booking sync

    A separate outbox for online booking requests:

    • lib/core/sync/booking/booking_outbox_worker.dart
    • lib/core/sync/booking/booking_intent_applier.dart
    • lib/core/sync/booking/booking_quarantine_store.dart

    Account management

    • lib/core/sync/accounts_client.dart — cloud account API
    • lib/core/sync/accounts_session.dart — session management
    • lib/core/sync/accounts_models.dart — DTOs

    Device roster

    • lib/core/sync/roster_service.dart — periodic cloud pull of device roster
    • lib/core/sync/roster_store.dart — local roster cache
    • lib/core/sync/secondary_roster_refresher.dart — Secondary-specific roster updates

    Heartbeat

    • lib/core/sync/heartbeat_scheduler.dart — Primary telemetry to cloud

    Config sync

    • lib/core/sync/config/ — cloud feature flags, booking slug, config client/worker/store

    Credential store

    • lib/core/sync/secure_credential_store.dart — encrypted cloud credentials

    Observability

    lib/core/observability/ (~19 files):

    LayerKey files
    Structured loggingobs_logger.dart — configurable log levels
    Event collectionobs_event_sink.dart — batched event collection with backoff
    Metricsobs_metrics.dart — numeric metrics
    Cloud uploadobs_client.dart — POST /v1/logs/events, POST /v1/logs/bundle
    Upload pipelineerror_event_uploader.dart, pending_event_queue.dart, bundle_uploader.dart
    HTTP interceptorobs_interceptor.dart — logs request/response metadata
    Navigationobs_nav_observer.dart — GoRouter observer
    Remote commandscommand_executor.dart — cloud-requested diagnostic commands
    Storagering_buffer_store.dart (in-memory), pending_event_queue.dart (persistent)
    PII protectionobs_redaction.dart — PII/secret scrubbing
    Dedupdedup_window.dart — prevents duplicate events

    Design principles: batched, idempotent, backoff-aware, never blocking.

    Push notifications

    lib/core/push/ (Primary only):

    • push_service.dart — thin FirebaseMessaging wrapper; foreground/in-app only, no background isolate
    • booking_push_controller.dart — routes incoming FCM data messages to app logic
    • push_register_client.dart — registers the FCM token with the cloud API

    Every Firebase call is guarded so failures never throw. Starts/stops subscriptions on role transitions.

    Cloud sync mock server

    tool/cloud_sync_mock_server.dart (3.7 KB) — a local mock for testing the sync pipeline without a real cloud backend.

    Device-to-device handover

    When a Primary goes down, Secondaries can promote themselves:

    1. primary_locator.dart — subnet scan to find remaining devices
    2. candidate_verifier.dart — verifies a candidate is legitimate
    3. Settings UI provides manual promotion controls

    Backup & restore

    Built into the Settings feature. Backs up the full Drift database and can restore it. The cloud restore pipeline handles cross-device restore.

    Android foreground service

    flutter_foreground_task keeps the Primary's Shelf server alive in the background. Critical for the multi-device architecture — if the Primary's server stops, all Secondaries lose connectivity.

    Localization

    lib/l10n/:

    • app_en.arb (134 KB) — English translations
    • app_zh.arb (98 KB) — Chinese translations
    • Generated via l10n.yaml configuration

    Platform target

    • Android only — the app is designed for landscape tablets (1280×800)
    • No iOS or web support in the current configuration