Skip to content

CuriousDev21/ZTClient-Flutter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ZTClient Flutter - VPN Control Application

This Flutter application provides a user-friendly interface for controlling a VPN daemon through socket communication, displaying the current connection status, and handling VPN actions such as connect and disconnect. The app polls the daemon status every 5 seconds and manages the lifetime of the authentication token to ensure smooth VPN operations.

Table of Contents

Features

  • Daemon Communication: Interacts with a mock VPN daemon via UNIX sockets.
  • Polling: Continuously polls the daemon status every 5 seconds.
  • Connection Control: Provides buttons for connecting and disconnecting the VPN.
  • Error Handling: Displays user-friendly error messages when issues occur during communication with the VPN daemon or when network issues are encountered.
  • Debouncing: Ensures that rapid successive VPN actions (connect/disconnect) are debounced to avoid unnecessary socket connections or failures.
  • Token Management: Manages the lifetime of the authentication token (valid for 5 minutes).

Requirements

  • macOS/Linux: Required to use UNIX sockets for communication with the daemon.
  • Flutter: Version 3.10.0 or above
  • Dart: Version 3.0 or above
  • Xcode: Required for building and running the app on iOS.
  • Android Studio: Required for building and running the app on Android*.

Recommended IDE

The recommended IDE for working on this project is Visual Studio Code with dart and flutter plugins installed.

Architecture

This project follows a Layered Architecture approach that enforces separation of concerns, making the code more modular, scalable, and testable.

The project is divided into the following layers:

1. Presentation Layer

Handles the UI and user interaction. It contains the VpnActionNotifier, which orchestrates the flow of data from repositories and services to the UI, ensuring that the VPN status and actions (connect/disconnect) are properly handled.

2. Application Layer

Manages the state and provides access to repositories and services using Riverpod providers. This layer includes the RepositoryProvider and ServiceProvider, which offer a clean and modular way to access services like SocketService, AuthService, and repositories like ConnectionRepository and TokenRepository.

3. Domain Layer

Contains the core business logic and models. This layer includes business-critical elements such as DaemonStatusState and AuthToken, and handles domain-related logic, including error handling with custom exceptions (e.g., DataSourceException).

4. Data Layer

Responsible for interacting with external services such as the VPN daemon and secure token storage. This layer contains the actual implementation of repositories like ConnectionRepository (for VPN control) and TokenRepository (for managing authentication tokens).

The following diagram illustrates the flow of data and interactions between these layers: Architecture Diagram

Folder Structure

lib/
 ├── application/
 │    ├── providers/
 │    │    ├── repository.abs.dart
 │    │    └── service.abs.dart
 │    └── services/
 │         ├── api_config.dart
 │         ├── auth_service.dart
 │         ├── network_client.dart
 │         ├── secure_storage_service.dart
 │         └── socket_service.dart
 ├── core/
 │    ├── utils/
 │    │    ├── constants/
 │    │    │    ├── dimensions.dart
 │    │    │    └── display_breakpoint.dart
 │    │    ├── logger/
 │    │    │    └── app_logger.dart
 │    │    └── theme/
 │    │         └── app_style.dart
 │    └── widgets/
 │         └── responsive_container.dart
 ├── data/
 │    ├── repositories/
 │    │    ├── daemon_connection_repository.dart
 │    │    └── token_repository.dart
 ├── domain/
 │    ├── errors/
 │    │    ├── data_source_exception.dart
 │    │    └── network_exception.dart
 │    └── models/
 │         ├── auth/
 │         ├── daemon/
 │         └── daemon_status.dart
 ├── presentation/
 │    ├── providers/
 │    │    ├── vpn_action_notifier.dart
 │    │    └── vpn_action_notifier.freezed.dart
 │    └── screens/
 │         └── daemon_status_screen.dart
 └── main.dart

Key Folders:

  • application: Contains providers that manage the state and business logic for the app.
  • core: Contains utility classes such as logging.
  • data: Handles external service interactions, including the connection to the daemon and token management.
  • domain: Contains models, errors, and business logic.
  • presentation: Contains the UI, with a primary screen for the VPN control interface.

Libraries & Packages

The following packages are used in the project:

Package Purpose
flutter_riverpod State management for handling daemon status and actions.
freezed_annotation Data class immutability and union types for error handling.
flutter_secure_storage Storing sensitive data securely (e.g., tokens).
dio HTTP client for API requests.
build_runner Code generation for freezed and riverpod annotations.
json_serializable Automatically generating JSON serialization boilerplate.
logging Structured logging throughout the app.
mocktail Unit testing with mocks.

Setup and Installation

Prerequisites

  • Ensure your system is set up correctly by running:

    flutter doctor

    This command will check if your Flutter and Dart versions are installed and correctly configured, and it will provide information about any missing dependencies. Ensure all checks pass.

