Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 287 additions & 0 deletions doc/audit/platform-conditional-exports-audit.md

Large diffs are not rendered by default.

259 changes: 259 additions & 0 deletions doc/audit/service-decoupling-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# Service Decoupling Audit Report

**Generated**: 2026-01-09
**Project**: PrivacyGUI
**Purpose**: Document JNAP coupling status for future USP/TR migration

---

## Executive Summary

| Metric | Value |
|--------|-------|
| Total Service Files | 53 |
| Services with JNAP Dependency | 34 (64%) |
| RouterRepository References | 85 |
| Domain Models (JNAP) | 54 |
| Unique JNAP Actions Used | 110+ |
| **Architecture Violations** | **32** |

**Current Status**: 🔴 **High Coupling** — Most services directly depend on JNAP-specific types.

---

## ⚠️ Architecture Compliance Violations

The following sections document violations of the **Provider → Service → Repository** architecture pattern.

### RouterRepository Usage Outside Services (8 files)

These files directly access `routerRepositoryProvider` instead of going through a Service:

| File | Layer | Severity |
|------|-------|----------|
| `lib/page/advanced_settings/local_network_settings/views/local_network_settings_view.dart` | View | 🔴 High |
| `lib/page/dashboard/views/prepare_dashboard_view.dart` | View | 🔴 High |
| `lib/page/ai_assistant/views/router_assistant_view.dart` | View | 🔴 High |
| `lib/page/instant_setup/troubleshooter/views/pnp_no_internet_connection_view.dart` | View | 🔴 High |
| `lib/page/select_network/providers/select_network_provider.dart` | Provider | 🟡 Medium |
| `lib/page/vpn/providers/vpn_notifier.dart` | Provider | 🟡 Medium |
| `lib/page/wifi_settings/providers/channelfinder_provider.dart` | Provider | 🟡 Medium |
| `lib/page/instant_setup/troubleshooter/providers/_providers.dart` | Provider | 🟡 Medium |

### JNAPAction Usage Outside Services (3 files)

These files directly reference `JNAPAction` enum:

| File | Violation | Code Example |
|------|-----------|--------------|
| `select_network_provider.dart` | Direct JNAP call | `JNAPAction.isAdminPasswordDefault` |
| `prepare_dashboard_view.dart` | Direct JNAP call | `JNAPAction.getDeviceInfo` |
| `vpn_service.dart` | In service (acceptable) | - |

### JNAP Models Imported in Views/Providers (24 files)

Files that import `jnap/models/*` or `jnap/result/*` outside the Service layer:

**Views (14 files)**:
| File | Models Used |
|------|-------------|
| `dmz_settings_view.dart` | DMZ models |
| `internet_settings_view.dart` | WAN settings |
| `local_network_settings_view.dart` | LAN settings |
| `dashboard_home_view.dart` | Device info |
| `prepare_dashboard_view.dart` | Device info |
| `firmware_update_process_view.dart` | Firmware status |
| `instant_admin_view.dart` | Time settings |
| `node_detail_view.dart` | Node models |
| `instant_topology_view.dart` | Topology models |
| `instant_verify_view.dart` | Verify models |
| `login_local_view.dart` | Auth models |
| `pnp_*_view.dart` | ISP settings |

**Providers (10 files)**:
| File | Models Used |
|------|-------------|
| `node_light_settings_provider.dart` | LED settings |
| `channelfinder_provider.dart` | Radio info |
| `wifi_bundle_provider.dart` | WiFi settings |
| `select_network_provider.dart` | Network models |
| `wan_external_provider.dart` | WAN status |
| Others... | Various |

### Compliance Summary

| Violation Type | Count | Impact |
|----------------|-------|--------|
| **RouterRepository in Views** | 4 | 🔴 High - Direct protocol dependency |
| **RouterRepository in Providers** | 4 | 🟡 Medium - Should use Services |
| **JNAPAction in non-Services** | 2 | 🔴 High - Protocol leakage |
| **JNAP Models in Views** | 14 | 🟡 Medium - Model coupling |
| **JNAP Models in Providers** | 10 | 🟡 Medium - Model coupling |
| **Total Violations** | **34** | - |

### Recommended Fixes

1. **Views should NOT directly use RouterRepository**
- Create/use appropriate Services for these operations
- Pass data through Providers

