From ab05579fc1b423716c9d551632cceb5aed2aa671 Mon Sep 17 00:00:00 2001 From: koalasat Date: Fri, 25 Jul 2025 12:36:15 +0200 Subject: [PATCH] Encrypted Storage --- android/app/build.gradle.kts | 2 +- .../main/java/com/robosats/MainActivity.kt | 15 +- .../main/java/com/robosats/WebAppInterface.kt | 59 ++++++ .../com/robosats/models/EncryptedStorage.kt | 42 ++++ .../java/com/robosats/models/NostrClient.kt | 5 + android/gradle/libs.versions.toml | 2 + .../TopBar/NotificationsDrawer/index.tsx | 15 +- .../components/HostAlert/SelfhostedAlert.tsx | 6 +- .../src/components/HostAlert/UnsafeAlert.tsx | 6 +- frontend/src/models/Federation.model.ts | 4 - frontend/src/models/Garage.model.ts | 4 +- .../Settings.default.basic.selfhosted.ts | 3 - frontend/src/models/Settings.default.basic.ts | 3 - .../models/Settings.default.pro.selfhosted.ts | 3 - frontend/src/models/Settings.default.pro.ts | 3 - frontend/src/models/Settings.model.ts | 78 ++++--- frontend/src/services/Android/index.ts | 3 + .../System/SystemAndroidClient/index.ts | 49 ++--- .../System/SystemDesktopClient/index.ts | 28 +-- .../services/System/SystemWebClient/index.ts | 28 +-- frontend/src/services/System/index.ts | 5 +- .../services/api/ApiAndroidClient/index.ts | 17 +- frontend/templates/frontend/index.ejs | 198 ++++++++++-------- 23 files changed, 317 insertions(+), 261 deletions(-) create mode 100644 android/app/src/main/java/com/robosats/models/EncryptedStorage.kt diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 4ad932d4..a71213e2 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -66,7 +66,6 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) @@ -74,6 +73,7 @@ dependencies { implementation(libs.kmp.tor) implementation(libs.quartz) implementation(libs.ammolite) + implementation(libs.security.crypto.ktx) // Add the KMP Tor binary dependency (contains the native .so files) implementation(libs.kmp.tor.binary) implementation(libs.androidx.activity) diff --git a/android/app/src/main/java/com/robosats/MainActivity.kt b/android/app/src/main/java/com/robosats/MainActivity.kt index 1355b301..c90825c2 100644 --- a/android/app/src/main/java/com/robosats/MainActivity.kt +++ b/android/app/src/main/java/com/robosats/MainActivity.kt @@ -28,6 +28,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat +import com.robosats.models.EncryptedStorage import com.robosats.services.NotificationsService import com.robosats.tor.TorKmp import com.robosats.tor.TorKmpManager @@ -42,6 +43,8 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + EncryptedStorage.init(this) + // Lock the screen orientation to portrait mode requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT @@ -73,6 +76,9 @@ class MainActivity : AppCompatActivity() { } } + /** + * Initialize Notifications service + */ private fun initializeNotifications() { startForegroundService( Intent( @@ -82,8 +88,10 @@ class MainActivity : AppCompatActivity() { ) } + /** + * Initialize TorKmp if it's not already initialized + */ private fun initializeTor() { - // Initialize TorKmp if it's not already initialized try { try { torKmp = TorKmpManager.getTorKmpObject() @@ -170,6 +178,9 @@ class MainActivity : AppCompatActivity() { } } + /** + * Configures initial WebView settings with external blocked + */ private fun setupWebView() { // Double-check Tor is connected before proceeding if (!torKmp.isConnected()) { @@ -367,8 +378,6 @@ class MainActivity : AppCompatActivity() { webSettings.textZoom = 100 } - // SSL error description method removed as we're not using SSL - /** * Clear all WebView data when activity is destroyed */ diff --git a/android/app/src/main/java/com/robosats/WebAppInterface.kt b/android/app/src/main/java/com/robosats/WebAppInterface.kt index 1d569bac..3cae7c49 100644 --- a/android/app/src/main/java/com/robosats/WebAppInterface.kt +++ b/android/app/src/main/java/com/robosats/WebAppInterface.kt @@ -6,6 +6,7 @@ import android.util.Log import android.webkit.JavascriptInterface import android.webkit.WebView import android.widget.Toast +import com.robosats.models.EncryptedStorage import com.robosats.tor.TorKmpManager.getTorKmpObject import okhttp3.Call import okhttp3.Callback @@ -318,6 +319,64 @@ class WebAppInterface(private val context: Context, private val webView: WebView } } + @JavascriptInterface + fun getEncryptedStorage(uuid: String, key: String) { + // Validate inputs before processing + if (!isValidUuid(uuid) || !isValidInput(key)) { + Log.e(TAG, "Invalid input for getEncryptedStorage: uuid=$uuid, key=$key") + rejectPromise(uuid, "Invalid input parameters") + return + } + + try { + // Sanitize the input before passing to native code + val sanitizedKey = key.trim() + + val value = EncryptedStorage.getEncryptedStorage(sanitizedKey) + + // Safely encode and return the result + resolvePromise(uuid, value) + } catch (e: Exception) { + Log.e(TAG, "Error in getEncryptedStorage", e) + rejectPromise(uuid, "Error obtaining encrypted storage: $key") + } + } + + @JavascriptInterface + fun setEncryptedStorage(uuid: String, key: String, value: String) { + // Validate inputs before processing + if (!isValidUuid(uuid) || !isValidInput(key)) { + Log.e(TAG, "Invalid input for setEncryptedStorage: uuid=$uuid, key=$key") + rejectPromise(uuid, "Invalid input parameters") + return + } + // Sanitize the input before passing to native code + val sanitizedKey = key.trim() + val sanitizedValue = value.trim() + + EncryptedStorage.setEncryptedStorage(sanitizedKey, sanitizedValue) + + // Safely encode and return the result + resolvePromise(uuid, key) + } + + @JavascriptInterface + fun deleteEncryptedStorage(uuid: String, key: String) { + // Validate inputs before processing + if (!isValidUuid(uuid) || !isValidInput(key)) { + Log.e(TAG, "Invalid input for deleteEncryptedStorage: uuid=$uuid, key=$key") + rejectPromise(uuid, "Invalid input parameters") + return + } + // Sanitize the input before passing to native code + val sanitizedKey = key.trim() + + EncryptedStorage.deleteEncryptedStorage(sanitizedKey) + + // Safely encode and return the result + resolvePromise(uuid, key) + } + private fun onWsMessage(path: String?, message: String?) { val encodedMessage = encodeForJavaScript(message) safeEvaluateJavascript("javascript:window.AndroidRobosats.onWSMessage('$path', '$encodedMessage')") diff --git a/android/app/src/main/java/com/robosats/models/EncryptedStorage.kt b/android/app/src/main/java/com/robosats/models/EncryptedStorage.kt new file mode 100644 index 00000000..bff5c52f --- /dev/null +++ b/android/app/src/main/java/com/robosats/models/EncryptedStorage.kt @@ -0,0 +1,42 @@ +package com.robosats.models + +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey + + +object EncryptedStorage { + private const val PREFERENCES_NAME = "secret_keeper" + + private lateinit var sharedPreferences: SharedPreferences + + + fun init(context: Context) { + val masterKey: MasterKey = + MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + sharedPreferences = EncryptedSharedPreferences.create( + context, + PREFERENCES_NAME, + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, + ) as EncryptedSharedPreferences + } + + fun setEncryptedStorage(key: String, value: String) { + sharedPreferences.edit { putString(key, value) } + } + + fun getEncryptedStorage(key: String): String { + return sharedPreferences.getString(key, "") ?: "" + } + + fun deleteEncryptedStorage(key: String) { + sharedPreferences.edit { remove(key) } + } +} diff --git a/android/app/src/main/java/com/robosats/models/NostrClient.kt b/android/app/src/main/java/com/robosats/models/NostrClient.kt index f7d39ae3..78107bbb 100644 --- a/android/app/src/main/java/com/robosats/models/NostrClient.kt +++ b/android/app/src/main/java/com/robosats/models/NostrClient.kt @@ -7,6 +7,7 @@ import com.vitorpamplona.ammolite.relays.Relay import com.vitorpamplona.ammolite.relays.RelayPool import com.vitorpamplona.ammolite.relays.TypedFilter import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter +import org.json.JSONObject object NostrClient { private var subscriptionNotificationId = "robosatsNotificationId" @@ -41,6 +42,10 @@ object NostrClient { } private fun connectRelays() { + val garageString = EncryptedStorage.getEncryptedStorage("garage_slots") + + val garage = JSONObject(garageString) + val relays = emptyList() relays.forEach { diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 66f5daa1..eb7c778c 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -2,6 +2,7 @@ agp = "8.11.1" kotlin = "2.0.21" coreKtx = "1.16.0" +securityCryptoKtx = "1.1.0-beta01" junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" @@ -28,6 +29,7 @@ kmp-tor-binary = { group = "io.matthewnelson.kotlin-components", name = "kmp-tor okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } quartz = { module = "com.github.vitorpamplona.amethyst:quartz", version.ref = "quartz" } ammolite = { module = "com.github.vitorpamplona.amethyst:ammolite", version.ref = "quartz" } +security-crypto-ktx = { module = "androidx.security:security-crypto-ktx", version.ref = "securityCryptoKtx" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx b/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx index 6b890e5d..5446ef8a 100644 --- a/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx +++ b/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx @@ -59,14 +59,13 @@ const NotificationsDrawer = ({ const [openSnak, setOpenSnak] = React.useState(false); const [snakEvent, setSnakevent] = React.useState(); const [subscribedTokens, setSubscribedTokens] = React.useState([]); - const [_, setLastNotification] = React.useState( - parseInt( - systemClient.getItem('last_notification') === '' - ? '0' - : (systemClient.getItem('last_notification') ?? '0'), - 10, - ), - ); + const [_, setLastNotification] = React.useState(0); + + useEffect(() => { + systemClient.getItem('last_notification').then((result) => { + setLastNotification(!result || result === '' ? 0 : parseInt(result, 10)); + }); + }, []); useEffect(() => { setShow(false); diff --git a/frontend/src/components/HostAlert/SelfhostedAlert.tsx b/frontend/src/components/HostAlert/SelfhostedAlert.tsx index 32bec75d..fbd268bf 100644 --- a/frontend/src/components/HostAlert/SelfhostedAlert.tsx +++ b/frontend/src/components/HostAlert/SelfhostedAlert.tsx @@ -8,9 +8,9 @@ const SelfhostedAlert = (): React.JSX.Element => { const [show, setShow] = useState(false); useEffect(() => { - if (!systemClient.getItem('selfhosted-alert')) { - setShow(true); - } + systemClient.getItem('selfhosted-alert').then((result) => { + if (result) setShow(true); + }); }, []); // If alert is hidden return null diff --git a/frontend/src/components/HostAlert/UnsafeAlert.tsx b/frontend/src/components/HostAlert/UnsafeAlert.tsx index 0822e29a..7b2f0f16 100644 --- a/frontend/src/components/HostAlert/UnsafeAlert.tsx +++ b/frontend/src/components/HostAlert/UnsafeAlert.tsx @@ -41,9 +41,9 @@ const UnsafeAlert = (): React.JSX.Element => { const [unsafeClient, setUnsafeClient] = useState(false); useEffect(() => { - if (!systemClient.getItem('unsafe-alert')) { - setShow(true); - } + systemClient.getItem('unsafe-alert').then((result) => { + if (result) setShow(true); + }); }, []); const checkClient = (): void => { diff --git a/frontend/src/models/Federation.model.ts b/frontend/src/models/Federation.model.ts index a71e0292..8561ea14 100644 --- a/frontend/src/models/Federation.model.ts +++ b/frontend/src/models/Federation.model.ts @@ -8,7 +8,6 @@ import { defaultExchange, } from '.'; import defaultFederation from '../../static/federation.json'; -import { systemClient } from '../services/System'; import { federationLottery, getHost } from '../utils'; import { coordinatorDefaultValues } from './Coordinator.model'; import { updateExchangeInfo } from './Exchange.model'; @@ -98,9 +97,6 @@ export class Federation { } else { void this.loadBook(); } - - const federationUrls = Object.values(this.coordinators).map((c) => c.url); - systemClient.setCookie('federation', JSON.stringify(federationUrls)); }; refreshBookHosts: (robosatsOnly: boolean) => void = (robosatsOnly) => { diff --git a/frontend/src/models/Garage.model.ts b/frontend/src/models/Garage.model.ts index e3188380..c3e35a5e 100644 --- a/frontend/src/models/Garage.model.ts +++ b/frontend/src/models/Garage.model.ts @@ -51,9 +51,9 @@ class Garage { this.triggerHook('onSlotUpdate'); }; - loadSlots = (): void => { + loadSlots = async (): Promise => { this.slots = {}; - const slotsDump: string = systemClient.getItem('garage_slots') ?? ''; + const slotsDump: string = (await systemClient.getItem('garage_slots')) ?? ''; if (slotsDump !== '') { const rawSlots: Record = JSON.parse(slotsDump); diff --git a/frontend/src/models/Settings.default.basic.selfhosted.ts b/frontend/src/models/Settings.default.basic.selfhosted.ts index 9db4c295..174eca1f 100644 --- a/frontend/src/models/Settings.default.basic.selfhosted.ts +++ b/frontend/src/models/Settings.default.basic.selfhosted.ts @@ -1,11 +1,8 @@ -import { systemClient } from '../services/System'; import BaseSettings from './Settings.model'; class SettingsSelfhosted extends BaseSettings { constructor() { super(); - const fontSizeCookie = systemClient.getItem('settings_fontsize_basic'); - this.fontSize = fontSizeCookie !== '' ? Number(fontSizeCookie) : 14; } public frontend: 'basic' | 'pro' = 'basic'; diff --git a/frontend/src/models/Settings.default.basic.ts b/frontend/src/models/Settings.default.basic.ts index dc33f745..1ec64126 100644 --- a/frontend/src/models/Settings.default.basic.ts +++ b/frontend/src/models/Settings.default.basic.ts @@ -1,11 +1,8 @@ -import { systemClient } from '../services/System'; import BaseSettings from './Settings.model'; class Settings extends BaseSettings { constructor() { super(); - const fontSizeCookie = systemClient.getItem('settings_fontsize_basic'); - this.fontSize = fontSizeCookie !== '' ? Number(fontSizeCookie) : 14; } public frontend: 'basic' | 'pro' = 'basic'; diff --git a/frontend/src/models/Settings.default.pro.selfhosted.ts b/frontend/src/models/Settings.default.pro.selfhosted.ts index aca5d300..b688906d 100644 --- a/frontend/src/models/Settings.default.pro.selfhosted.ts +++ b/frontend/src/models/Settings.default.pro.selfhosted.ts @@ -1,11 +1,8 @@ -import { systemClient } from '../services/System'; import BaseSettings from './Settings.model'; class SettingsSelfhostedPro extends BaseSettings { constructor() { super(); - const fontSizeCookie = systemClient.getItem('settings_fontsize_pro'); - this.fontSize = fontSizeCookie !== '' ? Number(fontSizeCookie) : 12; } public frontend: 'basic' | 'pro' = 'pro'; diff --git a/frontend/src/models/Settings.default.pro.ts b/frontend/src/models/Settings.default.pro.ts index 2175406e..1152d7fc 100644 --- a/frontend/src/models/Settings.default.pro.ts +++ b/frontend/src/models/Settings.default.pro.ts @@ -1,11 +1,8 @@ -import { systemClient } from '../services/System'; import BaseSettings from './Settings.model'; class SettingsPro extends BaseSettings { constructor() { super(); - const fontSizeCookie = systemClient.getItem('settings_fontsize_pro'); - this.fontSize = fontSizeCookie !== '' ? Number(fontSizeCookie) : 12; } public frontend: 'basic' | 'pro' = 'pro'; diff --git a/frontend/src/models/Settings.model.ts b/frontend/src/models/Settings.model.ts index 0653a35c..36f30944 100644 --- a/frontend/src/models/Settings.model.ts +++ b/frontend/src/models/Settings.model.ts @@ -25,45 +25,57 @@ export type Language = class BaseSettings { constructor() { - const modeCookie: 'light' | 'dark' | '' = systemClient.getItem('settings_mode'); - this.mode = - modeCookie !== '' - ? modeCookie - : window?.matchMedia('(prefers-color-scheme: dark)')?.matches - ? 'dark' - : 'light'; + const [client] = window.RobosatsSettings.split('-'); + this.client = client as 'web' | 'mobile'; - this.lightQRs = systemClient.getItem('settings_light_qr') === 'true'; - - const languageCookie = systemClient.getItem('settings_language'); - this.language = - languageCookie !== '' - ? languageCookie - : i18n.resolvedLanguage == null - ? 'en' - : i18n.resolvedLanguage.substring(0, 2); - - const connection = systemClient.getItem('settings_connection'); - this.connection = connection && connection !== '' ? connection : this.connection; - - const networkCookie = systemClient.getItem('settings_network'); - this.network = networkCookie && networkCookie !== '' ? networkCookie : this.network; this.host = getHost(); - const [client] = window.RobosatsSettings.split('-'); - this.client = client; + systemClient.getItem('settings_mode').then((mode) => { + this.mode = mode !== '' ? (mode as 'light' | 'dark') : this.getMode(); + }); - const stopNotifications = systemClient.getItem('settings_stop_notifications'); - this.stopNotifications = client === 'mobile' && stopNotifications === 'true'; + systemClient.getItem('settings_fontsize_basic').then((fontSizeCookie) => { + this.fontSize = fontSizeCookie !== '' ? Number(fontSizeCookie) : 14; + }); - const useProxy = systemClient.getItem('settings_use_proxy'); - this.useProxy = client === 'mobile' && useProxy !== 'false'; - apiClient.useProxy = this.useProxy; - websocketClient.useProxy = this.useProxy; + systemClient.getItem('settings_light_qr').then((result) => { + this.lightQRs = result === 'true'; + }); + + systemClient.getItem('settings_language').then((result) => { + this.language = + result !== '' + ? (result as Language) + : i18n.resolvedLanguage == null + ? 'en' + : (i18n.resolvedLanguage.substring(0, 2) as Language); + }); + + systemClient.getItem('settings_connection').then((result) => { + this.connection = result && result !== '' ? (result as 'api' | 'nostr') : this.connection; + }); + + systemClient.getItem('settings_network').then((result) => { + this.network = result && result !== '' ? (result as 'mainnet' | 'testnet') : this.network; + }); + + systemClient.getItem('settings_stop_notifications').then((result) => { + this.stopNotifications = client === 'mobile' && result === 'true'; + }); + + systemClient.getItem('settings_use_proxy').then((result) => { + this.useProxy = client === 'mobile' && result !== 'false'; + apiClient.useProxy = this.useProxy; + websocketClient.useProxy = this.useProxy; + }); } + getMode = (): 'light' | 'dark' => { + return window?.matchMedia('(prefers-color-scheme: dark)')?.matches ? 'dark' : 'light'; + }; + public frontend: 'basic' | 'pro' = 'basic'; - public mode: 'light' | 'dark' = 'light'; + public mode: 'light' | 'dark' = this.getMode(); public client: 'web' | 'mobile' = 'web'; public fontSize: number = 14; public lightQRs: boolean = false; @@ -74,8 +86,8 @@ class BaseSettings { public host?: string; public unsafeClient: boolean = false; public selfhostedClient: boolean = false; - public useProxy: boolean; - public stopNotifications: boolean; + public useProxy: boolean = false; + public stopNotifications: boolean = false; } export default BaseSettings; diff --git a/frontend/src/services/Android/index.ts b/frontend/src/services/Android/index.ts index dc526ded..6b4ea82a 100644 --- a/frontend/src/services/Android/index.ts +++ b/frontend/src/services/Android/index.ts @@ -7,6 +7,9 @@ declare global { } interface AndroidAppRobosats { + getEncryptedStorage: (uuid: string, key: string) => void; + setEncryptedStorage: (uuid: string, key: string, value: string) => void; + deleteEncryptedStorage: (uuid: string, key: string) => void; generateRoboname: (uuid: string, initialString: string) => void; generateRobohash: (uuid: string, initialString: string) => void; copyToClipboard: (value: string) => void; diff --git a/frontend/src/services/System/SystemAndroidClient/index.ts b/frontend/src/services/System/SystemAndroidClient/index.ts index c1ca3303..4a9204a9 100644 --- a/frontend/src/services/System/SystemAndroidClient/index.ts +++ b/frontend/src/services/System/SystemAndroidClient/index.ts @@ -1,5 +1,6 @@ import { type SystemClient } from '..'; import AndroidRobosats from '../../Android'; +import { v4 as uuidv4 } from 'uuid'; class SystemAndroidClient implements SystemClient { constructor() { @@ -13,44 +14,30 @@ class SystemAndroidClient implements SystemClient { window.AndroidAppRobosats?.copyToClipboard(value ?? ''); }; - // Cookies - public getCookie: (key: string) => string = (key) => { - let cookieValue = null; - if (document?.cookie !== '') { - const cookies = document.cookie.split(';'); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the key we want? - if (cookie.substring(0, key.length + 1) === key + '=') { - cookieValue = decodeURIComponent(cookie.substring(key.length + 1)); - break; - } - } - } - - return cookieValue ?? ''; - }; - - public setCookie: (key: string, value: string) => void = (key, value) => { - document.cookie = `${key}=${value};path=/;SameSite=None;Secure`; - }; - - public deleteCookie: (key: string) => void = (key) => { - document.cookie = `${key}= ;path=/; expires = Thu, 01 Jan 1970 00:00:00 GMT`; - }; - // Local storage - public getItem: (key: string) => string = (key) => { - const value = window.localStorage.getItem(key); - return value ?? ''; + public getItem: (key: string) => Promise = async (key) => { + try { + const result = await new Promise((resolve, reject) => { + const uuid: string = uuidv4(); + window.AndroidAppRobosats?.getEncryptedStorage(uuid, key); + window.AndroidRobosats?.storePromise(uuid, resolve, reject); + }); + + return result; + } catch (error) { + console.error('Error generating roboname:', error); + return; + } }; public setItem: (key: string, value: string) => void = (key, value) => { - window.localStorage.setItem(key, value); + const uuid: string = uuidv4(); + window.AndroidAppRobosats?.setEncryptedStorage(uuid, key, value); }; public deleteItem: (key: string) => void = (key) => { - window.localStorage.removeItem(key); + const uuid: string = uuidv4(); + window.AndroidAppRobosats?.deleteEncryptedStorage(uuid, key); }; } diff --git a/frontend/src/services/System/SystemDesktopClient/index.ts b/frontend/src/services/System/SystemDesktopClient/index.ts index 75a8251c..562232ac 100644 --- a/frontend/src/services/System/SystemDesktopClient/index.ts +++ b/frontend/src/services/System/SystemDesktopClient/index.ts @@ -26,34 +26,8 @@ class SystemDesktopClient implements SystemClient { } }; - // Cookies - public getCookie: (key: string) => string = (key) => { - let cookieValue = null; - if (document?.cookie !== '') { - const cookies = document.cookie.split(';'); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the key we want? - if (cookie.substring(0, key.length + 1) === key + '=') { - cookieValue = decodeURIComponent(cookie.substring(key.length + 1)); - break; - } - } - } - - return cookieValue ?? ''; - }; - - public setCookie: (key: string, value: string) => void = (key, value) => { - document.cookie = `${key}=${value};path=/;SameSite=None;Secure`; - }; - - public deleteCookie: (key: string) => void = (key) => { - document.cookie = `${key}= ;path=/; expires = Thu, 01 Jan 1970 00:00:00 GMT`; - }; - // Local storage - public getItem: (key: string) => string = (key) => { + public getItem: (key: string) => Promise = async (key) => { const value = window.sessionStorage.getItem(key); return value ?? ''; }; diff --git a/frontend/src/services/System/SystemWebClient/index.ts b/frontend/src/services/System/SystemWebClient/index.ts index c7ba874d..5a9f3ce2 100644 --- a/frontend/src/services/System/SystemWebClient/index.ts +++ b/frontend/src/services/System/SystemWebClient/index.ts @@ -27,34 +27,8 @@ class SystemWebClient implements SystemClient { } }; - // Cookies - public getCookie: (key: string) => string = (key) => { - let cookieValue = null; - if (document?.cookie !== '') { - const cookies = document.cookie.split(';'); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the key we want? - if (cookie.substring(0, key.length + 1) === key + '=') { - cookieValue = decodeURIComponent(cookie.substring(key.length + 1)); - break; - } - } - } - - return cookieValue ?? ''; - }; - - public setCookie: (key: string, value: string) => void = (key, value) => { - document.cookie = `${key}=${value};path=/;SameSite=None;Secure`; - }; - - public deleteCookie: (key: string) => void = (key) => { - document.cookie = `${key}= ;path=/; expires = Thu, 01 Jan 1970 00:00:00 GMT`; - }; - // Local storage - public getItem: (key: string) => string = (key) => { + public getItem: (key: string) => Promise = async (key) => { const value = window.localStorage.getItem(key); return value ?? ''; }; diff --git a/frontend/src/services/System/index.ts b/frontend/src/services/System/index.ts index 9caf6489..62e6454e 100644 --- a/frontend/src/services/System/index.ts +++ b/frontend/src/services/System/index.ts @@ -5,10 +5,7 @@ import SystemAndroidClient from './SystemAndroidClient'; export interface SystemClient { loading: boolean; copyToClipboard: (value: string) => void; - getCookie: (key: string) => string | undefined; - setCookie: (key: string, value: string) => void; - deleteCookie: (key: string) => void; - getItem: (key: string) => string | undefined; + getItem: (key: string) => Promise; setItem: (key: string, value: string) => void; deleteItem: (key: string) => void; } diff --git a/frontend/src/services/api/ApiAndroidClient/index.ts b/frontend/src/services/api/ApiAndroidClient/index.ts index 7b2ca816..fc138026 100644 --- a/frontend/src/services/api/ApiAndroidClient/index.ts +++ b/frontend/src/services/api/ApiAndroidClient/index.ts @@ -1,5 +1,4 @@ import { type ApiClient, type Auth } from '..'; -import { systemClient } from '../../System'; import ApiWebClient from '../ApiWebClient'; import { v4 as uuidv4 } from 'uuid'; @@ -32,14 +31,8 @@ class ApiAndroidClient implements ApiClient { return headers; }; - private readonly parseResponse = (response: Record): object => { - if (response.headers['set-cookie'] != null) { - response.headers['set-cookie'].forEach((cookie: string) => { - const keySplit: string[] = cookie.split('='); - systemClient.setCookie(keySplit[0], keySplit[1].split(';')[0]); - }); - } - return response.json; + private readonly parseResponse = (response: string): object => { + return JSON.parse(response).json; }; public put: (baseUrl: string, path: string, body: object) => Promise = async ( @@ -64,7 +57,7 @@ class ApiAndroidClient implements ApiClient { window.AndroidRobosats?.storePromise(uuid, resolve, reject); }); - return this.parseResponse(JSON.parse(result)); + return this.parseResponse(result); }; public post: ( @@ -84,7 +77,7 @@ class ApiAndroidClient implements ApiClient { window.AndroidRobosats?.storePromise(uuid, resolve, reject); }); - return this.parseResponse(JSON.parse(result)); + return this.parseResponse(result); }; public get: (baseUrl: string, path: string, auth?: Auth) => Promise = async ( @@ -102,7 +95,7 @@ class ApiAndroidClient implements ApiClient { window.AndroidRobosats?.storePromise(uuid, resolve, reject); }); - return this.parseResponse(JSON.parse(result)); + return this.parseResponse(result); }; } diff --git a/frontend/templates/frontend/index.ejs b/frontend/templates/frontend/index.ejs index e18ed902..5fca6f57 100644 --- a/frontend/templates/frontend/index.ejs +++ b/frontend/templates/frontend/index.ejs @@ -1,103 +1,119 @@ - - - <% if (!mobile) { %> - - - - + + + + <% if (!mobile) { %> + + + + <% } %> - - - + + + - <% if (pro) { %> - RoboSats PRO - Simple and Private Bitcoin Exchange - - - - <% } else { %> - RoboSats - Simple and Private Bitcoin Exchange - - <% } %> + <% if (pro) { %> + RoboSats PRO - Simple and Private Bitcoin Exchange + + + + <% } else { %> + RoboSats - Simple and Private Bitcoin Exchange + + <% } %> - - - + + + - - - - - + + + + + + + +
+
+
+
+
+
+
+
    +
  • +
    Looking for robot parts ...
    +
  • +
  • +
    Adding layers to the onion ...
    +
  • +
  • +
    Winning at game theory ...
    +
  • +
  • +
    Moving Sats at light speed ...
    +
  • +
  • +
    Hiding in 2^256 bits of entropy...
    +
  • +
+
+
- - - + + + + \ No newline at end of file