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, NameComponents};
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(None));
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(None));
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(None));
85
86        Ok(())
87    }
88
89    #[instrument(level = "debug", skip(self), err(Debug))]
90    pub async fn duplicate_file(&self, id: &Uuid) -> LbResult<File> {
91        let mut source = self.get_file_by_id(*id).await?;
92        if let FileType::Link { target } = source.file_type {
93            source = self.get_file_by_id(target).await?;
94        }
95        if !source.is_document() {
96            return Err(LbErrKind::FileNotDocument.into());
97        }
98
99        let content = self.read_document(source.id, false).await?;
100
101        let mut name = NameComponents::from(&source.name);
102        name.next_in_children(self.get_children(&source.parent).await?);
103
104        let new_file = self
105            .create_file(&name.to_name(), &source.parent, FileType::Document)
106            .await?;
107        self.write_document(new_file.id, &content).await?;
108
109        Ok(new_file)
110    }
111
112    #[instrument(level = "debug", skip(self), err(Debug))]
113    pub async fn delete(&self, id: &Uuid) -> LbResult<()> {
114        let mut tx = self.begin_tx().await;
115        let db = tx.db();
116
117        let mut tree = (&db.base_metadata)
118            .to_staged(&mut db.local_metadata)
119            .to_lazy();
120
121        let id = &tree.linked_by(id)?.unwrap_or(*id);
122
123        tree.delete(id, &self.keychain)?;
124
125        tx.end();
126
127        self.events.meta_changed(Actor::User(None));
128
129        Ok(())
130    }
131
132    // todo: keychain?
133    #[instrument(level = "debug", skip(self), err(Debug))]
134    pub async fn root(&self) -> LbResult<File> {
135        let tx = self.ro_tx().await;
136        let db = tx.db();
137
138        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
139
140        let root_id = db.root.get().ok_or(LbErrKind::RootNonexistent)?;
141
142        let root = tree.decrypt(&self.keychain, root_id, &db.pub_key_lookup)?;
143
144        Ok(root)
145    }
146
147    #[instrument(level = "debug", skip(self), err(Debug))]
148    pub async fn list_metadatas(&self) -> LbResult<Vec<File>> {
149        let tx = self.ro_tx().await;
150        let db = tx.db();
151
152        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
153
154        let ids = tree.ids().into_iter();
155
156        tree.decrypt_all(&self.keychain, ids, &db.pub_key_lookup, true)
157    }
158
159    #[instrument(level = "debug", skip(self), err(Debug))]
160    pub async fn get_children(&self, id: &Uuid) -> LbResult<Vec<File>> {
161        let tx = self.ro_tx().await;
162        let db = tx.db();
163
164        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
165
166        let ids = tree.children_using_links(id)?.into_iter();
167
168        tree.decrypt_all(&self.keychain, ids, &db.pub_key_lookup, true)
169    }
170
171    #[instrument(level = "debug", skip(self), err(Debug))]
172    pub async fn get_and_get_children_recursively(&self, id: &Uuid) -> LbResult<Vec<File>> {
173        let tx = self.ro_tx().await;
174        let db = tx.db();
175
176        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
177
178        let descendants = tree.descendants_using_links(id)?;
179
180        tree.decrypt_all(
181            &self.keychain,
182            descendants.into_iter().chain(iter::once(*id)),
183            &db.pub_key_lookup,
184            true,
185        )
186    }
187
188    #[instrument(level = "debug", skip(self), err(Debug))]
189    pub async fn get_file_by_id(&self, id: Uuid) -> LbResult<File> {
190        let tx = self.ro_tx().await;
191        let db = tx.db();
192
193        let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
194
195        if tree.calculate_deleted(&id)? {
196            return Err(LbErrKind::FileNonexistent.into());
197        }
198        if tree.access_mode(Owner(self.keychain.get_pk()?), &id)? < Some(UserAccessMode::Read) {
199            return Err(LbErrKind::FileNonexistent.into());
200        }
201
202        let file = tree.decrypt(&self.keychain, &id, &db.pub_key_lookup)?;
203
204        Ok(file)
205    }
206
207    #[instrument(level = "debug", skip(self), err(Debug))]
208    pub async fn get_file_link_url(&self, id: Uuid) -> LbResult<String> {
209        let tx = self.ro_tx().await;
210        let db = tx.db();
211
212        let tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
213
214        // Ensure file exists
215        let id = tree.find(&id)?.id();
216
217        let account = self.get_account()?;
218        let link_url = match account.api_url.as_str() {
219            // Use a more user-friendly link for prod API - both route to the same place
220            "https://api.prod.lockbook.net" => "https://app.lockbook.net",
221            other => other,
222        };
223
224        Ok(format!("{}/open/{}", link_url, id))
225    }
226
227    pub async fn local_changes(&self) -> Vec<Uuid> {
228        let tx = self.ro_tx().await;
229        let db = tx.db();
230        db.local_metadata.get().keys().copied().collect()
231    }
232}