Skip to content

Commit

Permalink
Merge branch 'trunk' into c-defunct
Browse files Browse the repository at this point in the history
  • Loading branch information
XavierChanth authored Nov 25, 2024
2 parents a574e3f + ec021b5 commit ab1a918
Show file tree
Hide file tree
Showing 10 changed files with 735 additions and 131 deletions.
19 changes: 17 additions & 2 deletions packages/dart/npt_flutter/lib/constants.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart' show dotenv;

class Constants {
static bool dotenvLoaded = false;
static Future<void> loadDotenv() async {
if (dotenvLoaded) return;
try {
await dotenv.load();
dotenvLoaded = true;
} catch (_) {
dotenvLoaded = false;
}
}

static String? get namespace => 'noports';
// TODO: issue & secure API key properly
static String? get appAPIKey => 'asdf';

static Future<String?> get appAPIKey async {
await loadDotenv();
return dotenv.env["APP_API_KEY"];
}

static const pngIconDark = 'assets/noports-icon64-dark.png';
static const icoIconDark = 'assets/noports-icon64-dark.ico';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ class FavoriteBloc extends LoggingBloc<FavoriteEvent, FavoritesState> {

void clearAll() => emit(const FavoritesInitial());

FutureOr<void> _onLoad(
FavoriteLoadEvent event, Emitter<FavoritesState> emit) async {
FutureOr<void> _onLoad(FavoriteLoadEvent event, Emitter<FavoritesState> emit) async {
emit(const FavoritesLoading());

Map<String, Favorite>? favs;
Expand All @@ -35,8 +34,7 @@ class FavoriteBloc extends LoggingBloc<FavoriteEvent, FavoritesState> {
emit(FavoritesLoaded(favs.values));
}

FutureOr<void> _onAdd(
FavoriteAddEvent event, Emitter<FavoritesState> emit) async {
FutureOr<void> _onAdd(FavoriteAddEvent event, Emitter<FavoritesState> emit) async {
if (state is! FavoritesLoaded) {
return;
}
Expand All @@ -49,17 +47,13 @@ class FavoriteBloc extends LoggingBloc<FavoriteEvent, FavoritesState> {
} catch (_) {}
}

FutureOr<void> _onRemove(
FavoriteRemoveEvent event, Emitter<FavoritesState> emit) async {
FutureOr<void> _onRemove(FavoriteRemoveEvent event, Emitter<FavoritesState> emit) async {
if (state is! FavoritesLoaded) {
return;
}

emit(FavoritesLoaded(
(state as FavoritesLoaded)
.favorites
.toSet()
.difference(event.toRemove.toSet()),
(state as FavoritesLoaded).favorites.toSet().difference(event.toRemove.toSet()),
));

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import 'dart:convert';
import 'dart:io';

import 'package:at_auth/at_auth.dart';
import 'package:at_onboarding_flutter/at_onboarding_flutter.dart' hide Response;
import 'package:at_onboarding_flutter/at_onboarding_services.dart';
// ignore: implementation_imports
import 'package:at_onboarding_flutter/src/utils/at_onboarding_response_status.dart';
import 'package:at_server_status/at_server_status.dart';
import 'package:http/http.dart';
import 'package:http/io_client.dart';

// Type returned from a method below
export 'package:at_onboarding_flutter/src/utils/at_onboarding_response_status.dart';

const apiBase = '/api/app/v3';

enum NoPortsActivateApiEndpoints {
login('$apiBase/authenticate/atsign'),
validate('$apiBase/authenticate/atsign/activate');

final String path;
const NoPortsActivateApiEndpoints(this.path);
}

class ActivateUtil {
final String registrarUrl;
final String apiKey;
late final IOClient _http;

ActivateUtil({required this.registrarUrl, required this.apiKey}) {
var innerClient = HttpClient();
innerClient.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
_http = IOClient();
}

Future<Response> registrarApiRequest(NoPortsActivateApiEndpoints endpoint, Map<String, String?> data) async {
Uri url = Uri.https(registrarUrl, endpoint.path);

return _http.post(
url,
body: jsonEncode(data),
headers: <String, String>{
'Authorization': apiKey,
'Content-Type': 'application/json',
},
);
}

Future<({String? cramkey, String? errorMessage})> verifyActivation(
{required String atsign, required String otp}) async {
var res = await registrarApiRequest(
NoPortsActivateApiEndpoints.validate,
{
'atsign': atsign,
'otp': otp,
},
);
if (res.statusCode != 200) {
return (
errorMessage: AtOnboardingLocalizations.current.error_server_unavailable,
cramkey: null,
);
}
var payload = jsonDecode(res.body);
if (payload["message"] != "Verified") {
// The toString is for typesafety & to prevent unexpected crashes
return (errorMessage: payload["message"].toString(), cramkey: null);
}
String cramkey = payload["cramkey"]?.split(':').last ?? '';
return (cramkey: cramkey, errorMessage: null);
}

Future<AtOnboardingResult> onboardFromCramKey({
required String atsign,
required String cramkey,
required AtOnboardingConfig config,
}) async {
try {
atsign = atsign.startsWith('@') ? atsign : '@$atsign';
OnboardingService onboardingService = OnboardingService.getInstance();
bool isExist = await onboardingService.isExistingAtsign(atsign);
if (isExist) {
return AtOnboardingResult.error(
message: AtOnboardingLocalizations.current.error_atSign_activated,
);
}

//Delay for waiting for ServerStatus change to teapot when activating an atsign
await Future.delayed(const Duration(seconds: 10));

config.atClientPreference.cramSecret = cramkey;
onboardingService.setAtClientPreference = config.atClientPreference;

onboardingService.setAtsign = atsign;
AtOnboardingRequest req = AtOnboardingRequest(atsign);
var res = await onboardingService.onboard(
cramSecret: cramkey,
atOnboardingRequest: req,
);

if (res) {
int round = 1;
ServerStatus? atSignStatus = await onboardingService.checkAtSignServerStatus(atsign);
while (atSignStatus != ServerStatus.activated) {
if (round > 10) {
break;
}
await Future.delayed(const Duration(seconds: 3));
round++;
atSignStatus = await onboardingService.checkAtSignServerStatus(atsign);
}

if (atSignStatus == ServerStatus.teapot) {
return AtOnboardingResult.error(
message: AtOnboardingLocalizations.current.msg_atSign_unreachable,
);
} else if (atSignStatus == ServerStatus.activated) {
return AtOnboardingResult.success(atsign: atsign);
}
}

return AtOnboardingResult.error(message: AtOnboardingLocalizations.current.error_authenticated_failed);
} catch (e) {
if (e == AtOnboardingResponseStatus.authFailed) {
return AtOnboardingResult.error(
message: AtOnboardingLocalizations.current.error_authenticated_failed,
);
} else if (e == AtOnboardingResponseStatus.serverNotReached) {
return AtOnboardingResult.error(
message: AtOnboardingLocalizations.current.msg_atSign_unreachable,
);
} else if (e == AtOnboardingResponseStatus.timeOut) {
return AtOnboardingResult.error(
message: AtOnboardingLocalizations.current.msg_response_time_out,
);
}
return AtOnboardingResult.error(message: e.toString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'dart:async';
import 'package:at_onboarding_flutter/at_onboarding_flutter.dart';
import 'package:at_onboarding_flutter/at_onboarding_services.dart' show AtKeysFileUploadService, FileUploadStatus;
import 'package:at_server_status/at_server_status.dart';

// These types are returned from methods in this class so exports are provided for ease of use
export 'package:at_onboarding_flutter/at_onboarding_services.dart' show FileUploadStatus;
export 'package:at_server_status/at_server_status.dart' show AtStatus;

class NoPortsOnboardingUtil {
/// The upload service will be created when the first time [uploadAtKeysFile] is called
AtKeysFileUploadService? _uploadService;
AtServerStatus? _atServerStatus;
AtOnboardingConfig config;
NoPortsOnboardingUtil(this.config);

/// A method to check whether an atSign has been activated or not
Future<AtStatus> atServerStatus(String atSign) async {
_atServerStatus ??=
AtStatusImpl(rootUrl: config.atClientPreference.rootDomain, rootPort: config.atClientPreference.rootPort);
return _atServerStatus!.get(atSign);
}

/// Upload an atKeys file, returning a stream with the progress so we can update the ui accordingly.
/// Example implementation:
/// https://github.com/atsign-foundation/at_widgets/blob/b4006854fa93c21eeb5bcea41044787bdf0f6f32/packages/at_onboarding_flutter/lib/src/screen/at_onboarding_home_screen.dart#L659
Stream<FileUploadStatus> uploadAtKeysFile(String? atSign) {
_uploadService ??= AtKeysFileUploadService(config: config);
return _uploadService!.uploadKeyFile(atSign);
}

// TODO: implement APKAM onboarding
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Future<void> postOnboard(String atSign, String rootDomain) async {
status: OnboardingStatus.onboarded,
);
// Start loading application data in the background as soon as we have an atClient
App.navState.currentContext?.read<FavoriteBloc>().add(const FavoriteLoadEvent());
App.navState.currentContext?.read<ProfileListBloc>().add(const ProfileListLoadEvent());
App.navState.currentContext?.read<SettingsBloc>().add(const SettingsLoadEvent());
App.navState.currentContext?.read<FavoriteBloc>().add(const FavoriteLoadEvent());
}
Loading

0 comments on commit ab1a918

Please sign in to comment.