Skip to content

Mobile App Services

The mobile app uses four singleton services for API communication, authentication, secure storage, and network detection.

ApiService

File: lib/services/api_service.dart

Singleton Dio HTTP client for all API communication.

Configuration

Setting Value
Base URL API_URL environment define (default: http://localhost:3000)
Connect timeout 15 seconds
Receive timeout 15 seconds

Interceptors

Request interceptor:

  • Automatically injects Authorization: Bearer <token> header on every request
  • Token retrieved from StorageService

Response interceptor:

  • Catches 401 Unauthorized responses
  • Calls onUnauthenticated callback to trigger logout
  • Clears stored token and navigates to login screen

Debug logging:

  • In development mode, logs full request and response details
  • Disabled in production builds

Usage

final dio = ApiService.instance.dio;

// GET request
final response = await dio.get('/sessions/upcoming-cleans');

// POST with JSON body
await dio.post('/sessions/${id}/accept');

// POST with multipart form data (photo upload)
final formData = FormData.fromMap({
  'photo': await MultipartFile.fromFile(file.path),
  'type': 'before',
});
await dio.post('/photos/$roomCleanId', data: formData);

AuthService

File: lib/services/auth_service.dart

Manages JWT authentication, session persistence, and user state. Extends ChangeNotifier for reactive UI updates.

Properties

Property Type Description
user AuthUser? Current logged-in user
isAuthenticated bool Whether user has a valid session
isLoading bool Whether auth check is in progress
error String? Last authentication error message

Methods

login(String email, String password)

  1. Calls POST /api/auth/sign-in/email with credentials
  2. Extracts JWT token and user object from response
  3. Stores token via StorageService
  4. Sets user and notifies listeners
  5. Throws on failure (caught by login screen for error display)

logout()

  1. Calls POST /api/auth/sign-out
  2. Clears stored token via StorageService
  3. Sets user = null and notifies listeners

_checkSession() (internal)

Called automatically in the constructor on cold start:

  1. Retrieves stored token from StorageService
  2. If token exists, calls GET /api/auth/get-session
  3. On success: populates user state
  4. On failure: clears token, shows login screen

Provider Setup

ChangeNotifierProvider(create: (_) => AuthService())

Accessed via Provider.of<AuthService>(context) or context.watch<AuthService>().


StorageService

File: lib/services/storage_service.dart

Platform-aware secure token storage singleton.

Platform Behavior

Platform Storage Backend Security
iOS Keychain (via flutter_secure_storage) Hardware-backed encryption
Android Keystore (via flutter_secure_storage) Hardware-backed encryption
Web SharedPreferences Browser local storage

Methods

Method Description
saveAccessToken(String token) Store JWT token securely
getAccessToken() Retrieve stored JWT token (returns null if not set)
clearAccessToken() Remove stored token (used on logout)

Platform Detection

The service detects the runtime platform using kIsWeb and selects the appropriate storage backend automatically. No configuration needed.


NetworkService

File: lib/services/network_service.dart

Detects whether the device is on the property's local WiFi or accessing remotely. Extends ChangeNotifier for reactive UI updates.

Purpose

OpenSTR can restrict active cleaning sessions to on-site cleaners only. This service determines if the cleaner is on the local network by calling a server endpoint that checks the client's IP against configured local ranges.

Properties

Property Type Description
isLocal bool Whether device is on local WiFi
devOverride bool Developer override (bypass WiFi check)

Methods

checkNetwork()

  1. Calls GET /network-check
  2. Server returns { is_local: 1 } if client IP is in local range
  3. Caches result for 2 minutes to avoid excessive server calls
  4. Updates isLocal and notifies listeners

setDevOverride(bool value)

Developer toggle available in the Profile screen's dev settings. When enabled, isLocal always returns true regardless of actual network.

How It Works

The nginx reverse proxy uses a geo module that tags requests with an X-Is-Local header based on IP ranges. The API's /network-check endpoint reads this header and returns the result.

Provider Setup

ChangeNotifierProvider(create: (_) => NetworkService())

Network status is re-checked when the user switches tabs in the main shell.