Skip to content

Commit 8e98909

Browse files
authored
Merge pull request #369 from dkackman/startup-error-handling
Start up error handling
2 parents 1081d25 + d5962ec commit 8e98909

File tree

12 files changed

+78
-26
lines changed

12 files changed

+78
-26
lines changed

crates/sage-api/src/types/error_kind.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ pub enum ErrorKind {
99
NotFound,
1010
Unauthorized,
1111
Internal,
12+
DatabaseMigration,
1213
Nfc,
1314
}

crates/sage-cli/src/rpc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl_endpoints! {
2323
Self::Start => {
2424
let mut sage = Sage::new(&path);
2525
let mut receiver = sage.initialize().await?;
26+
sage.switch_wallet().await?;
2627
tokio::spawn(async move { while let Some(_message) = receiver.recv().await {} });
2728
start_rpc(Arc::new(Mutex::new(sage))).await
2829
},

crates/sage-rpc/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ where
4545
ErrorKind::Api => StatusCode::BAD_REQUEST,
4646
ErrorKind::NotFound => StatusCode::NOT_FOUND,
4747
ErrorKind::Unauthorized => StatusCode::UNAUTHORIZED,
48-
ErrorKind::Wallet | ErrorKind::Internal | ErrorKind::Nfc => {
49-
StatusCode::INTERNAL_SERVER_ERROR
50-
}
48+
ErrorKind::DatabaseMigration
49+
| ErrorKind::Wallet
50+
| ErrorKind::Internal
51+
| ErrorKind::Nfc => StatusCode::INTERNAL_SERVER_ERROR,
5152
};
5253
(status, error.to_string()).into_response()
5354
}

crates/sage/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,11 @@ impl Error {
224224
| KeychainError::Bip39(..)
225225
| KeychainError::Argon2(..) => ErrorKind::Internal,
226226
},
227+
Self::SqlxMigration(..) => ErrorKind::DatabaseMigration,
227228
Self::Send(..)
228229
| Self::Io(..)
229230
| Self::Client(..)
230231
| Self::Sqlx(..)
231-
| Self::SqlxMigration(..)
232232
| Self::Bip39(..)
233233
| Self::TomlDe(..)
234234
| Self::TomlSer(..)

