main.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. use async_std::sync::{Arc, Mutex};
  2. use std::{
  3. fs::{create_dir_all, read_dir},
  4. path::PathBuf,
  5. };
  6. use async_executor::Executor;
  7. use futures::{select, FutureExt};
  8. use fxhash::FxHashMap;
  9. use log::{error, info, warn};
  10. use serde::Deserialize;
  11. use sha2::Digest;
  12. use smol::future;
  13. use structopt::StructOpt;
  14. use structopt_toml::StructOptToml;
  15. use unicode_segmentation::UnicodeSegmentation;
  16. use url::Url;
  17. use darkfi::{
  18. async_daemonize,
  19. net::{self, settings::SettingsOpt},
  20. raft::{NetMsg, ProtocolRaft, Raft, RaftSettings},
  21. rpc::server::listen_and_serve,
  22. util::{
  23. cli::{get_log_config, get_log_level, spawn_config},
  24. expand_path,
  25. file::{load_file, load_json_file, save_file, save_json_file},
  26. path::get_config_path,
  27. },
  28. Error, Result,
  29. };
  30. mod error;
  31. mod jsonrpc;
  32. mod patch;
  33. use error::DarkWikiResult;
  34. use jsonrpc::JsonRpcInterface;
  35. use patch::{OpMethod, Patch};
  36. type Patches = (Vec<Patch>, Vec<Patch>, Vec<Patch>, Vec<Patch>);
  37. pub const CONFIG_FILE: &str = "darkwiki.toml";
  38. pub const CONFIG_FILE_CONTENTS: &str = include_str!("../darkwiki.toml");
  39. /// darkwikid cli
  40. #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  41. #[serde(default)]
  42. #[structopt(name = "darkwikid")]
  43. pub struct Args {
  44. /// Sets a custom config file
  45. #[structopt(long)]
  46. pub config: Option<String>,
  47. /// Sets Datastore Path
  48. #[structopt(long, default_value = "~/.config/darkfi/darkwiki")]
  49. pub datastore: String,
  50. /// Sets Docs Path
  51. #[structopt(long, default_value = "~/darkwiki")]
  52. pub docs: String,
  53. /// Sets Author Name for Patch
  54. #[structopt(long, default_value = "NONE")]
  55. pub author: String,
  56. /// JSON-RPC listen URL
  57. #[structopt(long = "rpc", default_value = "tcp://127.0.0.1:13055")]
  58. pub rpc_listen: Url,
  59. #[structopt(flatten)]
  60. pub net: SettingsOpt,
  61. /// Increase verbosity
  62. #[structopt(short, parse(from_occurrences))]
  63. pub verbose: u8,
  64. }
  65. pub struct DarkWikiSettings {
  66. author: String,
  67. docs_path: PathBuf,
  68. datastore_path: PathBuf,
  69. }
  70. fn str_to_chars(s: &str) -> Vec<&str> {
  71. s.graphemes(true).collect::<Vec<&str>>()
  72. }
  73. fn lcs(a: &str, b: &str) -> Vec<OpMethod> {
  74. let a: Vec<_> = str_to_chars(a);
  75. let b: Vec<_> = str_to_chars(b);
  76. let (na, nb) = (a.len(), b.len());
  77. let mut lengths = vec![vec![0; nb + 1]; na + 1];
  78. for (i, ci) in a.iter().enumerate() {
  79. for (j, cj) in b.iter().enumerate() {
  80. lengths[i + 1][j + 1] =
  81. if ci == cj { lengths[i][j] + 1 } else { lengths[i][j + 1].max(lengths[i + 1][j]) }
  82. }
  83. }
  84. let mut result = Vec::new();
  85. let (mut i, mut j) = (na, nb);
  86. while i > 0 && j > 0 {
  87. if a[i - 1] == b[j - 1] {
  88. result.push(OpMethod::Retain((1) as _));
  89. i -= 1;
  90. j -= 1;
  91. } else if lengths[i - 1][j] > lengths[i][j - 1] {
  92. result.push(OpMethod::Delete((1) as _));
  93. i -= 1;
  94. } else {
  95. result.push(OpMethod::Insert(b[j - 1].to_string()));
  96. j -= 1;
  97. }
  98. }
  99. result.reverse();
  100. result
  101. }
  102. fn title_to_id(title: &str) -> String {
  103. let mut hasher = sha2::Sha256::new();
  104. hasher.update(title);
  105. hex::encode(hasher.finalize())
  106. }
  107. struct Darkwiki {
  108. settings: DarkWikiSettings,
  109. rpc: (
  110. async_channel::Sender<Vec<Vec<(String, String)>>>,
  111. async_channel::Receiver<(String, bool, Vec<String>)>,
  112. ),
  113. raft: (async_channel::Sender<Patch>, async_channel::Receiver<Patch>),
  114. }
  115. impl Darkwiki {
  116. async fn start(&self) -> DarkWikiResult<()> {
  117. loop {
  118. select! {
  119. val = self.rpc.1.recv().fuse() => {
  120. let (cmd, dry, files) = val.map_err(Error::from)?;
  121. match cmd.as_str() {
  122. "update" => {
  123. self.on_receive_update(dry, files).await?;
  124. },
  125. "restore" => {
  126. self.on_receive_restore(dry, files).await?;
  127. },
  128. _ => {}
  129. }
  130. }
  131. patch = self.raft.1.recv().fuse() => {
  132. let patch = patch.map_err(Error::from)?;
  133. info!("Receive new patch from Raft {:?}", patch);
  134. self.on_receive_patch(&patch)?;
  135. }
  136. }
  137. }
  138. }
  139. fn on_receive_patch(&self, received_patch: &Patch) -> DarkWikiResult<()> {
  140. let sync_id_path = self.settings.datastore_path.join("sync").join(&received_patch.id);
  141. let local_id_path = self.settings.datastore_path.join("local").join(&received_patch.id);
  142. if let Ok(mut sync_patch) = load_json_file::<Patch>(&sync_id_path) {
  143. if sync_patch.timestamp == received_patch.timestamp {
  144. return Ok(())
  145. }
  146. if let Ok(local_patch) = load_json_file::<Patch>(&local_id_path) {
  147. if local_patch.timestamp == sync_patch.timestamp {
  148. sync_patch.base = local_patch.to_string();
  149. sync_patch.set_ops(received_patch.ops());
  150. } else {
  151. sync_patch.extend_ops(received_patch.ops());
  152. }
  153. } else {
  154. sync_patch.extend_ops(received_patch.ops());
  155. }
  156. sync_patch.timestamp = received_patch.timestamp;
  157. sync_patch.author = received_patch.author.clone();
  158. save_json_file::<Patch>(&sync_id_path, &sync_patch)?;
  159. } else if !received_patch.base.is_empty() {
  160. save_json_file::<Patch>(&sync_id_path, received_patch)?;
  161. }
  162. Ok(())
  163. }
  164. async fn on_receive_update(&self, dry: bool, files: Vec<String>) -> DarkWikiResult<()> {
  165. let (patches, local, sync, merge) = self.update(dry, files)?;
  166. if !dry {
  167. for patch in patches {
  168. info!("Send a patch to Raft {:?}", patch);
  169. self.raft.0.send(patch.clone()).await.map_err(Error::from)?;
  170. }
  171. }
  172. let local: Vec<(String, String)> =
  173. local.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  174. let sync: Vec<(String, String)> =
  175. sync.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  176. let merge: Vec<(String, String)> =
  177. merge.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  178. self.rpc.0.send(vec![local, sync, merge]).await.map_err(Error::from)?;
  179. Ok(())
  180. }
  181. async fn on_receive_restore(&self, dry: bool, files_name: Vec<String>) -> DarkWikiResult<()> {
  182. let patches = self.restore(dry, files_name)?;
  183. let patches: Vec<(String, String)> =
  184. patches.iter().map(|p| (p.title.to_owned(), p.to_string())).collect();
  185. self.rpc.0.send(vec![patches]).await.map_err(Error::from)?;
  186. Ok(())
  187. }
  188. fn restore(&self, dry: bool, files_name: Vec<String>) -> DarkWikiResult<Vec<Patch>> {
  189. let local_path = self.settings.datastore_path.join("local");
  190. let docs_path = self.settings.docs_path.clone();
  191. let local_files = read_dir(&local_path).map_err(Error::from)?;
  192. let mut patches = vec![];
  193. for file in local_files {
  194. let file_id = file.as_ref().unwrap().file_name();
  195. let file_id = file_id.to_str().unwrap();
  196. let file_path = local_path.join(&file_id);
  197. let local_patch: Patch = load_json_file(&file_path)?;
  198. if !files_name.is_empty() && !files_name.contains(&local_patch.title.to_string()) {
  199. continue
  200. }
  201. if let Ok(doc) = load_file(&docs_path.join(&local_patch.title)) {
  202. if local_patch.to_string() == doc {
  203. continue
  204. }
  205. }
  206. if !dry {
  207. save_file(&docs_path.join(&local_patch.title), &local_patch.to_string())?;
  208. }
  209. patches.push(local_patch);
  210. }
  211. Ok(patches)
  212. }
  213. fn update(&self, dry: bool, files_name: Vec<String>) -> DarkWikiResult<Patches> {
  214. let mut patches: Vec<Patch> = vec![];
  215. let mut local_patches: Vec<Patch> = vec![];
  216. let mut sync_patches: Vec<Patch> = vec![];
  217. let mut merge_patches: Vec<Patch> = vec![];
  218. let local_path = self.settings.datastore_path.join("local");
  219. let sync_path = self.settings.datastore_path.join("sync");
  220. let docs_path = self.settings.docs_path.clone();
  221. // save and compare docs in darkwiki and local dirs
  222. // then merged with sync patches if any received
  223. let docs = read_dir(&docs_path).map_err(Error::from)?;
  224. for doc in docs {
  225. let doc_title = doc.as_ref().unwrap().file_name();
  226. let doc_title = doc_title.to_str().unwrap();
  227. if !files_name.is_empty() && !files_name.contains(&doc_title.to_string()) {
  228. continue
  229. }
  230. // load doc content
  231. let edit = load_file(&docs_path.join(doc_title)).map_err(Error::from)?;
  232. let edit = edit.trim();
  233. let doc_id = title_to_id(doc_title);
  234. // create new patch
  235. let mut new_patch = Patch::new(doc_title, &doc_id, &self.settings.author);
  236. // check for any changes found with local doc and darkwiki doc
  237. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
  238. // no changes found
  239. if local_patch.to_string() == edit {
  240. continue
  241. }
  242. // check the differences with LCS algorithm
  243. let lcs_ops = lcs(&local_patch.to_string(), edit);
  244. // add the change ops to the new patch
  245. for op in lcs_ops {
  246. new_patch.add_op(&op);
  247. }
  248. new_patch.base = local_patch.to_string();
  249. local_patches.push(new_patch.clone());
  250. let mut b_patch = new_patch.clone();
  251. b_patch.base = "".to_string();
  252. patches.push(b_patch);
  253. // check if the same doc has received patch from the network
  254. if let Ok(sync_patch) = load_json_file::<Patch>(&sync_path.join(&doc_id)) {
  255. if sync_patch.timestamp != local_patch.timestamp {
  256. sync_patches.push(sync_patch.clone());
  257. let sync_patch_t = new_patch.transform(&sync_patch);
  258. new_patch = new_patch.merge(&sync_patch_t);
  259. if !dry {
  260. save_file(&docs_path.join(doc_title), &new_patch.to_string())?;
  261. }
  262. merge_patches.push(new_patch.clone());
  263. }
  264. }
  265. } else {
  266. new_patch.base = edit.to_string();
  267. local_patches.push(new_patch.clone());
  268. patches.push(new_patch.clone());
  269. };
  270. if !dry {
  271. save_json_file(&local_path.join(&doc_id), &new_patch)?;
  272. save_json_file(&sync_path.join(doc_id), &new_patch)?;
  273. }
  274. }
  275. // check if a new patch received
  276. // and save the new changes in both local and darkwiki dirs
  277. let sync_files = read_dir(&sync_path).map_err(Error::from)?;
  278. for file in sync_files {
  279. let file_id = file.as_ref().unwrap().file_name();
  280. let file_id = file_id.to_str().unwrap();
  281. let file_path = sync_path.join(&file_id);
  282. let sync_patch: Patch = load_json_file(&file_path)?;
  283. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&file_id)) {
  284. if local_patch.timestamp == sync_patch.timestamp {
  285. continue
  286. }
  287. }
  288. if !files_name.is_empty() && !files_name.contains(&sync_patch.title.to_string()) {
  289. continue
  290. }
  291. if !dry {
  292. save_file(&docs_path.join(&sync_patch.title), &sync_patch.to_string())?;
  293. save_json_file(&local_path.join(file_id), &sync_patch)?;
  294. }
  295. if !sync_patches.contains(&sync_patch) {
  296. sync_patches.push(sync_patch);
  297. }
  298. }
  299. Ok((patches, local_patches, sync_patches, merge_patches))
  300. }
  301. }
  302. async_daemonize!(realmain);
  303. async fn realmain(settings: Args, executor: Arc<Executor<'_>>) -> Result<()> {
  304. let datastore_path = expand_path(&settings.datastore)?;
  305. let docs_path = expand_path(&settings.docs)?;
  306. create_dir_all(docs_path.clone())?;
  307. create_dir_all(datastore_path.join("local"))?;
  308. create_dir_all(datastore_path.join("sync"))?;
  309. let (rpc_sx, rpc_rv) = async_channel::unbounded::<(String, bool, Vec<String>)>();
  310. let (notify_sx, notify_rv) = async_channel::unbounded::<Vec<Vec<(String, String)>>>();
  311. //
  312. // RPC
  313. //
  314. let rpc_interface = Arc::new(JsonRpcInterface::new(rpc_sx, notify_rv));
  315. executor.spawn(listen_and_serve(settings.rpc_listen.clone(), rpc_interface)).detach();
  316. //
  317. // Raft
  318. //
  319. let net_settings = settings.net;
  320. let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
  321. let datastore_raft = datastore_path.join("darkwiki.db");
  322. let raft_settings = RaftSettings { datastore_path: datastore_raft, ..RaftSettings::default() };
  323. let mut raft = Raft::<Patch>::new(raft_settings, seen_net_msgs.clone())?;
  324. //
  325. // P2p setup
  326. //
  327. let (p2p_send_channel, p2p_recv_channel) = async_channel::unbounded::<NetMsg>();
  328. let p2p = net::P2p::new(net_settings.into()).await;
  329. let p2p = p2p.clone();
  330. let registry = p2p.protocol_registry();
  331. let raft_node_id = raft.id();
  332. registry
  333. .register(net::SESSION_ALL, move |channel, p2p| {
  334. let raft_node_id = raft_node_id.clone();
  335. let sender = p2p_send_channel.clone();
  336. let seen_net_msgs_cloned = seen_net_msgs.clone();
  337. async move {
  338. ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs_cloned).await
  339. }
  340. })
  341. .await;
  342. p2p.clone().start(executor.clone()).await?;
  343. executor.spawn(p2p.clone().run(executor.clone())).detach();
  344. //
  345. // Darkwiki start
  346. //
  347. let raft_sx = raft.sender();
  348. let raft_rv = raft.receiver();
  349. executor
  350. .spawn(async move {
  351. let darkwiki_settings =
  352. DarkWikiSettings { author: settings.author, datastore_path, docs_path };
  353. let darkwiki = Darkwiki {
  354. settings: darkwiki_settings,
  355. raft: (raft_sx, raft_rv),
  356. rpc: (notify_sx, rpc_rv),
  357. };
  358. darkwiki.start().await.unwrap_or(());
  359. })
  360. .detach();
  361. //
  362. // Waiting Exit signal
  363. //
  364. let (signal, shutdown) = async_channel::bounded::<()>(1);
  365. ctrlc::set_handler(move || {
  366. warn!(target: "darkwiki", "Catch exit signal");
  367. // cleaning up tasks running in the background
  368. if let Err(e) = async_std::task::block_on(signal.send(())) {
  369. error!("Error on sending exit signal: {}", e);
  370. }
  371. })
  372. .unwrap();
  373. raft.run(p2p.clone(), p2p_recv_channel.clone(), executor.clone(), shutdown.clone()).await?;
  374. Ok(())
  375. }