feat(useragent): added connection info setup screen

This commit is contained in:
hdbg
2026-03-15 14:51:39 +01:00
parent 16d5b9a233
commit ec0e8a980c
25 changed files with 800 additions and 2441 deletions

View File

@@ -0,0 +1,56 @@
import 'dart:convert';
class ArbiterUrl {
const ArbiterUrl({
required this.host,
required this.port,
required this.caCert,
this.bootstrapToken,
});
final String host;
final int port;
final List<int> caCert;
final String? bootstrapToken;
static const _scheme = 'arbiter';
static const _certQueryKey = 'cert';
static const _bootstrapTokenQueryKey = 'bootstrap_token';
static ArbiterUrl parse(String value) {
final uri = Uri.tryParse(value);
if (uri == null || uri.scheme != _scheme) {
throw const FormatException("Invalid URL scheme, expected 'arbiter://'");
}
if (uri.host.isEmpty) {
throw const FormatException('Missing host in URL');
}
if (!uri.hasPort) {
throw const FormatException('Missing port in URL');
}
final cert = uri.queryParameters[_certQueryKey];
if (cert == null || cert.isEmpty) {
throw const FormatException("Missing 'cert' query parameter in URL");
}
final decodedCert = _decodeCert(cert);
return ArbiterUrl(
host: uri.host,
port: uri.port,
caCert: decodedCert,
bootstrapToken: uri.queryParameters[_bootstrapTokenQueryKey],
);
}
static List<int> _decodeCert(String cert) {
try {
return base64Url.decode(base64Url.normalize(cert));
} on FormatException catch (error) {
throw FormatException("Invalid base64 in 'cert' query parameter: ${error.message}");
}
}
}

View File

@@ -0,0 +1,3 @@
class Connection {}

View File

@@ -1,6 +1,3 @@
import 'package:flutter/services.dart';
enum KeyAlgorithm {
rsa, ecdsa, ed25519
}
@@ -16,4 +13,4 @@ abstract class KeyHandle {
abstract class KeyManager {
Future<KeyHandle?> get();
Future<KeyHandle> create();
}
}

View File

@@ -0,0 +1,67 @@
import 'dart:convert';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class StoredServerInfo {
const StoredServerInfo({
required this.address,
required this.port,
required this.caCertFingerprint,
});
final String address;
final int port;
final String caCertFingerprint;
Map<String, dynamic> toJson() => {
'address': address,
'port': port,
'caCertFingerprint': caCertFingerprint,
};
factory StoredServerInfo.fromJson(Map<String, dynamic> json) {
return StoredServerInfo(
address: json['address'] as String,
port: json['port'] as int,
caCertFingerprint: json['caCertFingerprint'] as String,
);
}
}
abstract class ServerInfoStorage {
Future<StoredServerInfo?> load();
Future<void> save(StoredServerInfo serverInfo);
Future<void> clear();
}
class SecureServerInfoStorage implements ServerInfoStorage {
static const _storageKey = 'server_info';
const SecureServerInfoStorage();
static const _storage = FlutterSecureStorage();
@override
Future<StoredServerInfo?> load() async {
final rawValue = await _storage.read(key: _storageKey);
if (rawValue == null) {
return null;
}
final decoded = jsonDecode(rawValue) as Map<String, dynamic>;
return StoredServerInfo.fromJson(decoded);
}
@override
Future<void> save(StoredServerInfo serverInfo) {
return _storage.write(
key: _storageKey,
value: jsonEncode(serverInfo.toJson()),
);
}
@override
Future<void> clear() {
return _storage.delete(key: _storageKey);
}
}