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