feat(useragent): initial connection impl
This commit is contained in:
@@ -16,5 +16,5 @@ sources = ['protobufs/*.proto']
|
||||
outputs = ['useragent/lib/proto/*']
|
||||
run = '''
|
||||
dart pub global activate protoc_plugin && \
|
||||
protoc --dart_out=useragent/lib/proto --proto_path=protobufs/ protobufs/*.proto
|
||||
protoc --dart_out=grpc:useragent/lib/proto --proto_path=protobufs/ protobufs/*.proto
|
||||
'''
|
||||
@@ -1,3 +1,132 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:arbiter/features/connection/server_info_storage.dart';
|
||||
import 'package:arbiter/features/identity/pk_manager.dart';
|
||||
import 'package:arbiter/proto/arbiter.pbgrpc.dart';
|
||||
import 'package:arbiter/proto/user_agent.pb.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:mtcore/markettakers.dart';
|
||||
|
||||
class Connection {
|
||||
final ClientChannel channel;
|
||||
final StreamController<UserAgentRequest> _tx;
|
||||
final StreamIterator<UserAgentResponse> _rx;
|
||||
|
||||
Connection({
|
||||
required this.channel,
|
||||
required StreamController<UserAgentRequest> tx,
|
||||
required ResponseStream<UserAgentResponse> rx,
|
||||
}) : _tx = tx,
|
||||
_rx = StreamIterator(rx);
|
||||
|
||||
Future<void> send(UserAgentRequest request) async {
|
||||
_tx.add(request);
|
||||
}
|
||||
|
||||
Future<UserAgentResponse> receive() async {
|
||||
await _rx.moveNext();
|
||||
return _rx.current;
|
||||
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
await _tx.close();
|
||||
await channel.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
Future<Connection> _connect(StoredServerInfo serverInfo) async {
|
||||
final channel = ClientChannel(
|
||||
serverInfo.address,
|
||||
port: serverInfo.port,
|
||||
options: ChannelOptions(
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
credentials: ChannelCredentials.secure(
|
||||
onBadCertificate: (cert, host) {
|
||||
return true;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final client = ArbiterServiceClient(channel);
|
||||
final tx = StreamController<UserAgentRequest>();
|
||||
|
||||
final rx = client.userAgent(tx.stream);
|
||||
|
||||
return Connection(channel: channel, tx: tx, rx: rx);
|
||||
}
|
||||
|
||||
List<int> formatChallenge(AuthChallenge challenge, List<int> pubkey) {
|
||||
final encodedPubkey = base64Encode(pubkey);
|
||||
final payload = "${challenge.nonce}:$encodedPubkey";
|
||||
return utf8.encode(payload);
|
||||
}
|
||||
|
||||
Future<Connection> connectAndAuthorize(
|
||||
StoredServerInfo serverInfo,
|
||||
KeyHandle key, {
|
||||
String? bootstrapToken,
|
||||
}) async {
|
||||
try {
|
||||
final connection = await _connect(serverInfo);
|
||||
talker.info(
|
||||
'Connected to server at ${serverInfo.address}:${serverInfo.port}',
|
||||
);
|
||||
final pubkey = await key.getPublicKey();
|
||||
|
||||
final req = AuthChallengeRequest(
|
||||
pubkey: pubkey,
|
||||
bootstrapToken: bootstrapToken,
|
||||
keyType: switch (key.alg) {
|
||||
KeyAlgorithm.rsa => KeyType.KEY_TYPE_RSA,
|
||||
KeyAlgorithm.ecdsa => KeyType.KEY_TYPE_ECDSA_SECP256K1,
|
||||
KeyAlgorithm.ed25519 => KeyType.KEY_TYPE_ED25519,
|
||||
},
|
||||
);
|
||||
await connection.send(UserAgentRequest(authChallengeRequest: req));
|
||||
talker.info(
|
||||
"Sent auth challenge request with pubkey ${base64Encode(pubkey)}",
|
||||
);
|
||||
|
||||
|
||||
class Connection {}
|
||||
final response = await connection.receive();
|
||||
|
||||
talker.info(
|
||||
'Received response from server, checking for auth challenge...',
|
||||
);
|
||||
|
||||
if (!response.hasAuthChallenge()) {
|
||||
throw Exception(
|
||||
'Expected AuthChallengeResponse, got ${response.whichPayload()}',
|
||||
);
|
||||
}
|
||||
|
||||
final challenge = formatChallenge(response.authChallenge, pubkey);
|
||||
talker.info(
|
||||
'Received auth challenge, signing with key ${base64Encode(pubkey)}',
|
||||
);
|
||||
|
||||
final signature = await key.sign(challenge);
|
||||
|
||||
final solutionReq = AuthChallengeSolution(signature: signature);
|
||||
await connection.send(UserAgentRequest(authChallengeSolution: solutionReq));
|
||||
|
||||
talker.info('Sent auth challenge solution, waiting for server response...');
|
||||
|
||||
final solutionResponse = await connection.receive();
|
||||
|
||||
if (!solutionResponse.hasAuthOk()) {
|
||||
throw Exception(
|
||||
'Expected AuthChallengeSolutionResponse, got ${solutionResponse.whichPayload()}',
|
||||
);
|
||||
}
|
||||
|
||||
talker.info('Authentication successful, connection established');
|
||||
|
||||
return connection;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to connect to server: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'server_info_storage.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class StoredServerInfo {
|
||||
const StoredServerInfo({
|
||||
required this.address,
|
||||
@@ -13,19 +17,9 @@ class StoredServerInfo {
|
||||
final int port;
|
||||
final String caCertFingerprint;
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'address': address,
|
||||
'port': port,
|
||||
'caCertFingerprint': caCertFingerprint,
|
||||
};
|
||||
factory StoredServerInfo.fromJson(Map<String, dynamic> json) => _$StoredServerInfoFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$StoredServerInfoToJson(this);
|
||||
|
||||
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 {
|
||||
21
useragent/lib/features/connection/server_info_storage.g.dart
Normal file
21
useragent/lib/features/connection/server_info_storage.g.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'server_info_storage.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
StoredServerInfo _$StoredServerInfoFromJson(Map<String, dynamic> json) =>
|
||||
StoredServerInfo(
|
||||
address: json['address'] as String,
|
||||
port: (json['port'] as num).toInt(),
|
||||
caCertFingerprint: json['caCertFingerprint'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$StoredServerInfoToJson(StoredServerInfo instance) =>
|
||||
<String, dynamic>{
|
||||
'address': instance.address,
|
||||
'port': instance.port,
|
||||
'caCertFingerprint': instance.caCertFingerprint,
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:arbiter/features/pk_manager.dart';
|
||||
import 'package:arbiter/features/identity/pk_manager.dart';
|
||||
|
||||
final storage = FlutterSecureStorage(
|
||||
aOptions: AndroidOptions.biometric(
|
||||
@@ -16,7 +16,6 @@ final storage = FlutterSecureStorage(
|
||||
synchronizable: false,
|
||||
accessControlFlags: [
|
||||
AccessControlFlag.userPresence,
|
||||
AccessControlFlag.privateKeyUsage,
|
||||
],
|
||||
usesDataProtectionKeychain: true,
|
||||
),
|
||||
@@ -10,14 +10,10 @@
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'client.pb.dart' as $0;
|
||||
import 'user_agent.pb.dart' as $1;
|
||||
|
||||
export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
|
||||
|
||||
class ServerInfo extends $pb.GeneratedMessage {
|
||||
@@ -86,21 +82,6 @@ class ServerInfo extends $pb.GeneratedMessage {
|
||||
void clearCertPublicKey() => $_clearField(2);
|
||||
}
|
||||
|
||||
class ArbiterServiceApi {
|
||||
final $pb.RpcClient _client;
|
||||
|
||||
ArbiterServiceApi(this._client);
|
||||
|
||||
$async.Future<$0.ClientResponse> client(
|
||||
$pb.ClientContext? ctx, $0.ClientRequest request) =>
|
||||
_client.invoke<$0.ClientResponse>(
|
||||
ctx, 'ArbiterService', 'Client', request, $0.ClientResponse());
|
||||
$async.Future<$1.UserAgentResponse> userAgent(
|
||||
$pb.ClientContext? ctx, $1.UserAgentRequest request) =>
|
||||
_client.invoke<$1.UserAgentResponse>(
|
||||
ctx, 'ArbiterService', 'UserAgent', request, $1.UserAgentResponse());
|
||||
}
|
||||
|
||||
const $core.bool _omitFieldNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const $core.bool _omitMessageNames =
|
||||
|
||||
90
useragent/lib/proto/arbiter.pbgrpc.dart
Normal file
90
useragent/lib/proto/arbiter.pbgrpc.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from arbiter.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'client.pb.dart' as $0;
|
||||
import 'user_agent.pb.dart' as $1;
|
||||
|
||||
export 'arbiter.pb.dart';
|
||||
|
||||
@$pb.GrpcServiceName('arbiter.ArbiterService')
|
||||
class ArbiterServiceClient extends $grpc.Client {
|
||||
/// The hostname for this service.
|
||||
static const $core.String defaultHost = '';
|
||||
|
||||
/// OAuth scopes needed for the client.
|
||||
static const $core.List<$core.String> oauthScopes = [
|
||||
'',
|
||||
];
|
||||
|
||||
ArbiterServiceClient(super.channel, {super.options, super.interceptors});
|
||||
|
||||
$grpc.ResponseStream<$0.ClientResponse> client(
|
||||
$async.Stream<$0.ClientRequest> request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createStreamingCall(_$client, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseStream<$1.UserAgentResponse> userAgent(
|
||||
$async.Stream<$1.UserAgentRequest> request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createStreamingCall(_$userAgent, request, options: options);
|
||||
}
|
||||
|
||||
// method descriptors
|
||||
|
||||
static final _$client =
|
||||
$grpc.ClientMethod<$0.ClientRequest, $0.ClientResponse>(
|
||||
'/arbiter.ArbiterService/Client',
|
||||
($0.ClientRequest value) => value.writeToBuffer(),
|
||||
$0.ClientResponse.fromBuffer);
|
||||
static final _$userAgent =
|
||||
$grpc.ClientMethod<$1.UserAgentRequest, $1.UserAgentResponse>(
|
||||
'/arbiter.ArbiterService/UserAgent',
|
||||
($1.UserAgentRequest value) => value.writeToBuffer(),
|
||||
$1.UserAgentResponse.fromBuffer);
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('arbiter.ArbiterService')
|
||||
abstract class ArbiterServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'arbiter.ArbiterService';
|
||||
|
||||
ArbiterServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.ClientRequest, $0.ClientResponse>(
|
||||
'Client',
|
||||
client,
|
||||
true,
|
||||
true,
|
||||
($core.List<$core.int> value) => $0.ClientRequest.fromBuffer(value),
|
||||
($0.ClientResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$1.UserAgentRequest, $1.UserAgentResponse>(
|
||||
'UserAgent',
|
||||
userAgent,
|
||||
true,
|
||||
true,
|
||||
($core.List<$core.int> value) => $1.UserAgentRequest.fromBuffer(value),
|
||||
($1.UserAgentResponse value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Stream<$0.ClientResponse> client(
|
||||
$grpc.ServiceCall call, $async.Stream<$0.ClientRequest> request);
|
||||
|
||||
$async.Stream<$1.UserAgentResponse> userAgent(
|
||||
$grpc.ServiceCall call, $async.Stream<$1.UserAgentRequest> request);
|
||||
}
|
||||
@@ -15,15 +15,6 @@ import 'dart:convert' as $convert;
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
|
||||
import 'package:protobuf/well_known_types/google/protobuf/empty.pbjson.dart'
|
||||
as $3;
|
||||
import 'package:protobuf/well_known_types/google/protobuf/timestamp.pbjson.dart'
|
||||
as $4;
|
||||
|
||||
import 'client.pbjson.dart' as $0;
|
||||
import 'evm.pbjson.dart' as $2;
|
||||
import 'user_agent.pbjson.dart' as $1;
|
||||
|
||||
@$core.Deprecated('Use serverInfoDescriptor instead')
|
||||
const ServerInfo$json = {
|
||||
'1': 'ServerInfo',
|
||||
@@ -37,88 +28,3 @@ const ServerInfo$json = {
|
||||
final $typed_data.Uint8List serverInfoDescriptor = $convert.base64Decode(
|
||||
'CgpTZXJ2ZXJJbmZvEhgKB3ZlcnNpb24YASABKAlSB3ZlcnNpb24SJgoPY2VydF9wdWJsaWNfa2'
|
||||
'V5GAIgASgMUg1jZXJ0UHVibGljS2V5');
|
||||
|
||||
const $core.Map<$core.String, $core.dynamic> ArbiterServiceBase$json = {
|
||||
'1': 'ArbiterService',
|
||||
'2': [
|
||||
{
|
||||
'1': 'Client',
|
||||
'2': '.arbiter.client.ClientRequest',
|
||||
'3': '.arbiter.client.ClientResponse',
|
||||
'5': true,
|
||||
'6': true
|
||||
},
|
||||
{
|
||||
'1': 'UserAgent',
|
||||
'2': '.arbiter.user_agent.UserAgentRequest',
|
||||
'3': '.arbiter.user_agent.UserAgentResponse',
|
||||
'5': true,
|
||||
'6': true
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use arbiterServiceDescriptor instead')
|
||||
const $core.Map<$core.String, $core.Map<$core.String, $core.dynamic>>
|
||||
ArbiterServiceBase$messageJson = {
|
||||
'.arbiter.client.ClientRequest': $0.ClientRequest$json,
|
||||
'.arbiter.client.AuthChallengeRequest': $0.AuthChallengeRequest$json,
|
||||
'.arbiter.client.AuthChallengeSolution': $0.AuthChallengeSolution$json,
|
||||
'.arbiter.client.ClientResponse': $0.ClientResponse$json,
|
||||
'.arbiter.client.AuthChallenge': $0.AuthChallenge$json,
|
||||
'.arbiter.client.AuthOk': $0.AuthOk$json,
|
||||
'.arbiter.evm.EvmSignTransactionResponse': $2.EvmSignTransactionResponse$json,
|
||||
'.arbiter.evm.TransactionEvalError': $2.TransactionEvalError$json,
|
||||
'.google.protobuf.Empty': $3.Empty$json,
|
||||
'.arbiter.evm.NoMatchingGrantError': $2.NoMatchingGrantError$json,
|
||||
'.arbiter.evm.SpecificMeaning': $2.SpecificMeaning$json,
|
||||
'.arbiter.evm.EtherTransferMeaning': $2.EtherTransferMeaning$json,
|
||||
'.arbiter.evm.TokenTransferMeaning': $2.TokenTransferMeaning$json,
|
||||
'.arbiter.evm.TokenInfo': $2.TokenInfo$json,
|
||||
'.arbiter.evm.PolicyViolationsError': $2.PolicyViolationsError$json,
|
||||
'.arbiter.evm.EvalViolation': $2.EvalViolation$json,
|
||||
'.arbiter.evm.GasLimitExceededViolation': $2.GasLimitExceededViolation$json,
|
||||
'.arbiter.evm.EvmAnalyzeTransactionResponse':
|
||||
$2.EvmAnalyzeTransactionResponse$json,
|
||||
'.arbiter.client.ClientConnectError': $0.ClientConnectError$json,
|
||||
'.arbiter.user_agent.UserAgentRequest': $1.UserAgentRequest$json,
|
||||
'.arbiter.user_agent.AuthChallengeRequest': $1.AuthChallengeRequest$json,
|
||||
'.arbiter.user_agent.AuthChallengeSolution': $1.AuthChallengeSolution$json,
|
||||
'.arbiter.user_agent.UnsealStart': $1.UnsealStart$json,
|
||||
'.arbiter.user_agent.UnsealEncryptedKey': $1.UnsealEncryptedKey$json,
|
||||
'.arbiter.evm.EvmGrantCreateRequest': $2.EvmGrantCreateRequest$json,
|
||||
'.arbiter.evm.SharedSettings': $2.SharedSettings$json,
|
||||
'.google.protobuf.Timestamp': $4.Timestamp$json,
|
||||
'.arbiter.evm.TransactionRateLimit': $2.TransactionRateLimit$json,
|
||||
'.arbiter.evm.SpecificGrant': $2.SpecificGrant$json,
|
||||
'.arbiter.evm.EtherTransferSettings': $2.EtherTransferSettings$json,
|
||||
'.arbiter.evm.VolumeRateLimit': $2.VolumeRateLimit$json,
|
||||
'.arbiter.evm.TokenTransferSettings': $2.TokenTransferSettings$json,
|
||||
'.arbiter.evm.EvmGrantDeleteRequest': $2.EvmGrantDeleteRequest$json,
|
||||
'.arbiter.evm.EvmGrantListRequest': $2.EvmGrantListRequest$json,
|
||||
'.arbiter.user_agent.ClientConnectionResponse':
|
||||
$1.ClientConnectionResponse$json,
|
||||
'.arbiter.user_agent.UserAgentResponse': $1.UserAgentResponse$json,
|
||||
'.arbiter.user_agent.AuthChallenge': $1.AuthChallenge$json,
|
||||
'.arbiter.user_agent.AuthOk': $1.AuthOk$json,
|
||||
'.arbiter.user_agent.UnsealStartResponse': $1.UnsealStartResponse$json,
|
||||
'.arbiter.evm.WalletCreateResponse': $2.WalletCreateResponse$json,
|
||||
'.arbiter.evm.WalletEntry': $2.WalletEntry$json,
|
||||
'.arbiter.evm.WalletListResponse': $2.WalletListResponse$json,
|
||||
'.arbiter.evm.WalletList': $2.WalletList$json,
|
||||
'.arbiter.evm.EvmGrantCreateResponse': $2.EvmGrantCreateResponse$json,
|
||||
'.arbiter.evm.EvmGrantDeleteResponse': $2.EvmGrantDeleteResponse$json,
|
||||
'.arbiter.evm.EvmGrantListResponse': $2.EvmGrantListResponse$json,
|
||||
'.arbiter.evm.EvmGrantList': $2.EvmGrantList$json,
|
||||
'.arbiter.evm.GrantEntry': $2.GrantEntry$json,
|
||||
'.arbiter.user_agent.ClientConnectionRequest':
|
||||
$1.ClientConnectionRequest$json,
|
||||
'.arbiter.user_agent.ClientConnectionCancel': $1.ClientConnectionCancel$json,
|
||||
};
|
||||
|
||||
/// Descriptor for `ArbiterService`. Decode as a `google.protobuf.ServiceDescriptorProto`.
|
||||
final $typed_data.Uint8List arbiterServiceDescriptor = $convert.base64Decode(
|
||||
'Cg5BcmJpdGVyU2VydmljZRJLCgZDbGllbnQSHS5hcmJpdGVyLmNsaWVudC5DbGllbnRSZXF1ZX'
|
||||
'N0Gh4uYXJiaXRlci5jbGllbnQuQ2xpZW50UmVzcG9uc2UoATABElwKCVVzZXJBZ2VudBIkLmFy'
|
||||
'Yml0ZXIudXNlcl9hZ2VudC5Vc2VyQWdlbnRSZXF1ZXN0GiUuYXJiaXRlci51c2VyX2FnZW50Ll'
|
||||
'VzZXJBZ2VudFJlc3BvbnNlKAEwAQ==');
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from arbiter.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'arbiter.pbjson.dart';
|
||||
import 'client.pb.dart' as $0;
|
||||
import 'user_agent.pb.dart' as $1;
|
||||
|
||||
export 'arbiter.pb.dart';
|
||||
|
||||
abstract class ArbiterServiceBase extends $pb.GeneratedService {
|
||||
$async.Future<$0.ClientResponse> client(
|
||||
$pb.ServerContext ctx, $0.ClientRequest request);
|
||||
$async.Future<$1.UserAgentResponse> userAgent(
|
||||
$pb.ServerContext ctx, $1.UserAgentRequest request);
|
||||
|
||||
$pb.GeneratedMessage createRequest($core.String methodName) {
|
||||
switch (methodName) {
|
||||
case 'Client':
|
||||
return $0.ClientRequest();
|
||||
case 'UserAgent':
|
||||
return $1.UserAgentRequest();
|
||||
default:
|
||||
throw $core.ArgumentError('Unknown method: $methodName');
|
||||
}
|
||||
}
|
||||
|
||||
$async.Future<$pb.GeneratedMessage> handleCall($pb.ServerContext ctx,
|
||||
$core.String methodName, $pb.GeneratedMessage request) {
|
||||
switch (methodName) {
|
||||
case 'Client':
|
||||
return client(ctx, request as $0.ClientRequest);
|
||||
case 'UserAgent':
|
||||
return userAgent(ctx, request as $1.UserAgentRequest);
|
||||
default:
|
||||
throw $core.ArgumentError('Unknown method: $methodName');
|
||||
}
|
||||
}
|
||||
|
||||
$core.Map<$core.String, $core.dynamic> get $json => ArbiterServiceBase$json;
|
||||
$core.Map<$core.String, $core.Map<$core.String, $core.dynamic>>
|
||||
get $messageJson => ArbiterServiceBase$messageJson;
|
||||
}
|
||||
@@ -26,10 +26,12 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
|
||||
factory AuthChallengeRequest({
|
||||
$core.List<$core.int>? pubkey,
|
||||
$core.String? bootstrapToken,
|
||||
KeyType? keyType,
|
||||
}) {
|
||||
final result = create();
|
||||
if (pubkey != null) result.pubkey = pubkey;
|
||||
if (bootstrapToken != null) result.bootstrapToken = bootstrapToken;
|
||||
if (keyType != null) result.keyType = keyType;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -50,6 +52,8 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
|
||||
..a<$core.List<$core.int>>(
|
||||
1, _omitFieldNames ? '' : 'pubkey', $pb.PbFieldType.OY)
|
||||
..aOS(2, _omitFieldNames ? '' : 'bootstrapToken')
|
||||
..aE<KeyType>(3, _omitFieldNames ? '' : 'keyType',
|
||||
enumValues: KeyType.values)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
@@ -88,6 +92,15 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
|
||||
$core.bool hasBootstrapToken() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearBootstrapToken() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
KeyType get keyType => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set keyType(KeyType value) => $_setField(3, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasKeyType() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearKeyType() => $_clearField(3);
|
||||
}
|
||||
|
||||
class AuthChallenge extends $pb.GeneratedMessage {
|
||||
|
||||
@@ -14,6 +14,31 @@ import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class KeyType extends $pb.ProtobufEnum {
|
||||
static const KeyType KEY_TYPE_UNSPECIFIED =
|
||||
KeyType._(0, _omitEnumNames ? '' : 'KEY_TYPE_UNSPECIFIED');
|
||||
static const KeyType KEY_TYPE_ED25519 =
|
||||
KeyType._(1, _omitEnumNames ? '' : 'KEY_TYPE_ED25519');
|
||||
static const KeyType KEY_TYPE_ECDSA_SECP256K1 =
|
||||
KeyType._(2, _omitEnumNames ? '' : 'KEY_TYPE_ECDSA_SECP256K1');
|
||||
static const KeyType KEY_TYPE_RSA =
|
||||
KeyType._(3, _omitEnumNames ? '' : 'KEY_TYPE_RSA');
|
||||
|
||||
static const $core.List<KeyType> values = <KeyType>[
|
||||
KEY_TYPE_UNSPECIFIED,
|
||||
KEY_TYPE_ED25519,
|
||||
KEY_TYPE_ECDSA_SECP256K1,
|
||||
KEY_TYPE_RSA,
|
||||
];
|
||||
|
||||
static final $core.List<KeyType?> _byValue =
|
||||
$pb.ProtobufEnum.$_initByValueList(values, 3);
|
||||
static KeyType? valueOf($core.int value) =>
|
||||
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||
|
||||
const KeyType._(super.value, super.name);
|
||||
}
|
||||
|
||||
class UnsealResult extends $pb.ProtobufEnum {
|
||||
static const UnsealResult UNSEAL_RESULT_UNSPECIFIED =
|
||||
UnsealResult._(0, _omitEnumNames ? '' : 'UNSEAL_RESULT_UNSPECIFIED');
|
||||
|
||||
@@ -15,6 +15,22 @@ import 'dart:convert' as $convert;
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
|
||||
@$core.Deprecated('Use keyTypeDescriptor instead')
|
||||
const KeyType$json = {
|
||||
'1': 'KeyType',
|
||||
'2': [
|
||||
{'1': 'KEY_TYPE_UNSPECIFIED', '2': 0},
|
||||
{'1': 'KEY_TYPE_ED25519', '2': 1},
|
||||
{'1': 'KEY_TYPE_ECDSA_SECP256K1', '2': 2},
|
||||
{'1': 'KEY_TYPE_RSA', '2': 3},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `KeyType`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List keyTypeDescriptor = $convert.base64Decode(
|
||||
'CgdLZXlUeXBlEhgKFEtFWV9UWVBFX1VOU1BFQ0lGSUVEEAASFAoQS0VZX1RZUEVfRUQyNTUxOR'
|
||||
'ABEhwKGEtFWV9UWVBFX0VDRFNBX1NFQ1AyNTZLMRACEhAKDEtFWV9UWVBFX1JTQRAD');
|
||||
|
||||
@$core.Deprecated('Use unsealResultDescriptor instead')
|
||||
const UnsealResult$json = {
|
||||
'1': 'UnsealResult',
|
||||
@@ -64,6 +80,14 @@ const AuthChallengeRequest$json = {
|
||||
'10': 'bootstrapToken',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'key_type',
|
||||
'3': 3,
|
||||
'4': 1,
|
||||
'5': 14,
|
||||
'6': '.arbiter.user_agent.KeyType',
|
||||
'10': 'keyType'
|
||||
},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_bootstrap_token'},
|
||||
@@ -73,8 +97,9 @@ const AuthChallengeRequest$json = {
|
||||
/// Descriptor for `AuthChallengeRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List authChallengeRequestDescriptor = $convert.base64Decode(
|
||||
'ChRBdXRoQ2hhbGxlbmdlUmVxdWVzdBIWCgZwdWJrZXkYASABKAxSBnB1YmtleRIsCg9ib290c3'
|
||||
'RyYXBfdG9rZW4YAiABKAlIAFIOYm9vdHN0cmFwVG9rZW6IAQFCEgoQX2Jvb3RzdHJhcF90b2tl'
|
||||
'bg==');
|
||||
'RyYXBfdG9rZW4YAiABKAlIAFIOYm9vdHN0cmFwVG9rZW6IAQESNgoIa2V5X3R5cGUYAyABKA4y'
|
||||
'Gy5hcmJpdGVyLnVzZXJfYWdlbnQuS2V5VHlwZVIHa2V5VHlwZUISChBfYm9vdHN0cmFwX3Rva2'
|
||||
'Vu');
|
||||
|
||||
@$core.Deprecated('Use authChallengeDescriptor instead')
|
||||
const AuthChallenge$json = {
|
||||
|
||||
22
useragent/lib/providers/connection/bootstrap_token.dart
Normal file
22
useragent/lib/providers/connection/bootstrap_token.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'bootstrap_token.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class BootstrapToken extends _$BootstrapToken {
|
||||
String? build() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void set(String token) {
|
||||
state = token;
|
||||
}
|
||||
|
||||
String? take() {
|
||||
final token = state;
|
||||
state = null;
|
||||
return token;
|
||||
}
|
||||
}
|
||||
62
useragent/lib/providers/connection/bootstrap_token.g.dart
Normal file
62
useragent/lib/providers/connection/bootstrap_token.g.dart
Normal file
@@ -0,0 +1,62 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'bootstrap_token.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(BootstrapToken)
|
||||
final bootstrapTokenProvider = BootstrapTokenProvider._();
|
||||
|
||||
final class BootstrapTokenProvider
|
||||
extends $NotifierProvider<BootstrapToken, String?> {
|
||||
BootstrapTokenProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'bootstrapTokenProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$bootstrapTokenHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
BootstrapToken create() => BootstrapToken();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(String? value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<String?>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$bootstrapTokenHash() => r'a59e679ab0561ed2ab4148660499891571d439db';
|
||||
|
||||
abstract class _$BootstrapToken extends $Notifier<String?> {
|
||||
String? build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref = this.ref as $Ref<String?, String?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<String?, String?>,
|
||||
String?,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
39
useragent/lib/providers/connection/connection_manager.dart
Normal file
39
useragent/lib/providers/connection/connection_manager.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
import 'package:arbiter/features/connection/connection.dart';
|
||||
import 'package:arbiter/providers/connection/bootstrap_token.dart';
|
||||
import 'package:arbiter/providers/key.dart';
|
||||
import 'package:arbiter/providers/server_info.dart';
|
||||
import 'package:mtcore/markettakers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'connection_manager.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class ConnectionManager extends _$ConnectionManager {
|
||||
@override
|
||||
Future<Connection?> build() async {
|
||||
final serverInfo = await ref.watch(serverInfoProvider.future);
|
||||
final key = await ref.watch(keyProvider.future);
|
||||
final token = ref.watch(bootstrapTokenProvider);
|
||||
|
||||
if (serverInfo == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
final Connection connection;
|
||||
try {
|
||||
connection = await connectAndAuthorize(serverInfo, key, bootstrapToken: token);
|
||||
} catch (e) {
|
||||
talker.handle(e);
|
||||
rethrow;
|
||||
}
|
||||
|
||||
|
||||
ref.onDispose(() {
|
||||
final connection = state.asData?.value;
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
});
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
54
useragent/lib/providers/connection/connection_manager.g.dart
Normal file
54
useragent/lib/providers/connection/connection_manager.g.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'connection_manager.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(ConnectionManager)
|
||||
final connectionManagerProvider = ConnectionManagerProvider._();
|
||||
|
||||
final class ConnectionManagerProvider
|
||||
extends $AsyncNotifierProvider<ConnectionManager, Connection?> {
|
||||
ConnectionManagerProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'connectionManagerProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$connectionManagerHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ConnectionManager create() => ConnectionManager();
|
||||
}
|
||||
|
||||
String _$connectionManagerHash() => r'8923346dff75a9a06127c71a0a39ca65d9733d8c';
|
||||
|
||||
abstract class _$ConnectionManager extends $AsyncNotifier<Connection?> {
|
||||
FutureOr<Connection?> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref = this.ref as $Ref<AsyncValue<Connection?>, Connection?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<Connection?>, Connection?>,
|
||||
AsyncValue<Connection?>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:mtcore/markettakers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:arbiter/features/pk_manager.dart';
|
||||
import 'package:arbiter/features/simple_ed25519.dart';
|
||||
import 'package:arbiter/features/identity/pk_manager.dart';
|
||||
import 'package:arbiter/features/identity/simple_ed25519.dart';
|
||||
|
||||
part 'key.g.dart';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:arbiter/features/server_info_storage.dart';
|
||||
import 'package:arbiter/features/connection/server_info_storage.dart';
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ class Router extends RootStackRouter {
|
||||
List<AutoRoute> get routes => [
|
||||
AutoRoute(page: Bootstrap.page, path: '/bootstrap', initial: true),
|
||||
AutoRoute(page: ServerInfoSetupRoute.page, path: '/server-info'),
|
||||
AutoRoute(page: ServerConnectionRoute.page, path: '/server-connection'),
|
||||
|
||||
AutoRoute(
|
||||
page: DashboardRouter.page,
|
||||
|
||||
@@ -13,18 +13,20 @@ import 'package:arbiter/screens/bootstrap.dart' as _i2;
|
||||
import 'package:arbiter/screens/dashboard.dart' as _i4;
|
||||
import 'package:arbiter/screens/dashboard/about.dart' as _i1;
|
||||
import 'package:arbiter/screens/dashboard/calc.dart' as _i3;
|
||||
import 'package:arbiter/screens/server_info_setup.dart' as _i5;
|
||||
import 'package:auto_route/auto_route.dart' as _i6;
|
||||
import 'package:arbiter/screens/server_connection.dart' as _i5;
|
||||
import 'package:arbiter/screens/server_info_setup.dart' as _i6;
|
||||
import 'package:auto_route/auto_route.dart' as _i7;
|
||||
import 'package:flutter/material.dart' as _i8;
|
||||
|
||||
/// generated route for
|
||||
/// [_i1.AboutScreen]
|
||||
class AboutRoute extends _i6.PageRouteInfo<void> {
|
||||
const AboutRoute({List<_i6.PageRouteInfo>? children})
|
||||
class AboutRoute extends _i7.PageRouteInfo<void> {
|
||||
const AboutRoute({List<_i7.PageRouteInfo>? children})
|
||||
: super(AboutRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'AboutRoute';
|
||||
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
static _i7.PageInfo page = _i7.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i1.AboutScreen();
|
||||
@@ -34,13 +36,13 @@ class AboutRoute extends _i6.PageRouteInfo<void> {
|
||||
|
||||
/// generated route for
|
||||
/// [_i2.Bootstrap]
|
||||
class Bootstrap extends _i6.PageRouteInfo<void> {
|
||||
const Bootstrap({List<_i6.PageRouteInfo>? children})
|
||||
class Bootstrap extends _i7.PageRouteInfo<void> {
|
||||
const Bootstrap({List<_i7.PageRouteInfo>? children})
|
||||
: super(Bootstrap.name, initialChildren: children);
|
||||
|
||||
static const String name = 'Bootstrap';
|
||||
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
static _i7.PageInfo page = _i7.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i2.Bootstrap();
|
||||
@@ -50,13 +52,13 @@ class Bootstrap extends _i6.PageRouteInfo<void> {
|
||||
|
||||
/// generated route for
|
||||
/// [_i3.CalcScreen]
|
||||
class CalcRoute extends _i6.PageRouteInfo<void> {
|
||||
const CalcRoute({List<_i6.PageRouteInfo>? children})
|
||||
class CalcRoute extends _i7.PageRouteInfo<void> {
|
||||
const CalcRoute({List<_i7.PageRouteInfo>? children})
|
||||
: super(CalcRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'CalcRoute';
|
||||
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
static _i7.PageInfo page = _i7.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i3.CalcScreen();
|
||||
@@ -66,13 +68,13 @@ class CalcRoute extends _i6.PageRouteInfo<void> {
|
||||
|
||||
/// generated route for
|
||||
/// [_i4.DashboardRouter]
|
||||
class DashboardRouter extends _i6.PageRouteInfo<void> {
|
||||
const DashboardRouter({List<_i6.PageRouteInfo>? children})
|
||||
class DashboardRouter extends _i7.PageRouteInfo<void> {
|
||||
const DashboardRouter({List<_i7.PageRouteInfo>? children})
|
||||
: super(DashboardRouter.name, initialChildren: children);
|
||||
|
||||
static const String name = 'DashboardRouter';
|
||||
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
static _i7.PageInfo page = _i7.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i4.DashboardRouter();
|
||||
@@ -81,17 +83,70 @@ class DashboardRouter extends _i6.PageRouteInfo<void> {
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i5.ServerInfoSetupScreen]
|
||||
class ServerInfoSetupRoute extends _i6.PageRouteInfo<void> {
|
||||
const ServerInfoSetupRoute({List<_i6.PageRouteInfo>? children})
|
||||
/// [_i5.ServerConnectionScreen]
|
||||
class ServerConnectionRoute
|
||||
extends _i7.PageRouteInfo<ServerConnectionRouteArgs> {
|
||||
ServerConnectionRoute({
|
||||
_i8.Key? key,
|
||||
String? arbiterUrl,
|
||||
List<_i7.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
ServerConnectionRoute.name,
|
||||
args: ServerConnectionRouteArgs(key: key, arbiterUrl: arbiterUrl),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ServerConnectionRoute';
|
||||
|
||||
static _i7.PageInfo page = _i7.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
final args = data.argsAs<ServerConnectionRouteArgs>(
|
||||
orElse: () => const ServerConnectionRouteArgs(),
|
||||
);
|
||||
return _i5.ServerConnectionScreen(
|
||||
key: args.key,
|
||||
arbiterUrl: args.arbiterUrl,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class ServerConnectionRouteArgs {
|
||||
const ServerConnectionRouteArgs({this.key, this.arbiterUrl});
|
||||
|
||||
final _i8.Key? key;
|
||||
|
||||
final String? arbiterUrl;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServerConnectionRouteArgs{key: $key, arbiterUrl: $arbiterUrl}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ServerConnectionRouteArgs) return false;
|
||||
return key == other.key && arbiterUrl == other.arbiterUrl;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ arbiterUrl.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i6.ServerInfoSetupScreen]
|
||||
class ServerInfoSetupRoute extends _i7.PageRouteInfo<void> {
|
||||
const ServerInfoSetupRoute({List<_i7.PageRouteInfo>? children})
|
||||
: super(ServerInfoSetupRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'ServerInfoSetupRoute';
|
||||
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
static _i7.PageInfo page = _i7.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i5.ServerInfoSetupScreen();
|
||||
return const _i6.ServerInfoSetupScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
60
useragent/lib/screens/server_connection.dart
Normal file
60
useragent/lib/screens/server_connection.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
import 'package:arbiter/providers/connection/connection_manager.dart';
|
||||
import 'package:arbiter/router.gr.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:sizer/sizer.dart';
|
||||
|
||||
@RoutePage()
|
||||
class ServerConnectionScreen extends HookConsumerWidget {
|
||||
const ServerConnectionScreen({super.key, this.arbiterUrl});
|
||||
|
||||
final String? arbiterUrl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final connectionState = ref.watch(connectionManagerProvider);
|
||||
|
||||
if (connectionState.value != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.router.replace(const DashboardRouter());
|
||||
});
|
||||
}
|
||||
|
||||
final body = switch (connectionState) {
|
||||
AsyncLoading() => const CircularProgressIndicator(),
|
||||
AsyncError(:final error) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Connection failed',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 1.5.h),
|
||||
Text('$error', textAlign: TextAlign.center),
|
||||
SizedBox(height: 2.h),
|
||||
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.router.replace(const ServerInfoSetupRoute());
|
||||
},
|
||||
child: const Text('Back to server setup'),
|
||||
),
|
||||
],
|
||||
),
|
||||
_ => const CircularProgressIndicator(),
|
||||
};
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Connecting')),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w),
|
||||
child: body,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:arbiter/features/arbiter_url.dart';
|
||||
import 'package:arbiter/features/server_info_storage.dart';
|
||||
import 'package:arbiter/features/connection/arbiter_url.dart';
|
||||
import 'package:arbiter/features/connection/server_info_storage.dart';
|
||||
import 'package:arbiter/providers/connection/bootstrap_token.dart';
|
||||
import 'package:arbiter/providers/server_info.dart';
|
||||
import 'package:arbiter/router.gr.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
@@ -25,7 +26,7 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
||||
if (serverInfo != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (context.mounted) {
|
||||
context.router.replace(const DashboardRouter());
|
||||
context.router.replace(ServerConnectionRoute());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -38,6 +39,14 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
||||
|
||||
try {
|
||||
final arbiterUrl = ArbiterUrl.parse(controller.text.trim());
|
||||
|
||||
// set token before triggering reconnection by updating server info
|
||||
if (arbiterUrl.bootstrapToken != null) {
|
||||
ref
|
||||
.read(bootstrapTokenProvider.notifier)
|
||||
.set(arbiterUrl.bootstrapToken!);
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(serverInfoProvider.notifier)
|
||||
.save(
|
||||
@@ -47,7 +56,9 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
context.router.replace(const DashboardRouter());
|
||||
context.router.replace(
|
||||
ServerConnectionRoute(arbiterUrl: controller.text.trim()),
|
||||
);
|
||||
}
|
||||
} on FormatException catch (error) {
|
||||
errorText.value = error.message;
|
||||
@@ -94,13 +105,15 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
||||
final options = [
|
||||
const _OptionCard(
|
||||
title: 'Local',
|
||||
subtitle: 'Will start and connect to a local service in a future update.',
|
||||
subtitle:
|
||||
'Will start and connect to a local service in a future update.',
|
||||
enabled: false,
|
||||
child: SizedBox.shrink(),
|
||||
),
|
||||
_OptionCard(
|
||||
title: 'Remote',
|
||||
subtitle: 'Paste an Arbiter URL to store the server address, port, and CA fingerprint.',
|
||||
subtitle:
|
||||
'Paste an Arbiter URL to store the server address, port, and CA fingerprint.',
|
||||
child: _RemoteConnectionForm(
|
||||
controller: controller,
|
||||
errorText: errorText.value,
|
||||
@@ -271,10 +284,7 @@ class _OptionCard extends StatelessWidget {
|
||||
),
|
||||
SizedBox(height: 1.h),
|
||||
Text(subtitle, style: theme.textTheme.bodyMedium),
|
||||
if (enabled) ...[
|
||||
SizedBox(height: 2.h),
|
||||
child,
|
||||
],
|
||||
if (enabled) ...[SizedBox(height: 2.h), child],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,5 +4,7 @@
|
||||
<dict>
|
||||
<key>keychain-access-groups</key>
|
||||
<array/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -6,5 +6,7 @@
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -218,7 +218,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.3.5+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
@@ -401,8 +401,16 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
freezed:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: freezed
|
||||
sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.3"
|
||||
freezed_annotation:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
|
||||
@@ -538,13 +546,21 @@ packages:
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.11.0"
|
||||
version: "4.9.0"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -918,6 +934,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.8"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -22,6 +22,7 @@ dependencies:
|
||||
hosted: https://git.markettakers.org/api/packages/MarketTakers/pub/
|
||||
version: ^1.0.6
|
||||
cryptography: ^2.9.0
|
||||
crypto: ^3.0.6
|
||||
flutter_secure_storage: ^10.0.0
|
||||
cryptography_flutter: ^2.3.4
|
||||
riverpod_annotation: ^4.0.0
|
||||
@@ -30,6 +31,8 @@ dependencies:
|
||||
flutter_hooks: ^0.21.3+1
|
||||
auto_route: ^11.1.0
|
||||
protobuf: ^6.0.0
|
||||
freezed_annotation: ^3.1.0
|
||||
json_annotation: ^4.9.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -39,6 +42,8 @@ dev_dependencies:
|
||||
riverpod_generator: ^4.0.0+1
|
||||
build_runner: ^2.12.2
|
||||
auto_route_generator: ^10.4.0
|
||||
freezed: ^3.2.3
|
||||
json_serializable: ^6.11.2
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
Reference in New Issue
Block a user