Installation Steps

  1. Clone the repository:

    git clone https://github.com/CuriousDev21/ZTClient-Flutter.git
    cd cloudflare_zt_flutter
  2. Install dependencies:

    flutter pub get
  3. Generate necessary files (e.g., for freezed and riverpod):

    flutter pub run build_runner build --delete-conflicting-outputs

Running the App

Before running the app, it's a good idea to check the connected devices or simulators available using the following command:

flutter devices

This will list all connected devices and simulators. For example, if you're targeting iOS, you will get a list of available iOS simulators, and you can choose one to run the app. Use the specific device ID shown in the flutter devices output.

Run Command

To run the app on a specific device or simulator, use the following command (replace device_id with the actual device or simulator ID you got from flutter devices):

flutter run -d device_id

However, if you're targeting macOS, you can run the following command directly:

flutter run -d macos

Alternative: Use Visual Studio Code

Alternatively, you can use the Build button in Visual Studio Code, which automatically detects and builds the app for the selected device or simulator. This is an easier approach if you are working in the Visual Studio Code environment.

Running the Daemon-Lite

Before running the application, you must start the mock daemon-lite on your system. This provides the VPN daemon service the app interacts with.

  1. Navigate to the daemon-lite directory (where the daemon binary is stored):

    cd daemon-lite
  2. Start the daemon-lite with default options:

    ./daemon-lite

    This will start the daemon listening on a UNIX socket at /tmp/daemon-lite.

  3. Optionally, you can start the daemon with additional parameters such as:

    • Failure rate (-f): How often the requests should fail (once every X calls). For example:

      ./daemon-lite -f 3
    • Connection timeout (-c): The maximum amount of time the connect request can take to establish a connection (in milliseconds):

      ./daemon-lite -c 5000
    • Disconnect timeout (-d): The maximum amount of time the disconnect request can take to disconnect (in milliseconds):

      ./daemon-lite -d 3000

    You can see a list of all available parameters using:

    ./daemon-lite --help

Testing

Unit tests are provided using the mocktail package for mocking service and repository layers.

Run all tests:

flutter test

Key Testing Areas:

  • Daemon Interaction: Ensures that the app can effectively communicate with the VPN daemon and handle edge cases.
  • Error Handling: Ensures that errors from the daemon are handled appropriately and shown to the user.
  • Token Management: Ensures the auth token is fetched, cached, and refreshed as needed.

macOS Packaging

The application has already been packaged as a DMG (Disk Image) file, which is included in the releases/ folder of this repository.

Steps to Install the DMG:

  1. Navigate to the releases/ folder in this repository's Flutter project folder cloudflare_zt_flutter/.
  2. Find the file VPNControl-Flutter.dmg.
  3. Double-click to open the DMG file.
  4. Open it directly or Drag and drop the app into your Applications folder.

Important: macOS Security Warning

Since the application is not signed with an Apple Developer ID, macOS will block the app from opening initially. To bypass this, follow these steps:

  1. After downloading and moving the application to your Applications folder, try opening the app by double-clicking. You will see a warning stating that the app "can't be opened because Apple cannot check it for malicious software."

  2. Open System Preferences and navigate to Security & Privacy.

  3. In the General tab, you will see a message that the app was blocked. Click Open Anyway.

  4. You will now be able to launch the app.

Alternatively, you

can build the app locally using Flutter:

Build the macOS App Locally

  1. Clone the repository:

    git clone https://github.com/CuriousDev21/ZTClient-Flutter.git
    cd ZTClient-Flutter
  2. Run the macOS build command:

    flutter build macos
  3. Navigate to the generated build folder:

    open build/macos/Build/Products/Release/
    
  4. Run the app directly from the Release folder.

Limitations & Future Work

Limitations:

  • The app runs perfectly on macOS and iOS. However, on Android, the /tmp path is reserved and cannot be used for socket communication. Altering the daemon to use another directory would have been required to make it work on Android emulators.
  • Packaging the app as .pkg for macOS requires additional notarization, which wasn't possible without a Developer ID Installer Certificate.

Future Work:

  • Support for Android: In a real-world scenario, modifying the daemon to use a different socket path (instead of /tmp) would be essential for supporting Android.
  • Daemon Code Refactor: Potential improvements to the daemon itself could allow it to be more portable across different operating systems.
  • Notifications on Status Changes: Adding OS-level notifications for VPN status changes would enhance user experience.
  • Improve Error Handling: More advanced error handling mechanisms, such as retries with exponential backoff, could be introduced for robustness.
  • Integration Testing: Using tools like Patrol and Patrol_CLI to perform integration tests on the app's UI and interactions with a daemon instance.
  • Localization Support: Adding support for multiple languages would make the app more accessible to a wider audience.