Skip to content

Commit

Permalink
Merge pull request #1219 from Foundation-Devices/ENV-1101-keys-endpoint
Browse files Browse the repository at this point in the history
ENV-1101: fetch some API keys from Server endpoint
  • Loading branch information
icota authored Jun 18, 2024
2 parents 751a06e + df40baa commit f62f8d0
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 78 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ jobs:
KEY_JKS: ${{ secrets.KEY_JKS }}
KEY_PASSWORD: ${{ secrets.ALIAS_PASSWORD }}
ALIAS_PASSWORD: ${{ secrets.KEY_PASSWORD }}
MAP_API_KEY: ${{ secrets.MAP_API_KEY }}
RAMP_API_KEY: ${{ secrets.RAMP_API_KEY }}
run: echo -n $KEY_JKS | base64 -di > android/key.jks && just docker-build-android-sign

- uses: actions/upload-artifact@v2
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,8 @@ jobs:
run: ./build_ffi_ios.sh

- name: Build iOS App Store Package
env:
MAP_API_KEY: ${{ secrets.MAP_API_KEY }}
RAMP_API_KEY: ${{ secrets.RAMP_API_KEY }}
run: |
flutter build ipa --dart-define=MAP_API_KEY=$MAP_API_KEY --dart-define=RAMP_API_KEY=$RAMP_API_KEY --release --export-options-plist=ios/ExportOptions.plist
flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
- uses: actions/upload-artifact@v2
with:
Expand Down
7 changes: 3 additions & 4 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,17 @@ docker-build:
docker-build-android: docker-build
mkdir -p release && \
docker run --mount type=bind,source="$(pwd)"/release,target=/release \
-e MAP_API_KEY=$MAP_API_KEY -e RAMP_API_KEY=$RAMP_API_KEY\
-t {{docker_image}} /bin/bash \
-c "flutter build apk --release --dart-define='MAP_API_KEY=$MAP_API_KEY' --dart-define='RAMP_API_KEY=$RAMP_API_KEY' -P nosign && flutter build appbundle --dart-define='MAP_API_KEY=$MAP_API_KEY' --dart-define='RAMP_API_KEY=$RAMP_API_KEY' --release -P nosign \
-c "flutter build apk --release -P nosign && flutter build appbundle --release -P nosign \
&& cp /root/build/app/outputs/flutter-apk/app-release.apk /release \
&& cp /root/build/app/outputs/bundle/release/app-release.aab /release"

docker-build-android-sign: docker-build
mkdir -p release && \
docker run --mount type=bind,source="$(pwd)"/release,target=/release \
-e ALIAS_PASSWORD=$ALIAS_PASSWORD -e KEY_PASSWORD=$KEY_PASSWORD -e MAP_API_KEY=$MAP_API_KEY -e RAMP_API_KEY=$RAMP_API_KEY \
-e ALIAS_PASSWORD=$ALIAS_PASSWORD -e KEY_PASSWORD=$KEY_PASSWORD \
-t {{docker_image}} /bin/bash \
-c "flutter build apk --dart-define='MAP_API_KEY=$MAP_API_KEY' --dart-define='RAMP_API_KEY=$RAMP_API_KEY' --release && flutter build appbundle --dart-define='MAP_API_KEY=$MAP_API_KEY' --dart-define='RAMP_API_KEY=$RAMP_API_KEY' --release \
-c "flutter build apk --release && flutter build appbundle --release \
&& cp /root/build/app/outputs/flutter-apk/app-release.apk /release \
&& cp /root/build/app/outputs/bundle/release/app-release.aab /release"

Expand Down
55 changes: 55 additions & 0 deletions lib/business/keys_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later

import 'dart:async';
import 'package:envoy/util/console.dart';
import 'package:envoy/util/envoy_storage.dart';
import 'package:envoy/business/server.dart';

class KeysManager {
ApiKeys? keys;

static final KeysManager _instance = KeysManager._internal();

factory KeysManager() {
return _instance;
}

static Future<KeysManager> init() async {
var singleton = KeysManager._instance;
await singleton._initAsync();
return singleton;
}

_initAsync() async {
keys = await EnvoyStorage().getApiKeys();
}

KeysManager._internal() {
kPrint("Instance of KeysManager created!");

// Fetch from the Envoy Server
_fetchKeysFromServer();

// Check once an hour
Timer.periodic(const Duration(hours: 1), (_) {
_fetchKeysFromServer();
});
}

void _fetchKeysFromServer() {
Server().fetchApiKeys().then((keys) => _store(keys)).catchError((e) {
kPrint("Couldn't fetch API keys: $e");
});
}

_store(ApiKeys newKeys) async {
if (keys == null ||
keys!.mapsKey != newKeys.mapsKey ||
keys!.rampKey != newKeys.rampKey) {
await EnvoyStorage().storeApiKeys(newKeys);
keys = newKeys;
}
}
}
41 changes: 0 additions & 41 deletions lib/business/map_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ import 'package:tor/tor.dart';
import 'package:http_tor/http_tor.dart';
import 'package:envoy/business/scheduler.dart';
import 'dart:core';
import 'package:envoy/business/coordinates.dart';