2. **Providers should use Services, not RouterRepository**
- `VpnNotifier` should use `VpnService`
- `ChannelFinderProvider` should use `ChannelFinderService`

3. **Consider Domain Models separate from JNAP Models**
- Create UI-specific models in `lib/page/**/models/`
- Transform JNAP models to domain models in Services

---

## Service Inventory

### Core Services (`lib/core/data/services/`)

| Service | JNAP Coupled | Primary Functions |
|---------|--------------|-------------------|
| `polling_service.dart` | 🔴 Yes | Core data polling, transaction building |
| `dashboard_manager_service.dart` | 🔴 Yes | Dashboard state, device info |
| `device_manager_service.dart` | 🔴 Yes | Device CRUD, backhaul info |
| `firmware_update_service.dart` | 🔴 Yes | Firmware check/update |

### Feature Services (`lib/page/**/services/`)

| Category | Services | JNAP Coupled |
|----------|----------|--------------|
| **WiFi Settings** | `wifi_settings_service.dart`, `channel_finder_service.dart` | 🔴 Yes |
| **Network Settings** | `local_network_settings_service.dart`, `internet_settings_service.dart` | 🔴 Yes |
| **Security** | `firewall_settings_service.dart`, `dmz_settings_service.dart` | 🔴 Yes |
| **Instant Features** | `instant_privacy_service.dart`, `instant_safety_service.dart`, `instant_verify_service.dart`, `instant_topology_service.dart` | 🔴 Yes |
| **Administration** | `administration_settings_service.dart`, `router_password_service.dart`, `timezone_service.dart`, `power_table_service.dart` | 🔴 Yes |
| **Advanced Settings** | `static_routing_service.dart`, `ddns_service.dart`, port services | 🔴 Yes |
| **Nodes** | `node_detail_service.dart`, `add_nodes_service.dart`, `add_wired_nodes_service.dart`, `node_light_settings_service.dart` | 🔴 Yes |
| **Health Check** | `health_check_service.dart` | 🔴 Yes |
| **Setup** | `pnp_service.dart`, `pnp_isp_service.dart`, `auto_parent_first_login_service.dart` | 🔴 Yes |

### Non-JNAP Services (Cloud/Auth)

| Service | Purpose |
|---------|---------|
| `auth_service.dart` | Authentication (uses JNAP for local login) |
| `connectivity_service.dart` | Network connectivity check |
| Cloud services (`lib/core/cloud/`) | Linksys Cloud API (separate protocol) |

---

## JNAP Action Usage (Top 20)

| Action | Usage Count | Used By Services |
|--------|-------------|------------------|
| `getGuestRadioSettings` | 10 | wifi_settings, polling |
| `getLANSettings` | 9 | local_network_settings, internet_settings |
| `getWANStatus` | 8 | polling, dashboard, instant_verify |
| `getRadioInfo` | 8 | wifi_settings, polling, dashboard |
| `getDevices` | 8 | device_manager, polling |
| `getDeviceInfo` | 8 | dashboard, polling, side_effect |
| `getFirmwareUpdateSettings` | 6 | firmware_update, polling |
| `setFirmwareUpdateSettings` | 4 | firmware_update |
| `reboot` | 4 | administration, pnp |
| `getMACFilterSettings` | 4 | wifi_settings |
| `getInternetConnectionStatus` | 4 | polling, pnp |
| `getBackhaulInfo` | 4 | device_manager, polling |
| `factoryReset` | 4 | administration |

---

## Service Contracts Summary

### Core Read Operations

| Domain | Operation | JNAP Action | USP Equivalent (TBD) |
|--------|-----------|-------------|----------------------|
| Device | Get device info | `getDeviceInfo` | `Device.DeviceInfo.` |
| Device | Get device list | `getDevices` | `Device.Hosts.Host.` |
| Network | Get WAN status | `getWANStatus` | `Device.IP.Interface.` |
| WiFi | Get radio info | `getRadioInfo` | `Device.WiFi.Radio.` |
| WiFi | Get guest settings | `getGuestRadioSettings` | `Device.WiFi.SSID.` |
| System | Get system stats | `getSystemStats` | TBD |

### Core Write Operations

| Domain | Operation | JNAP Action | Side Effects |
|--------|-----------|-------------|--------------|
| Device | Set device name | `setDeviceProperties` | None |
| WiFi | Set radio settings | `setRadioSettings` | WiFi restart |
| System | Reboot | `reboot` | Device restart |
| System | Factory reset | `factoryReset` | Device restart |
| Firmware | Start update | `updateFirmwareNow` | Device restart |

