1#[macro_use]
19extern crate tracing;
20
21pub mod blocking;
22pub mod io;
23pub mod ipc;
24pub mod macros;
25pub mod model;
26pub mod search;
27pub mod service;
28pub mod subscribers;
29#[cfg(target_family = "wasm")]
30pub mod wasm;
31
32#[derive(Clone)]
33pub struct Lb {
34 pub local: Arc<OnceLock<LocalLb>>,
35 pub remote: Option<Arc<RemoteLb>>,
36 pub config: Config,
37}
38
39#[derive(Clone)]
40pub struct LocalLb {
41 pub config: Config,
42 pub user_last_seen: Arc<RwLock<Instant>>,
43 pub keychain: Keychain,
44 pub db: LbDb,
45 pub docs: AsyncDocs,
46 pub client: Network,
47 pub events: EventSubs,
48 pub status: StatusUpdater,
49 pub syncer: Syncer,
50}
51
52impl LocalLb {
53 #[instrument(level = "info", skip_all, err(Debug))]
54 pub async fn init(config: Config) -> LbResult<Self> {
55 let docs = AsyncDocs::from(&config);
56 let db_cfg = db_rs::Config::in_folder(&config.writeable_path);
57 #[cfg(target_os = "ios")]
59 let db_cfg = db_rs::Config { fs_locks: false, ..db_cfg };
60 let db = CoreDb::init(db_cfg).map_err(|err| LbErrKind::Unexpected(format!("{err:#?}")))?;
61 let keychain = Keychain::from(db.account.get());
62 let db = Arc::new(RwLock::new(db));
63 let client = Network { client_type: config.client_type, ..Network::default() };
64
65 let status = StatusUpdater::default();
66 let syncer = Default::default();
67 let events = EventSubs::default();
68 let user_last_seen = Arc::new(RwLock::new(Instant::now()));
69
70 let result =
71 Self { config, keychain, db, docs, client, syncer, events, status, user_last_seen };
72
73 #[cfg(not(target_family = "wasm"))]
74 {
75 result.setup_syncer();
76 result.setup_status().await?;
77 }
78
79 Ok(result)
80 }
81}
82
83impl Lb {
84 pub async fn init(config: Config) -> LbResult<Self> {
85 let local: Arc<OnceLock<LocalLb>> = Arc::new(OnceLock::new());
86 let init_err = match LocalLb::init(config.clone()).await {
87 Ok(loc) => {
88 logging::init(&loc.config)?;
89 ipc::spawn_host(loc.clone());
90 let _ = local.set(loc);
91 return Ok(Self { local, remote: None, config });
92 }
93 Err(err) => err,
94 };
95 if let Some(remote) = ipc::connect_guest(&config).await {
96 return Ok(Self { local, remote: Some(remote), config });
97 }
98 Err(init_err)
99 }
100}
101
102impl Lb {
103 pub async fn create_account(
104 &self, username: &str, api_url: &str, welcome_doc: bool,
105 ) -> LbResult<Account> {
106 if let Some(local) = self.local.get() {
107 return local.create_account(username, api_url, welcome_doc).await;
108 }
109 let account = self
110 .call::<Account>(Request::CreateAccount {
111 username: username.to_string(),
112 api_url: api_url.to_string(),
113 welcome_doc,
114 })
115 .await?;
116 self.cache_account_on_remote(&account);
117 Ok(account)
118 }
119
120 pub async fn import_account(&self, key: &str, api_url: Option<&str>) -> LbResult<Account> {
121 if let Some(local) = self.local.get() {
122 return local.import_account(key, api_url).await;
123 }
124 let account = self
125 .call::<Account>(Request::ImportAccount {
126 key: key.to_string(),
127 api_url: api_url.map(|s| s.to_string()),
128 })
129 .await?;
130 self.cache_account_on_remote(&account);
131 Ok(account)
132 }
133
134 pub async fn import_account_private_key_v1(&self, account: Account) -> LbResult<Account> {
135 if let Some(local) = self.local.get() {
136 return local.import_account_private_key_v1(account).await;
137 }
138 let account = self
139 .call::<Account>(Request::ImportAccountPrivateKeyV1 { account })
140 .await?;
141 self.cache_account_on_remote(&account);
142 Ok(account)
143 }
144
145 pub async fn import_account_phrase(
146 &self, phrase: [&str; 24], api_url: &str,
147 ) -> LbResult<Account> {
148 if let Some(local) = self.local.get() {
149 return local.import_account_phrase(phrase, api_url).await;
150 }
151 let account = self
152 .call::<Account>(Request::ImportAccountPhrase {
153 phrase: phrase.iter().map(|s| s.to_string()).collect(),
154 api_url: api_url.to_string(),
155 })
156 .await?;
157 self.cache_account_on_remote(&account);
158 Ok(account)
159 }
160
161 fn cache_account_on_remote(&self, account: &Account) {
162 if let Some(remote) = &self.remote {
163 remote.cache_account(account.clone());
164 }
165 }
166
167 pub async fn delete_account(&self) -> LbResult<()> {
168 if let Some(local) = self.local.get() {
169 return local.delete_account().await;
170 }
171 self.call(Request::DeleteAccount).await
172 }
173
174 pub fn get_account(&self) -> LbResult<Account> {
175 if let Some(local) = self.local.get() {
176 return local.get_account().cloned();
177 }
178 self.remote
179 .as_ref()
180 .expect("get_account: remote must be set when local is unset")
181 .get_account()
182 .cloned()
183 }
184
185 pub async fn suggested_docs(&self, settings: RankingWeights) -> LbResult<Vec<Uuid>> {
186 if let Some(local) = self.local.get() {
187 return local.suggested_docs(settings).await;
188 }
189 self.call(Request::SuggestedDocs { settings }).await
190 }
191
192 pub async fn clear_suggested(&self) -> LbResult<()> {
193 if let Some(local) = self.local.get() {
194 return local.clear_suggested().await;
195 }
196 self.call(Request::ClearSuggested).await
197 }
198
199 pub async fn clear_suggested_id(&self, id: Uuid) -> LbResult<()> {
200 if let Some(local) = self.local.get() {
201 return local.clear_suggested_id(id).await;
202 }
203 self.call(Request::ClearSuggestedId { id }).await
204 }
205
206 pub fn app_foregrounded(&self) {
207 if let Some(local) = self.local.get() {
208 local.app_foregrounded();
209 return;
210 }
211 if let Some(remote) = &self.remote {
212 let r = Arc::clone(remote);
213 tokio::spawn(async move {
214 let _ = r.try_call::<()>(Request::AppForegrounded).await;
215 });
216 }
217 }
218
219 pub async fn disappear_account(&self, username: &str) -> LbResult<()> {
220 if let Some(local) = self.local.get() {
221 return local.disappear_account(username).await;
222 }
223 self.call(Request::DisappearAccount { username: username.to_string() })
224 .await
225 }
226
227 pub async fn disappear_file(&self, id: Uuid) -> LbResult<()> {
228 if let Some(local) = self.local.get() {
229 return local.disappear_file(id).await;
230 }
231 self.call(Request::DisappearFile { id }).await
232 }
233
234 pub async fn list_users(&self, filter: Option<AccountFilter>) -> LbResult<Vec<Username>> {
235 if let Some(local) = self.local.get() {
236 return local.list_users(filter).await;
237 }
238 self.call(Request::ListUsers { filter }).await
239 }
240
241 pub async fn get_account_info(&self, identifier: AccountIdentifier) -> LbResult<AccountInfo> {
242 if let Some(local) = self.local.get() {
243 return local.get_account_info(identifier).await;
244 }
245 self.call(Request::GetAccountInfo { identifier }).await
246 }
247
248 pub async fn validate_account(&self, username: &str) -> LbResult<AdminValidateAccount> {
249 if let Some(local) = self.local.get() {
250 return local.validate_account(username).await;
251 }
252 self.call(Request::AdminValidateAccount { username: username.to_string() })
253 .await
254 }
255
256 pub async fn validate_server(&self) -> LbResult<AdminValidateServer> {
257 if let Some(local) = self.local.get() {
258 return local.validate_server().await;
259 }
260 self.call(Request::AdminValidateServer).await
261 }
262
263 pub async fn file_info(&self, id: Uuid) -> LbResult<AdminFileInfoResponse> {
264 if let Some(local) = self.local.get() {
265 return local.file_info(id).await;
266 }
267 self.call(Request::AdminFileInfo { id }).await
268 }
269
270 pub async fn rebuild_index(&self, index: ServerIndex) -> LbResult<()> {
271 if let Some(local) = self.local.get() {
272 return local.rebuild_index(index).await;
273 }
274 self.call(Request::RebuildIndex { index }).await
275 }
276
277 pub async fn set_user_tier(&self, username: &str, info: AdminSetUserTierInfo) -> LbResult<()> {
278 if let Some(local) = self.local.get() {
279 return local.set_user_tier(username, info).await;
280 }
281 self.call(Request::SetUserTier { username: username.to_string(), info })
282 .await
283 }
284
285 pub async fn upgrade_account_stripe(&self, account_tier: StripeAccountTier) -> LbResult<()> {
286 if let Some(local) = self.local.get() {
287 return local.upgrade_account_stripe(account_tier).await;
288 }
289 self.call(Request::UpgradeAccountStripe { account_tier })
290 .await
291 }
292
293 pub async fn upgrade_account_google_play(
294 &self, purchase_token: &str, account_id: &str,
295 ) -> LbResult<()> {
296 if let Some(local) = self.local.get() {
297 return local
298 .upgrade_account_google_play(purchase_token, account_id)
299 .await;
300 }
301 self.call(Request::UpgradeAccountGooglePlay {
302 purchase_token: purchase_token.to_string(),
303 account_id: account_id.to_string(),
304 })
305 .await
306 }
307
308 pub async fn upgrade_account_app_store(
309 &self, original_transaction_id: String, app_account_token: String,
310 ) -> LbResult<()> {
311 if let Some(local) = self.local.get() {
312 return local
313 .upgrade_account_app_store(original_transaction_id, app_account_token)
314 .await;
315 }
316 self.call(Request::UpgradeAccountAppStore { original_transaction_id, app_account_token })
317 .await
318 }
319
320 pub async fn cancel_subscription(&self) -> LbResult<()> {
321 if let Some(local) = self.local.get() {
322 return local.cancel_subscription().await;
323 }
324 self.call(Request::CancelSubscription).await
325 }
326
327 pub async fn get_subscription_info(&self) -> LbResult<Option<SubscriptionInfo>> {
328 if let Some(local) = self.local.get() {
329 return local.get_subscription_info().await;
330 }
331 self.call(Request::GetSubscriptionInfo).await
332 }
333
334 #[cfg(not(target_family = "wasm"))]
335 pub async fn recent_panic(&self) -> LbResult<bool> {
336 if let Some(local) = self.local.get() {
337 return local.recent_panic().await;
338 }
339 self.call(Request::RecentPanic).await
340 }
341
342 #[cfg(not(target_family = "wasm"))]
343 pub async fn write_panic_to_file(&self, error_header: String, bt: String) -> LbResult<String> {
344 if let Some(local) = self.local.get() {
345 return local.write_panic_to_file(error_header, bt).await;
346 }
347 self.call(Request::WritePanicToFile { error_header, bt })
348 .await
349 }
350
351 #[cfg(not(target_family = "wasm"))]
352 pub async fn debug_info(&self, os_info: String, check_docs: bool) -> LbResult<DebugInfo> {
353 if let Some(local) = self.local.get() {
354 return local.debug_info(os_info, check_docs).await;
355 }
356 self.call(Request::DebugInfo { os_info, check_docs }).await
357 }
358
359 pub async fn read_document(
360 &self, id: Uuid, user_activity: bool,
361 ) -> LbResult<DecryptedDocument> {
362 if let Some(local) = self.local.get() {
363 return local.read_document(id, user_activity).await;
364 }
365 self.call(Request::ReadDocument { id, user_activity }).await
366 }
367
368 pub async fn write_document(&self, id: Uuid, content: &[u8]) -> LbResult<()> {
369 if let Some(local) = self.local.get() {
370 return local.write_document(id, content).await;
371 }
372 self.call(Request::WriteDocument { id, content: content.to_vec() })
373 .await
374 }
375
376 pub async fn read_document_with_hmac(
377 &self, id: Uuid, user_activity: bool,
378 ) -> LbResult<(Option<DocumentHmac>, DecryptedDocument)> {
379 if let Some(local) = self.local.get() {
380 return local.read_document_with_hmac(id, user_activity).await;
381 }
382 self.call(Request::ReadDocumentWithHmac { id, user_activity })
383 .await
384 }
385
386 pub async fn safe_write(
387 &self, id: Uuid, old_hmac: Option<DocumentHmac>, content: Vec<u8>,
388 ) -> LbResult<DocumentHmac> {
389 if let Some(local) = self.local.get() {
390 return local.safe_write(id, old_hmac, content).await;
391 }
392 self.call(Request::SafeWrite { id, old_hmac, content })
393 .await
394 }
395
396 pub async fn create_file(
397 &self, name: &str, parent: &Uuid, file_type: FileType,
398 ) -> LbResult<File> {
399 if let Some(local) = self.local.get() {
400 return local.create_file(name, parent, file_type).await;
401 }
402 self.call::<File>(Request::CreateFile {
403 name: name.to_string(),
404 parent: *parent,
405 file_type,
406 })
407 .await
408 }
409
410 pub async fn rename_file(&self, id: &Uuid, new_name: &str) -> LbResult<()> {
411 if let Some(local) = self.local.get() {
412 return local.rename_file(id, new_name).await;
413 }
414 self.call(Request::RenameFile { id: *id, new_name: new_name.to_string() })
415 .await
416 }
417
418 pub async fn move_file(&self, id: &Uuid, new_parent: &Uuid) -> LbResult<()> {
419 if let Some(local) = self.local.get() {
420 return local.move_file(id, new_parent).await;
421 }
422 self.call(Request::MoveFile { id: *id, new_parent: *new_parent })
423 .await
424 }
425
426 pub async fn delete(&self, id: &Uuid) -> LbResult<()> {
427 if let Some(local) = self.local.get() {
428 return local.delete(id).await;
429 }
430 self.call(Request::Delete { id: *id }).await
431 }
432
433 pub async fn root(&self) -> LbResult<File> {
434 if let Some(local) = self.local.get() {
435 return local.root().await;
436 }
437 self.call(Request::Root).await
438 }
439
440 pub async fn list_metadatas(&self) -> LbResult<Vec<File>> {
441 if let Some(local) = self.local.get() {
442 return local.list_metadatas().await;
443 }
444 self.call(Request::ListMetadatas).await
445 }
446
447 pub async fn get_children(&self, id: &Uuid) -> LbResult<Vec<File>> {
448 if let Some(local) = self.local.get() {
449 return local.get_children(id).await;
450 }
451 self.call(Request::GetChildren { id: *id }).await
452 }
453
454 pub async fn get_and_get_children_recursively(&self, id: &Uuid) -> LbResult<Vec<File>> {
455 if let Some(local) = self.local.get() {
456 return local.get_and_get_children_recursively(id).await;
457 }
458 self.call(Request::GetAndGetChildrenRecursively { id: *id })
459 .await
460 }
461
462 pub async fn get_file_by_id(&self, id: Uuid) -> LbResult<File> {
463 if let Some(local) = self.local.get() {
464 return local.get_file_by_id(id).await;
465 }
466 self.call(Request::GetFileById { id }).await
467 }
468
469 pub async fn get_file_link_url(&self, id: Uuid) -> LbResult<String> {
470 if let Some(local) = self.local.get() {
471 return local.get_file_link_url(id).await;
472 }
473 self.call(Request::GetFileLinkUrl { id }).await
474 }
475
476 pub async fn local_changes(&self) -> Vec<Uuid> {
477 if let Some(local) = self.local.get() {
478 return local.local_changes().await;
479 }
480 self.call::<_>(Request::LocalChanges)
481 .await
482 .unwrap_or_default()
483 }
484
485 pub async fn test_repo_integrity(&self, check_docs: bool) -> LbResult<Vec<Warning>> {
486 if let Some(local) = self.local.get() {
487 return local.test_repo_integrity(check_docs).await;
488 }
489 self.call(Request::TestRepoIntegrity { check_docs }).await
490 }
491
492 pub async fn create_link_at_path(&self, path: &str, target_id: Uuid) -> LbResult<File> {
493 if let Some(local) = self.local.get() {
494 return local.create_link_at_path(path, target_id).await;
495 }
496 self.call(Request::CreateLinkAtPath { path: path.to_string(), target_id })
497 .await
498 }
499
500 pub async fn create_at_path(&self, path: &str) -> LbResult<File> {
501 if let Some(local) = self.local.get() {
502 return local.create_at_path(path).await;
503 }
504 self.call(Request::CreateAtPath { path: path.to_string() })
505 .await
506 }
507
508 pub async fn get_by_path(&self, path: &str) -> LbResult<File> {
509 if let Some(local) = self.local.get() {
510 return local.get_by_path(path).await;
511 }
512 self.call(Request::GetByPath { path: path.to_string() })
513 .await
514 }
515
516 pub async fn get_path_by_id(&self, id: Uuid) -> LbResult<String> {
517 if let Some(local) = self.local.get() {
518 return local.get_path_by_id(id).await;
519 }
520 self.call(Request::GetPathById { id }).await
521 }
522
523 pub async fn list_paths(&self, filter: Option<Filter>) -> LbResult<Vec<String>> {
524 if let Some(local) = self.local.get() {
525 return local.list_paths(filter).await;
526 }
527 self.call(Request::ListPaths { filter }).await
528 }
529
530 pub async fn list_paths_with_ids(
531 &self, filter: Option<Filter>,
532 ) -> LbResult<Vec<(Uuid, String)>> {
533 if let Some(local) = self.local.get() {
534 return local.list_paths_with_ids(filter).await;
535 }
536 self.call(Request::ListPathsWithIds { filter }).await
537 }
538
539 pub async fn share_file(&self, id: Uuid, username: &str, mode: ShareMode) -> LbResult<()> {
540 if let Some(local) = self.local.get() {
541 return local.share_file(id, username, mode).await;
542 }
543 self.call(Request::ShareFile { id, username: username.to_string(), mode })
544 .await
545 }
546
547 pub async fn get_pending_shares(&self) -> LbResult<Vec<File>> {
548 if let Some(local) = self.local.get() {
549 return local.get_pending_shares().await;
550 }
551 self.call(Request::GetPendingShares).await
552 }
553
554 pub async fn get_pending_share_files(&self) -> LbResult<Vec<File>> {
555 if let Some(local) = self.local.get() {
556 return local.get_pending_share_files().await;
557 }
558 self.call(Request::GetPendingShareFiles).await
559 }
560
561 pub async fn known_usernames(&self) -> LbResult<Vec<String>> {
562 if let Some(local) = self.local.get() {
563 return local.known_usernames().await;
564 }
565 self.call(Request::KnownUsernames).await
566 }
567
568 pub async fn reject_share(&self, id: &Uuid) -> LbResult<()> {
569 if let Some(local) = self.local.get() {
570 return local.reject_share(id).await;
571 }
572 self.call(Request::RejectShare { id: *id }).await
573 }
574
575 pub async fn pin_file(&self, id: Uuid) -> LbResult<()> {
576 if let Some(local) = self.local.get() {
577 return local.pin_file(id).await;
578 }
579 self.call(Request::PinFile { id }).await
580 }
581
582 pub async fn unpin_file(&self, id: Uuid) -> LbResult<()> {
583 if let Some(local) = self.local.get() {
584 return local.unpin_file(id).await;
585 }
586 self.call(Request::UnpinFile { id }).await
587 }
588
589 pub async fn list_pinned(&self) -> LbResult<Vec<Uuid>> {
590 if let Some(local) = self.local.get() {
591 return local.list_pinned().await;
592 }
593 self.call(Request::ListPinned).await
594 }
595
596 pub async fn get_usage(&self) -> LbResult<UsageMetrics> {
597 if let Some(local) = self.local.get() {
598 return local.get_usage().await;
599 }
600 self.call(Request::GetUsage).await
601 }
602
603 pub async fn sync(&self) -> LbResult<()> {
604 if let Some(local) = self.local.get() {
605 return local.sync().await;
606 }
607 self.call(Request::Sync).await
608 }
609
610 pub async fn status(&self) -> Status {
611 if let Some(local) = self.local.get() {
612 return local.status().await;
613 }
614 self.call::<_>(Request::Status).await.unwrap_or_default()
615 }
616
617 pub async fn get_last_synced(&self) -> LbResult<i64> {
618 if let Some(local) = self.local.get() {
619 return local.get_last_synced().await;
620 }
621 self.call(Request::GetLastSynced).await
622 }
623
624 pub async fn get_last_synced_human(&self) -> LbResult<String> {
625 if let Some(local) = self.local.get() {
626 return local.get_last_synced_human().await;
627 }
628 self.call(Request::GetLastSyncedHuman).await
629 }
630
631 pub fn config(&self) -> &Config {
632 &self.config
633 }
634
635 pub fn subscribe(&self) -> tokio::sync::broadcast::Receiver<service::events::Event> {
636 if let Some(local) = self.local.get() {
637 return local.subscribe();
638 }
639 self.remote
640 .as_ref()
641 .expect("subscribe: remote must be set when local is unset")
642 .subscribe()
643 }
644
645 pub fn get_timestamp_human_string(&self, timestamp: i64) -> String {
646 use basic_human_duration::ChronoHumanDuration;
647 if timestamp != 0 {
648 time::Duration::milliseconds(crate::model::clock::get_time().0 - timestamp)
649 .format_human()
650 .to_string()
651 } else {
652 "never".to_string()
653 }
654 }
655}
656pub fn get_code_version() -> &'static str {
657 env!("CARGO_PKG_VERSION")
658}
659
660pub static DEFAULT_API_LOCATION: &str = "https://app.lockbook.net";
661pub static CORE_CODE_VERSION: &str = env!("CARGO_PKG_VERSION");
662
663use crate::io::CoreDb;
664use crate::ipc::client::RemoteLb;
665use crate::subscribers::syncer::Syncer;
666use db_rs::Db;
667
668use crate::service::logging;
669use io::LbDb;
670use io::docs::AsyncDocs;
671use io::network::Network;
672use model::core_config::Config;
673pub use model::errors::{LbErrKind, LbResult};
674use service::events::EventSubs;
675use service::keychain::Keychain;
676use std::sync::{Arc, OnceLock};
677use subscribers::status::StatusUpdater;
678use tokio::sync::RwLock;
679pub use uuid::Uuid;
680use web_time::Instant;
681
682use crate::ipc::protocol::Request;
683use crate::model::account::{Account, Username};
684use crate::model::api::{
685 AccountFilter, AccountIdentifier, AccountInfo, AdminFileInfoResponse, AdminSetUserTierInfo,
686 AdminValidateAccount, AdminValidateServer, ServerIndex, StripeAccountTier, SubscriptionInfo,
687};
688use crate::model::crypto::DecryptedDocument;
689use crate::model::errors::Warning;
690use crate::model::file::{File, ShareMode};
691use crate::model::file_metadata::{DocumentHmac, FileType};
692use crate::model::path_ops::Filter;
693use crate::service::activity::RankingWeights;
694#[cfg(not(target_family = "wasm"))]
695use crate::service::debug::DebugInfo;
696use crate::service::usage::UsageMetrics;
697use crate::subscribers::status::Status;