Skip to content
Open
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
2 changes: 1 addition & 1 deletion .fvmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"flutter": "3.35.7"
"flutter": "3.41.0"
}
135 changes: 135 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
name: Build & Release

on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
build-android:
name: Build Android APK
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.0'
channel: stable
cache: true

- name: Install dependencies
run: flutter pub get

- name: Decode keystore
if: env.KEYSTORE_BASE64 != ''
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEY_PROPERTIES: ${{ secrets.KEY_PROPERTIES }}
run: |
echo "$KEYSTORE_BASE64" | base64 -d > android/app/upload-keystore.jks
echo "$KEY_PROPERTIES" > android/key.properties

- name: Build APK
run: flutter build apk --release

- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: android-apk
path: build/app/outputs/flutter-apk/app-release.apk

build-windows:
name: Build Windows Installer
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.0'
channel: stable
cache: true

- name: Install dependencies
run: flutter pub get

- name: Build Windows
run: flutter build windows --release

- name: Create MSIX installer
run: |
dart pub global activate msix
dart pub global run msix:create --build-windows false
continue-on-error: true

- name: Upload Windows build
uses: actions/upload-artifact@v4
with:
name: windows-installer
path: |
build/windows/x64/runner/Release/
build/windows/x64/runner/Release/*.msix

build-macos:
name: Build macOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.0'
channel: stable
cache: true

- name: Install dependencies
run: flutter pub get

- name: Build macOS
run: flutter build macos --release

- name: Create DMG
run: |
APP_PATH="build/macos/Build/Products/Release/hushnet_frontend.app"
DMG_PATH="build/macos/HushNet.dmg"
hdiutil create -volname "HushNet" -srcfolder "$APP_PATH" -ov -format UDZO "$DMG_PATH"

- name: Upload macOS DMG
uses: actions/upload-artifact@v4
with:
name: macos-dmg
path: build/macos/HushNet.dmg

release:
name: Create GitHub Release
needs: [build-android, build-windows, build-macos]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts

- name: Prepare release assets
run: |
mv artifacts/android-apk/app-release.apk artifacts/HushNet-android.apk
mv artifacts/macos-dmg/HushNet.dmg artifacts/HushNet-macos.dmg
cd artifacts/windows-installer && zip -r ../HushNet-windows.zip . && cd ../..

- name: Create Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
artifacts/HushNet-android.apk
artifacts/HushNet-macos.dmg
artifacts/HushNet-windows.zip
8 changes: 6 additions & 2 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ jobs:
# https://github.com/dart-lang/setup-dart/blob/main/README.md
# - uses: dart-lang/setup-dart@v1
- uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603

- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.0'
channel: stable
cache: true
- name: Install dependencies
run: dart pub get
run: flutter pub get

# Uncomment this step to verify the use of 'dart format' on each commit.
# - name: Verify formatting
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"dart.flutterSdkPath": ".fvm\\versions\\3.35.7",
"dart.flutterSdkPath": ".fvm/versions/3.41.0",
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",
"C_Cpp_Runner.debuggerPath": "gdb",
Expand Down
20 changes: 20 additions & 0 deletions lib/data/node/node_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:developer';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:hushnet_frontend/models/node.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> connectToNode(
Expand Down Expand Up @@ -39,3 +40,22 @@ Future<void> connectToNode(
await prefs.setString('node_address', nodeAddress);
}
}

Future<List<Node>> fetchNodes() async {
final dio = Dio();
try {
final response = await dio.get('https://registry.hushnet.net/api/nodes');
if (response.statusCode == 200) {
final List<dynamic> nodesJson = response.data['nodes'] ?? [];
final List<Node> nodes = nodesJson
.map((nodeJson) => Node.fromJson(nodeJson))
.toList();
return nodes;
} else {
throw Exception('Failed to load nodes');
}
} catch (e) {
log('Error fetching nodes: $e');
throw Exception('Failed to load nodes');
}
}
56 changes: 56 additions & 0 deletions lib/models/node.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'dart:convert';

class Node {
final String apiBaseUrl;
final String countryCode;
final String countryName;
final Map<String, dynamic> features;
final String host;
final String ip;
final int? lastLatencyMs;
final String lastSeenAt;
final String name;
final String protocolVersion;
final String status;

Node({
required this.apiBaseUrl,
required this.countryCode,
required this.countryName,
required this.features,
required this.host,
required this.ip,
required this.lastLatencyMs,
required this.lastSeenAt,
required this.name,
required this.protocolVersion,
required this.status,
});

factory Node.fromJson(Map<String, dynamic> json) {
return Node(
apiBaseUrl: json['api_base_url']?.toString() ?? '',
countryCode: json['country_code']?.toString() ?? '',
countryName: json['country_name']?.toString() ?? '',
features: Map<String, dynamic>.from(json['features'] ?? {}),
host: json['host']?.toString() ?? '',
ip: json['ip']?.toString() ?? '',
lastLatencyMs: json['last_latency_ms'] is int
? json['last_latency_ms'] as int
: int.tryParse(json['last_latency_ms']?.toString() ?? ''),
lastSeenAt: json['last_seen_at']?.toString() ?? '',
name: json['name']?.toString() ?? '',
protocolVersion: json['protocol_version']?.toString() ?? '',
status: json['status']?.toString() ?? '',
);
}
}

List<Node> parseNodes(String responseBody) {
final decoded = jsonDecode(responseBody) as Map<String, dynamic>;
final nodesJson = decoded['nodes'] as List<dynamic>? ?? [];

return nodesJson
.map((e) => Node.fromJson(e as Map<String, dynamic>))
.toList();
}
Loading
Loading