---

## Migration Readiness

### Ready for Migration (After Protocol Defined)
- Services with clean separation between JNAP calls and business logic
- Services using `RouterRepository` through dependency injection

### Requires Refactoring
- Services with inline JNAP action handling
- Services with complex transaction building logic

### Special Considerations
- **Polling**: Batch transaction pattern may differ in USP
- **Side Effects**: Device restart handling needs protocol-agnostic abstraction
- **Real-time Updates**: USP supports WebSocket subscriptions

---

## Recommendations

### Short Term (Now)
1. ✅ Document all service contracts (this report)
2. Keep new services clean with single responsibility
3. Avoid spreading JNAP dependencies to Providers

### Medium Term (When USP Spec Available)
1. Review USP data model mapping
2. Identify common vs protocol-specific operations
3. Design protocol adapter interface based on actual needs

### Long Term (Migration)
1. Implement `UspAdapter` alongside `JnapAdapter`
2. Migrate services one by one with feature flags
3. Maintain parallel support during transition period

---

## Appendix: Service File Locations

### Core Services
```
lib/core/data/services/
├── polling_service.dart
├── dashboard_manager_service.dart
├── device_manager_service.dart
└── firmware_update_service.dart
```

### Feature Services
```
lib/page/
├── advanced_settings/
│ ├── administration/services/
│ ├── apps_and_gaming/ddns/services/
│ ├── apps_and_gaming/ports/services/
│ ├── dmz/services/
│ ├── firewall/services/
│ ├── internet_settings/services/
│ ├── local_network_settings/services/
│ └── static_routing/services/
├── health_check/services/
├── instant_admin/services/
├── instant_privacy/services/
├── instant_safety/services/
├── instant_setup/services/
├── instant_topology/services/
├── instant_verify/services/
├── login/auto_parent/services/
├── nodes/services/
└── wifi_settings/services/
```
17 changes: 17 additions & 0 deletions lib/constants/client_type/client_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// Client Type - Cross-platform client type constants.
///
/// This library provides platform-aware client type identification.
/// Consumers only need to import this file:
///
/// ```dart
/// import 'package:privacy_gui/constants/client_type/client_type.dart';
///
/// final type = clientType; // automatically returns correct platform value
/// ```
///
/// The correct platform implementation is automatically selected at compile time.
library;

export 'get_client_type.dart'
if (dart.library.io) 'mobile_client_type.dart'
if (dart.library.html) 'web_client_type.dart';
4 changes: 1 addition & 3 deletions lib/constants/cloud_const.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'build_config.dart';

import 'client_type/get_client_type.dart'
if (dart.library.io) 'client_type/mobile_client_type.dart'
if (dart.library.html) 'client_type/web_client_type.dart';
import 'client_type/client_type.dart';

const kCloudBase = 'CLOUD_BASE_URL';
const kCloudJNAP = 'CLOUD_JNAP_BASE_URL';
Expand Down
4 changes: 1 addition & 3 deletions lib/constants/url_links.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import 'dart:ui';

import 'package:privacy_gui/core/utils/logger.dart';
import 'package:privacy_gui/util/url_helper/url_helper.dart'
if (dart.library.io) 'package:privacy_gui/util/url_helper/url_helper_mobile.dart'
if (dart.library.html) 'package:privacy_gui/util/url_helper/url_helper_web.dart';
import 'package:privacy_gui/util/url_helper/url_helper.dart';

const officialWebHost = 'https://store.linksys.com';
const officialSupportHost = 'https://support.linksys.com';
Expand Down
4 changes: 1 addition & 3 deletions lib/core/cloud/linksys_cloud_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ import 'package:privacy_gui/core/cloud/model/cloud_communication_method.dart';
import 'package:privacy_gui/core/cloud/model/cloud_network_model.dart';
import 'package:privacy_gui/core/cloud/model/cloud_session_model.dart';
import 'package:privacy_gui/providers/connectivity/_connectivity.dart';
import 'package:privacy_gui/core/utils/ip_getter/get_local_ip.dart'
if (dart.library.io) 'package:privacy_gui/core/utils/ip_getter/mobile_get_local_ip.dart'
if (dart.library.html) 'package:privacy_gui/core/utils/ip_getter/web_get_local_ip.dart';
import 'package:privacy_gui/core/utils/ip_getter/ip_getter.dart';