class MapData {
static const String mapApiKey =
String.fromEnvironment("MAP_API_KEY", defaultValue: '');
List<Venue> venues = [];

static final MapData _instance = MapData._internal();
Expand Down Expand Up @@ -107,42 +104,4 @@ class MapData {
"https://coinmap.org/api/v1/venues/$id",
);
}

Future<Coordinates> getCoordinates(
String divisionName, String countryName) async {
var response = await HttpTor(Tor.instance, EnvoyScheduler().parallel).get(
"https://api.geoapify.com/v1/geocode/search?text=$divisionName&format=json&apiKey=$mapApiKey",
);

var data = jsonDecode(response.body);
if (data['results'] != null && data['results'].isNotEmpty) {
for (var result in data['results']) {
if (result['country'] == countryName) {
// Extract latitude and longitude
double latitude = (result['lat'] as num).toDouble();
double longitude = (result['lon'] as num).toDouble();

return Coordinates(latitude, longitude);
}
}
}

// If no matching result is found, search by country name
var response2 = await HttpTor(Tor.instance, EnvoyScheduler().parallel).get(
"https://api.geoapify.com/v1/geocode/search?country=$countryName&format=json&apiKey=$mapApiKey",
);

var data2 = jsonDecode(response2.body);
if (data2['results'] != null && data2['results'].isNotEmpty) {
var firstResult = data2['results'][0];

// Extract latitude and longitude
double latitude = (firstResult['lat'] as num).toDouble();
double longitude = (firstResult['lon'] as num).toDouble();

return Coordinates(latitude, longitude);
}

return Coordinates(null, null);
}
}
37 changes: 28 additions & 9 deletions lib/business/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,39 @@ class Server {
throw Exception('Failed to find firmware');
}
}

Future<ApiKeys> fetchApiKeys() async {
final response = await http!.get('$_serverAddress/keys');

if (response.statusCode == 202) {
return ApiKeys.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to fetch API keys');
}
}
}

