Skip to main content

lb_rs/service/
file.rs

1use crate::LocalLb;
2use crate::model::access_info::UserAccessMode;
3use crate::model::errors::{LbErrKind, LbResult};
4use crate::model::file::File;
5use crate::model::file_like::FileLike;
6use crate::model::file_metadata::{FileType, Owner};
7use crate::model::filename::MAX_FILENAME_LENGTH;
8use crate::model::symkey;
9use crate::model::tree_like::TreeLike;
10use crate::service::events::Actor;
11use std::iter;
12use uuid::Uuid;
13
14impl LocalLb {
15    #[instrument(level = "debug", skip(self), err(Debug))]
16    pub async fn create_file(
17        &self, name: &str, parent: &Uuid, file_type: FileType,
18    ) -> LbResult<File> {
19        let mut tx = self.begin_tx().await;
20        let db = tx.db();
21
22        // todo this is checked later and probably can be removed
23        if name.len() > MAX_FILENAME_LENGTH {
24            return Err(LbErrKind::FileNameTooLong.into());
25        }
26        let mut tree = (&db.base_metadata)
27            .to_staged(&mut db.local_metadata)
28            .to_lazy();
29
30        let id = tree.create(
31            Uuid::new_v4(),
32            symkey::generate_key(),
33            parent,
34            name,
35            file_type,
36            &self.keychain,
37        )?;
38
39        let ui_file = tree.decrypt(&self.keychain, &id, &db.pub_key_lookup)?;
40
41        tx.end();
42
43        self.events.meta_changed(Actor::User);
44        Ok(ui_file)
45    }
46
47    #[instrument(level = "debug", skip(self), err(Debug))]
48    pub async fn rename_file(&self, id: &Uuid, new_name: &str) -> LbResult<()> {
49        let mut tx = self.begin_tx().await;
50        let db = tx.db();
51
52        if new_name.len() > MAX_FILENAME_LENGTH {
53            return Err(LbErrKind::FileNameTooLong.into());
54        }
55        let mut tree = (&db.base_metadata)
56            .to_staged(&mut db.local_metadata)
57            .to_lazy();
58
59        let id = &tree.linked_by(id)?.unwrap_or(*id);
60
61        tree.rename(id, new_name, &self.keychain)?;
62
63        tx.end();
64
65        self.events.meta_changed(Actor::User);
66
67        Ok(())
68    }
69
70    #[instrument(level = "debug", skip(self), err(Debug))]
71    pub async fn move_file(&self, id: &Uuid, new_parent: &Uuid) -> LbResult<()> {
72        let mut tx = self.begin_tx().await;
73        let db = tx.db();
74
75        let mut tree = (&db.base_metadata)
76            .to_staged(&mut db.local_metadata)
77            .to_lazy();
78
79        let id = &tree.linked_by(id)?.unwrap_or(*id);
80
81        tree.move_file(id, new_parent, &self.keychain)?;
82        tx.end();
83
84        self.events.meta_changed(Actor::User);
85
86        Ok(())
87    }
88
89    #[instrument(level = "debug", skip(self), err(Debug))]
90    pub async fn delete(&self, id: &Uuid) -> LbResult<()> {
91        let mut tx = self.begin_tx().await;
92        let db = tx.db();
93
94        let mut tree = (&db.base_metadata)
95            .to_staged(&mut db.local_metadata)
96            .to_lazy();
97
98        let id = &tree.linked_by(id)?.unwrap_or(*id);
99
100        tree.delete(id, &self.keychain)?;
101
102        tx.end();
103
104        self.events.meta_changed(Actor::User);
105
106        Ok(())
107    }
108
109    // todo: keychain?
110    #[instrument(level = "debug", skip(self), err(Debug))]
111    pub async fn root(&self) -> LbResult<File> {
112        let tx = self.ro_tx().await;
113        let db = tx.db();
114
115        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
116
117        let root_id = db.root.get().ok_or(LbErrKind::RootNonexistent)?;
118
119        let root = tree.decrypt(&self.keychain, root_id, &db.pub_key_lookup)?;
120
121        Ok(root)
122    }
123
124    #[instrument(level = "debug", skip(self), err(Debug))]
125    pub async fn list_metadatas(&self) -> LbResult<Vec<File>> {
126        let tx = self.ro_tx().await;
127        let db = tx.db();
128
129        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
130
131        let ids = tree.ids().into_iter();
132
133        tree.decrypt_all(&self.keychain, ids, &db.pub_key_lookup, true)
134    }
135
136    #[instrument(level = "debug", skip(self), err(Debug))]
137    pub async fn get_children(&self, id: &Uuid) -> LbResult<Vec<File>> {
138        let tx = self.ro_tx().await;
139        let db = tx.db();
140
141        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
142
143        let ids = tree.children_using_links(id)?.into_iter();
144
145        tree.decrypt_all(&self.keychain, ids, &db.pub_key_lookup, true)
146    }
147
148    #[instrument(level = "debug", skip(self), err(Debug))]
149    pub async fn get_and_get_children_recursively(&self, id: &Uuid) -> LbResult<Vec<File>> {
150        let tx = self.ro_tx().await;
151        let db = tx.db();
152
153        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
154
155        let descendants = tree.descendants_using_links(id)?;
156
157        tree.decrypt_all(
158            &self.keychain,
159            descendants.into_iter().chain(iter::once(*id)),
160            &db.pub_key_lookup,
161            true,
162        )
163    }
164
165    #[instrument(level = "debug", skip(self), err(Debug))]
166    pub async fn get_file_by_id(&self, id: Uuid) -> LbResult<File> {
167        let tx = self.ro_tx().await;
168        let db = tx.db();
169
170        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
171
172        if tree.calculate_deleted(&id)? {
173            return Err(LbErrKind::FileNonexistent.into());
174        }
175        if tree.access_mode(Owner(self.keychain.get_pk()?), &id)? < Some(UserAccessMode::Read) {
176            return Err(LbErrKind::FileNonexistent.into());
177        }
178
179        let file = tree.decrypt(&self.keychain, &id, &db.pub_key_lookup)?;
180
181        Ok(file)
182    }
183
184    #[instrument(level = "debug", skip(self), err(Debug))]
185    pub async fn get_file_link_url(&self, id: Uuid) -> LbResult<String> {
186        let tx = self.ro_tx().await;
187        let db = tx.db();
188
189        let tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
190
191        // Ensure file exists
192        let id = tree.find(&id)?.id();
193
194        let account = self.get_account()?;
195        let link_url = match account.api_url.as_str() {
196            // Use a more user-friendly link for prod API - both route to the same place
197            "https://api.prod.lockbook.net" => "https://app.lockbook.net",
198            other => other,
199        };
200
201        Ok(format!("{}/open/{}", link_url, id))
202    }
203
204    pub async fn local_changes(&self) -> Vec<Uuid> {
205        let tx = self.ro_tx().await;
206        let db = tx.db();
207        db.local_metadata.get().keys().copied().collect()
208    }
209}