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 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 #[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 let id = tree.find(&id)?.id();
216
217 let account = self.get_account()?;
218 let link_url = match account.api_url.as_str() {
219 "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}