class User {
final String id;
bool confirmed = false;
String token = "";
class ApiKeys {
final String mapsKey;
final String rampKey;

User({
required this.id,
required this.confirmed,
ApiKeys({
required this.mapsKey,
required this.rampKey,
});

factory User.fromJson(Map<String, dynamic> json) {
return User(id: json['id'], confirmed: json['confirmed']);
factory ApiKeys.fromJson(Map<String, dynamic> json) {
final keys = json['keys'];
return ApiKeys(mapsKey: keys['maps_api'], rampKey: keys['ramp_api']);
}

Map<String, dynamic> toJson() {
return {
'keys': {
'maps_api': mapsKey,
'ramp_api': rampKey,
}
};
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import 'package:envoy/business/account_manager.dart';
import 'package:envoy/business/connectivity_manager.dart';
import 'package:envoy/business/envoy_seed.dart';
import 'package:envoy/business/keys_manager.dart';
import 'package:envoy/business/map_data.dart';
import 'package:envoy/business/scheduler.dart';
import 'package:envoy/business/local_storage.dart';
Expand Down Expand Up @@ -58,6 +59,7 @@ Future<void> initSingletons() async {
await EnvoyStorage().init();
await LocalStorage.init();
EnvoyScheduler.init();
await KeysManager.init();
EnvoyReport().init();
Settings.restore();
Tor.init(enabled: Settings().torEnabled());
Expand Down
20 changes: 11 additions & 9 deletions lib/ui/components/map_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:convert';
import 'dart:math';
import 'dart:core';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:envoy/business/keys_manager.dart';
import 'package:envoy/ui/components/pop_up.dart';
import 'package:envoy/ui/theme/envoy_icons.dart';
import 'package:envoy/ui/theme/envoy_spacing.dart';
Expand Down Expand Up @@ -186,10 +187,13 @@ class MarkersPageState extends State<MarkersPage> {
}
}

String _openStreetMap(int z, int x, int y) {
final url =
"https://maps.geoapify.com/v1/tile/$mapType/$z/$x/$y.png?&apiKey=${MapData.mapApiKey}";
return url;
String _getTileUrl(int z, int x, int y) {
final mapApiKey = KeysManager().keys?.mapsKey;
if (mapApiKey == null) {
return "";
}

return "https://maps.geoapify.com/v1/tile/$mapType/$z/$x/$y.png?&apiKey=$mapApiKey";
}

Widget _buildVenueMarkerWidget(
Expand Down Expand Up @@ -349,7 +353,7 @@ class MarkersPageState extends State<MarkersPage> {
x %= tilesInZoom;
y %= tilesInZoom;
return CachedNetworkImage(
imageUrl: _openStreetMap(z, x, y),
imageUrl: _getTileUrl(z, x, y),
fit: BoxFit.cover,
errorListener: (e) {
setState(() {
Expand Down Expand Up @@ -560,10 +564,8 @@ class TriangleShadow extends CustomPainter {
center: Alignment.topCenter,
focalRadius: 0.5,
radius: 0.8,
stops: const [
0.0,
0.9
], // Ensure stops are correct for smooth transition
stops: const [0.0, 0.9],
// Ensure stops are correct for smooth transition
colors: [
EnvoyColors.border1.withOpacity(0.7), // Adjust colors as needed
Colors.transparent
Expand Down
10 changes: 6 additions & 4 deletions lib/ui/components/ramp_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later

import 'dart:convert';
import 'package:envoy/business/keys_manager.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:http_tor/http_tor.dart';
Expand All @@ -20,15 +21,16 @@ import 'package:envoy/ui/shield.dart';
import 'package:envoy/business/scheduler.dart';

class RampWidget {
static const String rampApiKey =
String.fromEnvironment("RAMP_API_KEY", defaultValue: '');

static void showRamp(BuildContext context, Account account, String address) {
if (KeysManager().keys == null) {
return;
}

final ramp = RampFlutter();
final Configuration configuration = Configuration();
configuration.hostAppName = "Ramp Flutter";
configuration.url = "https://app.ramp.network";
configuration.hostApiKey = rampApiKey;
configuration.hostApiKey = KeysManager().keys!.rampKey;
configuration.userAddress = address;
configuration.swapAsset = "BTC_BTC";
ramp.onOnrampPurchaseCreated = (purchase, purchaseViewToken, apiUrl) =>
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/storage/coins_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:sembast/sembast.dart';
/// Repository for storing and retrieving coin tag data
class CoinRepository {
final storage = EnvoyStorage();
Database db = EnvoyStorage().db;
Database db = EnvoyStorage().db();

static CoinRepository? _instance;

Expand Down
26 changes: 22 additions & 4 deletions lib/util/envoy_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'package:sembast/utils/sembast_import_export.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wallet/wallet.dart' as wallet;
import 'package:envoy/business/country.dart';
import 'package:envoy/business/server.dart';

class FirmwareInfo {
FirmwareInfo({
Expand Down Expand Up @@ -65,6 +66,7 @@ const String blogPostsStoreName = "blog_posts";
const String exchangeRateStoreName = "exchange_rate";
const String locationsStoreName = "locations";
const String selectedCountryStoreName = "countries";
const String apiKeysStoreName = "api_keys";

///keeps track of spend input tags, this would be handy to show previously used tags
///for example when user trying RBF.
Expand Down Expand Up @@ -116,6 +118,8 @@ class EnvoyStorage {
StoreRef<int, String> locationStore =
StoreRef<int, String>(locationsStoreName);

StoreRef<int, String> apiKeysStore = StoreRef<int, String>(apiKeysStoreName);

// Store everything except videos, blogs and locations
Map<String, StoreRef> storesToBackUp = {};

Expand Down Expand Up @@ -236,7 +240,7 @@ class EnvoyStorage {
}

Future removePromptState(DismissiblePrompt prompt) async {
await dismissedPromptsStore.record(prompt.toString()).delete(db);
await dismissedPromptsStore.record(prompt.toString()).delete(_db);
return true;
}

Expand All @@ -254,7 +258,7 @@ class EnvoyStorage {
final filter = Finder(filter: Filter.byKey(prompt.toString()));
//returns boolean stream that updates when provided key is updated
return dismissedPromptsStore
.find(db, finder: filter)
.find(_db, finder: filter)
.then((event) => event.isNotEmpty);
}

Expand Down Expand Up @@ -503,7 +507,7 @@ class EnvoyStorage {
// Following methods have same signature as shared_preferences
// but use the preferences DB store instead
Future setNewPreferencesValue(String key, value) async {
await preferencesStore.record(key).put(db, value);
await preferencesStore.record(key).put(_db, value);
return true;
}

Expand Down Expand Up @@ -752,5 +756,19 @@ class EnvoyStorage {
return null;
}

Database get db => _db;
Future<bool> storeApiKeys(ApiKeys keys) async {
await apiKeysStore.record(0).put(_db, jsonEncode(keys.toJson()));
return true;
}

Future<ApiKeys?> getApiKeys() async {
var finder = Finder(filter: Filter.byKey(0));
var keys = await apiKeysStore.findFirst(_db, finder: finder);
if (keys != null) {
return ApiKeys.fromJson(jsonDecode(keys.value));
}
return null;
}

Database db() => _db;
}

0 comments on commit f62f8d0

Please sign in to comment.