crates/sage/src/sage.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ impl Sage {
6969
self.setup_logging()?;
7070

7171
let receiver = self.setup_sync_manager()?;
72-
self.switch_wallet().await?;
7372
self.setup_peers().await?;
7473

7574
info!("Sage wallet initialized");

src-tauri/src/commands.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,13 @@ pub async fn set_rpc_run_on_startup(
146146
Ok(())
147147
}
148148

149+
#[command]
150+
#[specta]
151+
pub async fn switch_wallet(state: State<'_, AppState>) -> Result<()> {
152+
state.lock().await.switch_wallet().await?;
153+
Ok(())
154+
}
155+
149156
#[command]
150157
#[specta]
151158
pub async fn move_key(state: State<'_, AppState>, fingerprint: u32, index: u32) -> Result<()> {

src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub fn run() {
114114
commands::stop_rpc_server,
115115
commands::get_rpc_run_on_startup,
116116
commands::set_rpc_run_on_startup,
117+
commands::switch_wallet,
117118
commands::move_key,
118119
commands::download_cni_offercode,
119120
])

src/bindings.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ async getRpcRunOnStartup() : Promise<boolean> {
278278
async setRpcRunOnStartup(runOnStartup: boolean) : Promise<null> {
279279
return await TAURI_INVOKE("set_rpc_run_on_startup", { runOnStartup });
280280
},
281+
async switchWallet() : Promise<null> {
282+
return await TAURI_INVOKE("switch_wallet");
283+
},
281284
async moveKey(fingerprint: number, index: number) : Promise<null> {
282285
return await TAURI_INVOKE("move_key", { fingerprint, index });
283286
},
@@ -363,7 +366,7 @@ export type DerivationMode = { mode: "default" } |
363366
export type DerivationRecord = { index: number; public_key: string; address: string }
364367
export type DidRecord = { launcher_id: string; name: string | null; visible: boolean; coin_id: string; address: string; amount: Amount; recovery_hash: string | null; created_height: number | null; create_transaction_id: string | null }
365368
export type Error = { kind: ErrorKind; reason: string }
366-
export type ErrorKind = "wallet" | "api" | "not_found" | "unauthorized" | "internal" | "nfc"
369+
export type ErrorKind = "wallet" | "api" | "not_found" | "unauthorized" | "internal" | "database_migration" | "nfc"
367370
export type FilterUnlockedCoins = { coin_ids: string[] }
368371
export type FilterUnlockedCoinsResponse = { coin_ids: string[] }
369372
export type GenerateMnemonic = { use_24_words: boolean }

src/contexts/ErrorContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export default function ErrorDialog({ error, setError }: ErrorDialogProps) {
110110
<DialogContent>
111111
<DialogHeader>
112112
<DialogTitle>{kind ? `${kind} ` : ''}Error</DialogTitle>
113-
<DialogDescription className='break-all'>
113+
<DialogDescription className='break-words'>
114114
{error?.reason}
115115
</DialogDescription>
116116
</DialogHeader>

src/hooks/useInitialization.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
import { commands } from '@/bindings';
22
import { useCallback, useEffect, useState } from 'react';
33
import { useErrors } from './useErrors';
4+
import { logoutAndUpdateState } from '@/state';
45

56
export default function useInitialization() {
67
const { addError } = useErrors();
78

89
const [initialized, setInitialized] = useState(false);
910

10-
const initialize = useCallback(async () => {
11-
commands
12-
.initialize()
13-
.then(() => setInitialized(true))
14-
.catch(addError);
11+
const onInitialize = useCallback(async () => {
12+
try {
13+
await commands.initialize();
14+
setInitialized(true);
15+
await commands.switchWallet();
16+
} catch (error: any) {
17+
// Always add the error to be displayed
18+
addError(error);
19+
20+
// Check if this is a database migration, which is recoverable
21+
if (error.kind === 'database_migration') {
22+
try {
23+
await logoutAndUpdateState();
24+
} catch (logoutError) {
25+
console.error('Error during logout:', logoutError);
26+
}
27+
} else {
28+
console.error('Unrecoverable initialization error', error);
29+
}
30+
}
1531
}, [addError]);
1632

1733
useEffect(() => {
1834
if (!initialized) {
19-
initialize();
35+
onInitialize();
2036
}
21-
}, [initialized, initialize]);
37+
}, [initialized, onInitialize]);
2238

2339
return initialized;
2440
}

src/pages/Login.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,14 @@ export default function Login() {
9191
useEffect(() => {
9292
commands
9393
.getKey({})
94-
.then((data) => data.key !== null && navigate('/wallet'))
95-
.catch(addError);
94+
.then((data) => {
95+
if (data.key !== null) {
96+
navigate('/wallet');
97+
}
98+
})
99+
.catch((error) => {
100+
addError(error);
101+
});
96102
}, [navigate, addError]);
97103

98104
const [activeId, setActiveId] = useState<UniqueIdentifier | null>(null);
@@ -333,13 +339,17 @@ function WalletItem({ draggable, info, keys, setKeys }: WalletItemProps) {
333339
const loginSelf = (explicit: boolean) => {
334340
if (isMenuOpen && !explicit) return;
335341

336-
loginAndUpdateState(info.fingerprint).then(() => {
337-
commands
338-
.getKey({})
339-
.then((data) => setWallet(data.key))
340-
.then(() => navigate('/wallet'))
341-
.catch(addError);
342-
});
342+
loginAndUpdateState(info.fingerprint, addError)
343+
.then(() => {
344+
commands
345+
.getKey({})
346+
.then((data) => setWallet(data.key))
347+
.then(() => navigate('/wallet'))
348+
.catch(addError);
349+
})
350+
.catch((error) => {
351+
addError(error);
352+
});
343353
};
344354

345355
useEffect(() => {

src/state.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
GetSyncStatusResponse,
77
KeyInfo,
88
} from './bindings';
9+
import { CustomError } from './contexts/ErrorContext';
910

1011
export interface WalletState {
1112
sync: GetSyncStatusResponse;
@@ -85,9 +86,21 @@ events.syncEvent.listen((event) => {
8586
}
8687
});
8788

88-
export async function loginAndUpdateState(fingerprint: number): Promise<void> {
89-
await commands.login({ fingerprint });
90-
await fetchState();
89+
export async function loginAndUpdateState(
90+
fingerprint: number,
91+
onError?: (error: CustomError) => void,
92+
): Promise<void> {
93+
try {
94+
await commands.login({ fingerprint });
95+
await fetchState();
96+
} catch (error) {
97+
if (onError) {
98+
onError(error as CustomError);
99+
} else {
100+
console.error(error);
101+
}
102+
throw error;
103+
}
91104
}
92105

93106
// Create a separate function to handle wallet state updates

0 commit comments

Comments
 (0)