final cloudRepositoryProvider = Provider((ref) => LinksysCloudRepository(
httpClient: LinksysHttpClient(getHost: () {
Expand Down
4 changes: 1 addition & 3 deletions lib/core/cloud/linksys_device_cloud_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import 'package:privacy_gui/core/utils/devices.dart';
import 'package:privacy_gui/core/utils/logger.dart';
import 'package:privacy_gui/providers/connectivity/connectivity_info.dart';
import 'package:privacy_gui/providers/connectivity/connectivity_provider.dart';
import 'package:privacy_gui/core/utils/ip_getter/get_local_ip.dart'
if (dart.library.io) 'package:privacy_gui/core/utils/ip_getter/mobile_get_local_ip.dart'
if (dart.library.html) 'package:privacy_gui/core/utils/ip_getter/web_get_local_ip.dart';
import 'package:privacy_gui/core/utils/ip_getter/ip_getter.dart';

final deviceCloudServiceProvider = Provider((ref) => DeviceCloudService(
httpClient: LinksysHttpClient(getHost: () {
Expand Down
4 changes: 1 addition & 3 deletions lib/core/jnap/router_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ import 'package:privacy_gui/core/data/providers/side_effect_provider.dart';
import 'package:privacy_gui/core/utils/logger.dart';
import 'package:privacy_gui/utils.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:privacy_gui/core/utils/ip_getter/get_local_ip.dart'
if (dart.library.io) 'package:privacy_gui/core/utils/ip_getter/mobile_get_local_ip.dart'
if (dart.library.html) 'package:privacy_gui/core/utils/ip_getter/web_get_local_ip.dart';
import 'package:privacy_gui/core/utils/ip_getter/ip_getter.dart';

enum CommandType {
remote,
Expand Down
19 changes: 19 additions & 0 deletions lib/core/utils/assign_ip/assign_ip.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// Assign IP - Cross-platform web location manipulation.
///
/// This library provides platform-aware web location functions.
/// Consumers only need to import this file:
///
/// ```dart
/// import 'package:privacy_gui/core/utils/assign_ip/assign_ip.dart';
///
/// assignWebLocation('https://new-host.local');
/// ```
///
/// The correct platform implementation is automatically selected at compile time.
/// - Web: Uses `window.location` APIs
/// - Native: No-op (these operations are web-specific)
library;

export 'base_assign_ip.dart'
if (dart.library.io) 'mobile_assign_ip.dart'
if (dart.library.html) 'web_assign_ip.dart';
14 changes: 14 additions & 0 deletions lib/core/utils/assign_ip/mobile_assign_ip.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// Mobile implementation of assign_ip functions.
/// These are no-op on native platforms as they are web-specific operations.

void assignWebLocation(String url) {
// No-op on native - this is a web-specific operation
}

void updateWebHost(String host) {
// No-op on native - this is a web-specific operation
}

void reload() {
// No-op on native - this is a web-specific operation
}
17 changes: 17 additions & 0 deletions lib/core/utils/ip_getter/ip_getter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// IP Getter - Cross-platform local IP address retrieval.
///
/// This library provides platform-aware IP address retrieval functions.
/// Consumers only need to import this file:
///
/// ```dart
/// import 'package:privacy_gui/core/utils/ip_getter/ip_getter.dart';
///
/// final ip = getLocalIp(ref);
/// ```
///
/// The correct platform implementation is automatically selected at compile time.
library;

export 'get_local_ip.dart'
if (dart.library.io) 'mobile_get_local_ip.dart'
if (dart.library.html) 'web_get_local_ip.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import 'package:privacy_gui/page/components/views/arguments_view.dart';
import 'package:privacy_gui/providers/redirection/redirection_provider.dart';
import 'package:privacy_gui/util/error_code_helper.dart';
import 'package:ui_kit_library/ui_kit.dart';
import 'package:privacy_gui/core/utils/assign_ip/base_assign_ip.dart'
if (dart.library.html) 'package:privacy_gui/core/utils/assign_ip/web_assign_ip.dart';
import 'package:privacy_gui/core/utils/assign_ip/assign_ip.dart';

enum InternetSettingsViewType {
ipv4,
Expand Down
Loading