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