add settings page with reset app: clears keychain secrets and SQLite cache

This commit is contained in:
2026-05-18 04:04:24 +08:00
parent fc81e0889a
commit 9c856129d8
5 changed files with 117 additions and 0 deletions
+29
View File
@@ -28,6 +28,13 @@ import {
MoneroWalletManager, MoneroWalletManager,
} from './lib/monero' } from './lib/monero'
import { canPromptTouchID, promptTouchID } from './lib/biometric' import { canPromptTouchID, promptTouchID } from './lib/biometric'
import { sqlite } from './lib/sqlite'
import {
tokenMetadata,
transactions,
transactionReceipts,
txHistory,
} from './lib/db/schema'
const env = getENV() const env = getENV()
@@ -134,6 +141,10 @@ type TxEntry = {
type RPC = { type RPC = {
bun: RPCSchema<{ bun: RPCSchema<{
requests: { requests: {
resetApp: {
params: {}
response: boolean
}
biometricCanAuth: { biometricCanAuth: {
params: {} params: {}
response: boolean response: boolean
@@ -366,6 +377,24 @@ const rpc = BrowserView.defineRPC<RPC>({
maxRequestTime: 120000, maxRequestTime: 120000,
handlers: { handlers: {
requests: { requests: {
resetApp: async () => {
try {
await Promise.all([
Bun.secrets.delete({ service: 'koins', name: 'vault' }),
Bun.secrets.delete({ service: 'koins', name: 'vault_password' }),
Bun.secrets.delete({ service: 'koins', name: 'alchemy_key' }),
])
sqlite.run('DELETE FROM token_metadata')
sqlite.run('DELETE FROM transactions')
sqlite.run('DELETE FROM transaction_receipts')
sqlite.run('DELETE FROM tx_history')
console.log('[rpc] resetApp complete')
return true
} catch (e) {
console.log('[rpc] resetApp error:', e)
return false
}
},
biometricCanAuth: async () => { biometricCanAuth: async () => {
try { try {
return canPromptTouchID() return canPromptTouchID()
@@ -14,6 +14,7 @@
import ArrowDown from '@lucide/svelte/icons/arrow-down' import ArrowDown from '@lucide/svelte/icons/arrow-down'
import ArrowUp from '@lucide/svelte/icons/arrow-up' import ArrowUp from '@lucide/svelte/icons/arrow-up'
import QrCodeIcon from '@lucide/svelte/icons/qr-code' import QrCodeIcon from '@lucide/svelte/icons/qr-code'
import SettingsIcon from '@lucide/svelte/icons/settings'
import Fingerprint from '@lucide/svelte/icons/fingerprint' import Fingerprint from '@lucide/svelte/icons/fingerprint'
import * as Dialog from '$lib/components/ui/dialog/index.js' import * as Dialog from '$lib/components/ui/dialog/index.js'
import QRCode from 'qrcode' import QRCode from 'qrcode'
@@ -252,6 +253,9 @@
<Button onclick={() => w.refresh()} disabled={w.loading}> <Button onclick={() => w.refresh()} disabled={w.loading}>
{w.loading ? 'Refreshing...' : 'Refresh'} {w.loading ? 'Refreshing...' : 'Refresh'}
</Button> </Button>
<Button variant="outline" onclick={() => navigate('/settings')}>
<SettingsIcon size={16} />
</Button>
<Button variant="outline" onclick={() => w.lock()}> <Button variant="outline" onclick={() => w.lock()}>
Lock Lock
</Button> </Button>
+4
View File
@@ -92,6 +92,10 @@ export type TokenBalanceResult = {
type RPC = { type RPC = {
bun: { bun: {
requests: { requests: {
resetApp: {
params: {}
response: boolean
}
biometricCanAuth: { biometricCanAuth: {
params: {} params: {}
response: boolean response: boolean
@@ -199,6 +199,24 @@ export const Wallet = () => {
} }
} }
const resetApp = async () => {
if (!electrobun.rpc) return false
const [ok] = await tryCatch(
electrobun.rpc.request.resetApp({}),
)
if (!ok) return false
seed = ''
address = ''
balance = '0'
error = ''
tokenBalances = []
transactions = []
apiKey = ''
vaultExists = false
passwordHash = ''
return true
}
const setPassword = async (password: string) => { const setPassword = async (password: string) => {
if (!electrobun.rpc) return if (!electrobun.rpc) return
const ph = await hashPassword(password) const ph = await hashPassword(password)
@@ -473,6 +491,7 @@ export const Wallet = () => {
unlockWithBiometrics, unlockWithBiometrics,
unlockWithPassword, unlockWithPassword,
setPassword, setPassword,
resetApp,
saveVault, saveVault,
switchNetwork, switchNetwork,
saveApiKey, saveApiKey,
+61
View File
@@ -0,0 +1,61 @@
<script lang="ts">
import { wallet } from '$lib/states/wallet.svelte.js'
import { Button } from '$lib/components/ui/button/index.js'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '$lib/components/ui/card/index.js'
import { navigate } from 'sv-router/generated'
const w = wallet
let confirm = $state(false)
let resetting = $state(false)
async function handleReset() {
resetting = true
const ok = await w.resetApp()
if (ok) navigate('/')
resetting = false
confirm = false
}
</script>
<div class="mx-auto mt-16 max-w-md">
<div class="flex flex-col gap-4">
<Card>
<CardHeader>
<CardTitle>Settings</CardTitle>
<CardDescription>App preferences and data management</CardDescription>
</CardHeader>
<CardContent class="space-y-4">
<div>
<p class="font-medium text-xs">Reset App</p>
<p class="text-muted-foreground text-xs mt-1">
Removes your seed, API key, and all cached data from the keychain
and database. You can then set up the wallet fresh.
</p>
<div class="mt-3">
{#if confirm}
<div class="flex gap-2 items-center">
<p class="text-xs text-red-500">Are you sure?</p>
<Button size="sm" variant="destructive" onclick={handleReset} disabled={resetting}>
{resetting ? 'Resetting...' : 'Yes, reset everything'}
</Button>
<Button size="sm" variant="outline" onclick={() => (confirm = false)}>
Cancel
</Button>
</div>
{:else}
<Button variant="outline" onclick={() => (confirm = true)}>
Reset App
</Button>
{/if}
</div>
</div>
</CardContent>
</Card>
</div>
</div>