From 5deba2fdb13373f7fea1b9bf56d57281496f725e Mon Sep 17 00:00:00 2001 From: tuxmain Date: Wed, 11 Jan 2023 22:56:32 +0100 Subject: [PATCH] Api & JS client --- Cargo.lock | 99 ++++++++---------- Cargo.toml | 4 +- client/basic.html | 1 + client/js/webcomment.js | 128 +++++++++++++++++++++-- client/style.css | 9 ++ src/db.rs | 28 ++--- src/server.rs | 2 +- src/server/api.rs | 213 ++++++++++++++++++++++++++++---------- src/server/api/queries.rs | 15 +++ src/server/api/resps.rs | 44 ++++++++ 10 files changed, 399 insertions(+), 144 deletions(-) create mode 100644 client/style.css create mode 100644 src/server/api/queries.rs create mode 100644 src/server/api/resps.rs diff --git a/Cargo.lock b/Cargo.lock index 31a3273..3f78783 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -304,9 +304,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" +checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ "proc-macro2", "quote", @@ -353,9 +353,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" @@ -421,11 +421,12 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" dependencies = [ "memchr", + "serde", ] [[package]] @@ -681,9 +682,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" dependencies = [ "cc", "cxxbridge-flags", @@ -693,9 +694,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0" +checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" dependencies = [ "cc", "codespan-reporting", @@ -708,15 +709,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" [[package]] name = "cxxbridge-macro" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" dependencies = [ "proc-macro2", "quote", @@ -768,7 +769,7 @@ dependencies = [ "hashbrown", "lock_api", "once_cell", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.6", ] [[package]] @@ -1149,9 +1150,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", "bstr", @@ -1399,11 +1400,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "a05705bc64e0b66a806c3740bd6578ea66051b157ec42dc219c785cbf185aef3" dependencies = [ - "crossbeam-utils", "globset", "lazy_static", "log", @@ -1462,9 +1462,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itertools" @@ -1770,9 +1770,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if", "libc", @@ -1809,9 +1809,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" +checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a" dependencies = [ "thiserror", "ucd-trie", @@ -1819,9 +1819,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" +checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6" dependencies = [ "pest", "pest_generator", @@ -1829,9 +1829,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" +checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c" dependencies = [ "pest", "pest_meta", @@ -1842,13 +1842,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" +checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51" dependencies = [ "once_cell", "pest", - "sha1 0.10.5", + "sha2 0.10.6", ] [[package]] @@ -2127,9 +2127,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2349,11 +2349,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.13.1", + "base64 0.21.0", ] [[package]] @@ -2486,17 +2486,6 @@ dependencies = [ "sha1_smol", ] -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - [[package]] name = "sha1_smol" version = "1.0.0" @@ -2665,7 +2654,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha1 0.6.1", + "sha1", "syn", ] @@ -2865,9 +2854,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.23.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", "bytes", @@ -2988,9 +2977,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "type-map" @@ -3355,7 +3344,7 @@ name = "webcomment" version = "0.1.0" dependencies = [ "argon2", - "base64 0.20.0", + "base64 0.21.0", "clap", "crossbeam-channel", "directories", diff --git a/Cargo.toml b/Cargo.toml index fae2e12..c77044f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] argon2 = "0.4.1" -base64 = "0.20.0" +base64 = "0.21.0" clap = { version = "4.0.32", default-features = false, features = ["derive", "error-context", "help", "std", "usage"] } crossbeam-channel = "0.5.6" directories = "4.0.1" @@ -29,7 +29,7 @@ sha2 = "0.10.6" sled = "0.34.7" tera = { version = "1.17.1", features = ["builtins", "date-locale"] } tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies", "logger"] } -tokio = { version = "1.23.0", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.24.1", features = ["macros", "rt-multi-thread"] } toml_edit = { version = "0.17.1", features = ["easy"] } typed-sled = "0.2.3" unic-langid = { version = "0.9.1", features = ["macros"] } diff --git a/client/basic.html b/client/basic.html index 849953a..54d28f3 100644 --- a/client/basic.html +++ b/client/basic.html @@ -5,6 +5,7 @@ Webcomment +
diff --git a/client/js/webcomment.js b/client/js/webcomment.js index c8ed596..f76fa0a 100644 --- a/client/js/webcomment.js +++ b/client/js/webcomment.js @@ -8,27 +8,68 @@ You should have received a copy of the GNU Affero General Public License along w var webcomments = {}; const MODE_TOPIC = 1;// param: {topic:str} +const ORDER_BY_DATE_ASC = 1; +const ORDER_BY_DATE_DESC = 2; + +const DEFAULT_CONFIG = { + default_order: ORDER_BY_DATE_ASC, + /*template_comment: ` +
+

{{comment.author}} {{comment.post_time}}

+

{{comment.text}}

+
`,*/ + template_comment: ` +
+

+ {{comment.post_time}} +

+

+
`, + template_widget: ` +
+
+
+ New comment +
+
+
+ +
+
+`, +}; class Webcomment { - constructor(root, api, mode, mode_param) { - this.root = root; + constructor(root_id, api, mode, mode_param, config) { + this.root_id = root_id; this.api = api; this.mode = mode; this.mode_param = mode_param; + this.config = config; - console.log("constr"); + this.root = document.getElementById(root_id); + this.root.innerHTML = config.template_widget.replaceAll("{{root.id}}", this.root_id);; + this.elem_comments = this.root.getElementsByClassName("comments")[0]; switch(mode) { case MODE_TOPIC: - this.query_comments_by_topic(mode_param.topic); + var this_ = this; + this.query_comments_by_topic(mode_param.topic, function(resp) { + this_.append_comments(resp.comments); + }); break; default: console.log("Webcomment: invalid mode"); } } - query_comments_by_topic(topic) { - console.log("query"); + query_comments_by_topic(topic, success) { $.ajax({ method: "POST", url: this.api+"/api/comments_by_topic", @@ -36,15 +77,80 @@ class Webcomment { mutation_token: "", topic: topic, }), - success: function(resp) { - console.log(resp); - }, + success: success, dataType: "json", contentType: "application/json; charset=utf-8", }); } + + query_new_comment(topic, author, email, text, success) { + $.ajax({ + method: "POST", + url: this.api+"/api/new_comment", + data: JSON.stringify({ + author: author, + email: email, + text: text, + topic: topic, + }), + success: success, + dataType: "json", + contentType: "application/json; charset=utf-8", + }); + } + + post_new_comment() { + var elem_author = $("#comments .comment-new-form [name=author]")[0]; + var elem_email = $("#comments .comment-new-form [name=email]")[0]; + var elem_text = $("#comments .comment-new-form [name=text]")[0]; + + switch(this.mode) { + case MODE_TOPIC: + var comment = { + topic: this.mode_param.topic, + author: elem_author.value, + email: elem_email.value, + text: elem_text.value, + }; + var this_ = this; + this.query_new_comment(comment.topic, comment.author, comment.email, comment.text, function(resp) { + if(resp.id) { + comment.id = resp.id; + comment.post_time = resp.post_time; + this_.append_comments([comment]); + elem_text.value = ""; + } + }); + break; + default: + console.log("Webcomment: invalid mode"); + } + } + + append_comments(comments) { + for(var i in comments) { + var comment = comments[i]; + var post_time = new Date(comment.post_time*1000); + + var comment_html = this.config.template_comment; + comment_html = comment_html.replaceAll("{{root.id}}", this.root_id); + comment_html = comment_html.replaceAll("{{comment.id}}", comment.id); + //comment_html = comment_html.replaceAll("{{comment.author}}", comment.author); + comment_html = comment_html.replaceAll("{{comment.post_time}}", post_time.toLocaleDateString()+" "+post_time.toLocaleTimeString()); + //comment_html = comment_html.replaceAll("{{comment.text}}", comment.text); + $(this.elem_comments).append(comment_html); + + var elem = document.getElementById(this.root_id+"-"+comment.id); + elem.getElementsByClassName("comment-author")[0].innerHTML = comment.author; + elem.getElementsByClassName("comment-text")[0].innerHTML = comment.text; + } + } } -function webcomment_topic(root_id, api, topic) { - webcomments[root_id] = (new Webcomment(document.getElementById(root_id), api, MODE_TOPIC, {topic: topic})); +function webcomment_topic(root_id, api, topic, config=DEFAULT_CONFIG) { + webcomments[root_id] = (new Webcomment(root_id, api, MODE_TOPIC, {topic: topic}, config)); +} + +function post_new_comment(root_id) { + webcomments[root_id].post_new_comment(); } diff --git a/client/style.css b/client/style.css new file mode 100644 index 0000000..055c099 --- /dev/null +++ b/client/style.css @@ -0,0 +1,9 @@ +.comment { + border-left: 2px solid #888; + border-right: 2px solid #888; + padding: 4px; +} + +.comment-meta, .comment-meta a, .comment-meta a:visited { + color: #666; +} \ No newline at end of file diff --git a/src/db.rs b/src/db.rs index 8fc1f72..2b66122 100644 --- a/src/db.rs +++ b/src/db.rs @@ -12,10 +12,10 @@ const DB_DIR: &str = "db"; pub type Time = u64; -pub const BASE64: base64::engine::fast_portable::FastPortable = - base64::engine::fast_portable::FastPortable::from( +pub const BASE64: base64::engine::general_purpose::GeneralPurpose = + base64::engine::general_purpose::GeneralPurpose::new( &base64::alphabet::URL_SAFE, - base64::engine::fast_portable::NO_PAD, + base64::engine::general_purpose::NO_PAD, ); #[derive(Clone)] @@ -74,20 +74,13 @@ impl MutationToken { } pub fn to_base64(&self) -> String { - let mut buf = vec![0; 24]; - let size = BASE64.encode(&self.0, &mut buf); - buf.truncate(size); - String::from_utf8(buf).unwrap() + BASE64.encode(self.0) } pub fn from_base64(s: &str) -> Result { std::panic::catch_unwind(|| { let mut buf = [0; 16]; - BASE64.decode( - s.as_bytes(), - &mut buf, - BASE64.decoded_length_estimate(s.len()), - )?; + BASE64.decode_slice_unchecked(s.as_bytes(), &mut buf)?; Ok(Self(buf)) }) .map_err(|_| base64::DecodeError::InvalidLength)? @@ -134,20 +127,13 @@ impl CommentId { } pub fn to_base64(&self) -> String { - let mut buf = vec![0; 24]; - let size = BASE64.encode(&self.0, &mut buf); - buf.truncate(size); - String::from_utf8(buf).unwrap() + BASE64.encode(self.0) } pub fn from_base64(s: &str) -> Result { std::panic::catch_unwind(|| { let mut buf = [0; 16]; - BASE64.decode( - s.as_bytes(), - &mut buf, - BASE64.decoded_length_estimate(s.len()), - )?; + BASE64.decode_slice_unchecked(s.as_bytes(), &mut buf)?; Ok(Self(buf)) }) .map_err(|_| base64::DecodeError::InvalidLength)? diff --git a/src/server.rs b/src/server.rs index 2ac5cf3..2e67f23 100644 --- a/src/server.rs +++ b/src/server.rs @@ -29,7 +29,7 @@ pub async fn run_server( .build()) }); - api::init_routes(&mut app, config, dbs.clone()).await; + api::init_routes(&mut app, config, dbs.clone(), notify_send.clone()).await; page::init_routes(&mut app, config, dbs, templates, locales, notify_send).await; app.listen(config.listen).await.unwrap(); diff --git a/src/server/api.rs b/src/server/api.rs index b2db1c5..fd74098 100644 --- a/src/server/api.rs +++ b/src/server/api.rs @@ -1,54 +1,29 @@ -#![allow(clippy::too_many_arguments)] +mod queries; +mod resps; use crate::{config::*, db::*, helpers, notify::Notification}; use crossbeam_channel::Sender; use log::{error, warn}; -use serde::{Deserialize, Serialize}; -enum ApiError { - InvalidAdminPassword, -} - -pub async fn init_routes(app: &mut tide::Server<()>, config: &'static Config, dbs: Dbs) { +pub async fn init_routes( + app: &mut tide::Server<()>, + config: &'static Config, + dbs: Dbs, + notify_send: Sender, +) { // TODO pagination app.at(&format!("{}api/comments_by_topic", config.root_url)) .post({ let dbs = dbs.clone(); move |req: tide::Request<()>| query_comments_by_topic(req, config, dbs.clone()) }); -} - -#[derive(Serialize)] -struct CommentWithId { - pub addr: Option, - pub author: String, - pub editable: bool, - pub id: String, - pub last_edit_time: Option