lockbook_server_lib/
document_service.rs1use crate::ServerError;
2use crate::config::Config;
3use async_trait::async_trait;
4use lb_rs::model::crypto::EncryptedDocument;
5use lb_rs::model::file_metadata::DocumentHmac;
6use std::collections::HashMap;
7use std::fmt::Debug;
8use std::path::PathBuf;
9use std::sync::{Arc, Mutex};
10use tokio::fs::{File, remove_file};
11use tokio::io::{AsyncReadExt, AsyncWriteExt};
12use uuid::Uuid;
13
14#[async_trait]
15pub trait DocumentService: Send + Sync + Clone + 'static {
16 async fn insert<T: Debug>(
17 &self, id: &Uuid, hmac: &DocumentHmac, content: &EncryptedDocument,
18 ) -> Result<(), ServerError<T>>;
19 async fn get<T: Debug>(
20 &self, id: &Uuid, hmac: &DocumentHmac,
21 ) -> Result<EncryptedDocument, ServerError<T>>;
22 async fn maybe_get<T: Debug>(
23 &self, id: &Uuid, hmac: &DocumentHmac,
24 ) -> Result<Option<EncryptedDocument>, ServerError<T>>;
25 async fn delete<T: Debug>(&self, id: &Uuid, hmac: &DocumentHmac) -> Result<(), ServerError<T>>;
26
27 fn exists(&self, id: &Uuid, hmac: &DocumentHmac) -> bool;
28 fn get_path(&self, id: &Uuid, hmac: &DocumentHmac) -> PathBuf;
29}
30
31#[derive(Clone)]
32pub struct OnDiskDocuments {
33 config: Config,
34}
35
36impl From<&Config> for OnDiskDocuments {
37 fn from(value: &Config) -> Self {
38 Self { config: value.clone() }
39 }
40}
41
42#[async_trait]
43impl DocumentService for OnDiskDocuments {
44 async fn insert<T: Debug>(
45 &self, id: &Uuid, hmac: &DocumentHmac, content: &EncryptedDocument,
46 ) -> Result<(), ServerError<T>> {
47 let content = bincode::serialize(content)?;
48 let path = self.get_path(id, hmac);
49 let mut file = File::create(path.clone()).await?;
50 file.write_all(&content)
51 .await
52 .map_err(|err| internal!("{:?}", err))?;
53 file.flush().await.map_err(|err| internal!("{:?}", err))?;
54 Ok(())
55 }
56
57 async fn get<T: Debug>(
58 &self, id: &Uuid, hmac: &DocumentHmac,
59 ) -> Result<EncryptedDocument, ServerError<T>> {
60 let path = self.get_path(id, hmac);
61 let mut file = File::open(path.clone()).await?;
62 let mut content = vec![];
63 file.read_to_end(&mut content).await?;
64 let content = bincode::deserialize(&content)?;
65 Ok(content)
66 }
67
68 async fn maybe_get<T: Debug>(
69 &self, id: &Uuid, hmac: &DocumentHmac,
70 ) -> Result<Option<EncryptedDocument>, ServerError<T>> {
71 let path = self.get_path(id, hmac);
72 if !path.exists() {
73 return Ok(None);
74 }
75
76 Ok(Some(self.get(id, hmac).await?))
77 }
78
79 async fn delete<T: Debug>(&self, id: &Uuid, hmac: &DocumentHmac) -> Result<(), ServerError<T>> {
80 let path = self.get_path(id, hmac);
81 if path.exists() {
87 remove_file(path).await?;
88 }
89 Ok(())
90 }
91
92 fn exists(&self, id: &Uuid, hmac: &DocumentHmac) -> bool {
93 self.get_path(id, hmac).exists()
94 }
95
96 fn get_path(&self, id: &Uuid, hmac: &DocumentHmac) -> PathBuf {
97 let mut path = self.config.files.path.clone();
98 let hmac = base64::encode_config(hmac, base64::URL_SAFE);
100 path.push(format!("{id}-{hmac}"));
101 path
102 }
103}
104
105#[derive(Clone, Default)]
107pub struct InMemDocuments {
108 pub docs: Arc<Mutex<HashMap<String, EncryptedDocument>>>,
109}
110
111#[async_trait]
112impl DocumentService for InMemDocuments {
113 async fn insert<T: Debug>(
114 &self, id: &Uuid, hmac: &DocumentHmac, content: &EncryptedDocument,
115 ) -> Result<(), ServerError<T>> {
116 let hmac = base64::encode_config(hmac, base64::URL_SAFE);
117 let key = format!("{id}-{hmac}");
118 self.docs.lock().unwrap().insert(key, content.clone());
119 Ok(())
120 }
121
122 async fn get<T: Debug>(
123 &self, id: &Uuid, hmac: &DocumentHmac,
124 ) -> Result<EncryptedDocument, ServerError<T>> {
125 let hmac = base64::encode_config(hmac, base64::URL_SAFE);
126 let key = format!("{id}-{hmac}");
127 Ok(self.docs.lock().unwrap().get(&key).unwrap().clone())
128 }
129
130 async fn maybe_get<T: Debug>(
131 &self, id: &Uuid, hmac: &DocumentHmac,
132 ) -> Result<Option<EncryptedDocument>, ServerError<T>> {
133 let hmac = base64::encode_config(hmac, base64::URL_SAFE);
134 let key = format!("{id}-{hmac}");
135 Ok(self.docs.lock().unwrap().get(&key).cloned())
136 }
137
138 fn exists(&self, id: &Uuid, hmac: &DocumentHmac) -> bool {
139 let hmac = base64::encode_config(hmac, base64::URL_SAFE);
140 let key = format!("{id}-{hmac}");
141 self.docs.lock().unwrap().contains_key(&key)
142 }
143
144 fn get_path(&self, _id: &Uuid, _hmac: &DocumentHmac) -> PathBuf {
145 unimplemented!()
146 }
147
148 async fn delete<T: Debug>(&self, id: &Uuid, hmac: &DocumentHmac) -> Result<(), ServerError<T>> {
149 let hmac = base64::encode_config(hmac, base64::URL_SAFE);
150 let key = format!("{id}-{hmac}");
151 self.docs.lock().unwrap().remove(&key);
152
153 Ok(())
154 }
155}