Author: Manos Pitsidianakis [manos@pitsidianak.is]
Hash: 4644349ebb5b4d0c06fcdc7c7ed0534d60db2e1d
Timestamp: Mon, 08 May 2023 07:43:22 +0000 (1 year ago)

+148 -316 +/-6 browse
Remove warp
1diff --git a/Cargo.lock b/Cargo.lock
2index 0ef3b52..2efe9e8 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -548,12 +548,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
6 checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
7
8 [[package]]
9- name = "byteorder"
10- version = "1.4.3"
11- source = "registry+https://github.com/rust-lang/crates.io-index"
12- checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
13-
14- [[package]]
15 name = "bytes"
16 version = "1.4.0"
17 source = "registry+https://github.com/rust-lang/crates.io-index"
18 @@ -1798,8 +1792,6 @@ dependencies = [
19 "percent-encoding",
20 "serde",
21 "serde_json",
22- "tokio",
23- "warp",
24 ]
25
26 [[package]]
27 @@ -1823,7 +1815,6 @@ version = "0.1.1"
28 dependencies = [
29 "mailpot",
30 "tokio",
31- "warp",
32 ]
33
34 [[package]]
35 @@ -1931,16 +1922,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
36 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
37
38 [[package]]
39- name = "mime_guess"
40- version = "2.0.4"
41- source = "registry+https://github.com/rust-lang/crates.io-index"
42- checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
43- dependencies = [
44- "mime",
45- "unicase",
46- ]
47-
48- [[package]]
49 name = "minijinja"
50 version = "0.31.0"
51 source = "registry+https://github.com/rust-lang/crates.io-index"
52 @@ -2013,20 +1994,6 @@ dependencies = [
53 ]
54
55 [[package]]
56- name = "multiparty"
57- version = "0.1.0"
58- source = "registry+https://github.com/rust-lang/crates.io-index"
59- checksum = "ed1ec6589a6d4a1e0b33b4c0a3f6ee96dfba88ebdb3da51403fd7cf0a24a4b04"
60- dependencies = [
61- "bytes",
62- "futures-core",
63- "httparse",
64- "memchr",
65- "pin-project-lite",
66- "try-lock",
67- ]
68-
69- [[package]]
70 name = "native-tls"
71 version = "0.2.11"
72 source = "registry+https://github.com/rust-lang/crates.io-index"
73 @@ -2614,12 +2581,6 @@ dependencies = [
74 ]
75
76 [[package]]
77- name = "scoped-tls"
78- version = "1.0.1"
79- source = "registry+https://github.com/rust-lang/crates.io-index"
80- checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
81-
82- [[package]]
83 name = "scoped_threadpool"
84 version = "0.1.9"
85 source = "registry+https://github.com/rust-lang/crates.io-index"
86 @@ -3081,29 +3042,6 @@ dependencies = [
87 ]
88
89 [[package]]
90- name = "tokio-stream"
91- version = "0.1.12"
92- source = "registry+https://github.com/rust-lang/crates.io-index"
93- checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
94- dependencies = [
95- "futures-core",
96- "pin-project-lite",
97- "tokio",
98- ]
99-
100- [[package]]
101- name = "tokio-tungstenite"
102- version = "0.18.0"
103- source = "registry+https://github.com/rust-lang/crates.io-index"
104- checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd"
105- dependencies = [
106- "futures-util",
107- "log",
108- "tokio",
109- "tungstenite",
110- ]
111-
112- [[package]]
113 name = "tokio-util"
114 version = "0.7.7"
115 source = "registry+https://github.com/rust-lang/crates.io-index"
116 @@ -3231,25 +3169,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
117 checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
118
119 [[package]]
120- name = "tungstenite"
121- version = "0.18.0"
122- source = "registry+https://github.com/rust-lang/crates.io-index"
123- checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788"
124- dependencies = [
125- "base64 0.13.1",
126- "byteorder",
127- "bytes",
128- "http",
129- "httparse",
130- "log",
131- "rand",
132- "sha1",
133- "thiserror",
134- "url",
135- "utf-8",
136- ]
137-
138- [[package]]
139 name = "typenum"
140 version = "1.16.0"
141 source = "registry+https://github.com/rust-lang/crates.io-index"
142 @@ -3315,12 +3234,6 @@ dependencies = [
143 ]
144
145 [[package]]
146- name = "utf-8"
147- version = "0.7.6"
148- source = "registry+https://github.com/rust-lang/crates.io-index"
149- checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
150-
151- [[package]]
152 name = "uuid"
153 version = "1.3.1"
154 source = "registry+https://github.com/rust-lang/crates.io-index"
155 @@ -3379,37 +3292,6 @@ dependencies = [
156 ]
157
158 [[package]]
159- name = "warp"
160- version = "0.3.4"
161- source = "registry+https://github.com/rust-lang/crates.io-index"
162- checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c"
163- dependencies = [
164- "bytes",
165- "futures-channel",
166- "futures-util",
167- "headers",
168- "http",
169- "hyper",
170- "log",
171- "mime",
172- "mime_guess",
173- "multiparty",
174- "percent-encoding",
175- "pin-project",
176- "rustls-pemfile",
177- "scoped-tls",
178- "serde",
179- "serde_json",
180- "serde_urlencoded",
181- "tokio",
182- "tokio-stream",
183- "tokio-tungstenite",
184- "tokio-util",
185- "tower-service",
186- "tracing",
187- ]
188-
189- [[package]]
190 name = "wasi"
191 version = "0.10.0+wasi-snapshot-preview1"
192 source = "registry+https://github.com/rust-lang/crates.io-index"
193 diff --git a/archive-http/Cargo.toml b/archive-http/Cargo.toml
194index c6c465b..b7ec33e 100644
195--- a/archive-http/Cargo.toml
196+++ b/archive-http/Cargo.toml
197 @@ -14,11 +14,6 @@ default-run = "mpot-archives"
198 [[bin]]
199 name = "mpot-archives"
200 path = "src/main.rs"
201- required-features = ["warp"]
202-
203- [[bin]]
204- name = "mpot-gen"
205- path = "src/gen.rs"
206
207 [dependencies]
208 chrono = { version = "^0.4" }
209 @@ -28,10 +23,3 @@ minijinja = { version = "0.31.0", features = ["source", ] }
210 percent-encoding = { version = "^2.1", optional = true }
211 serde = { version = "^1", features = ["derive", ] }
212 serde_json = "^1"
213- tokio = { version = "1", features = ["full"], optional = true }
214- warp = { version = "^0.3", optional = true }
215-
216- [features]
217- default = ["gen"]
218- gen = []
219- warp = ["dep:percent-encoding", "dep:tokio", "dep:warp"]
220 diff --git a/archive-http/src/main.rs b/archive-http/src/main.rs
221index b50ffcc..b486d83 100644
222--- a/archive-http/src/main.rs
223+++ b/archive-http/src/main.rs
224 @@ -17,26 +17,89 @@
225 * along with this program. If not, see <https://www.gnu.org/licenses/>.
226 */
227
228+ use std::{fs::OpenOptions, io::Write};
229+
230 use mailpot::*;
231 use mailpot_archives::utils::*;
232 use minijinja::value::Value;
233- use percent_encoding::percent_decode_str;
234- use warp::Filter;
235
236- #[tokio::main]
237- async fn main() {
238- let config_path = std::env::args()
239- .nth(1)
240- .expect("Expected configuration file path as first argument.");
241- let conf = Configuration::from_file(config_path).unwrap();
242+ fn run_app() -> std::result::Result<(), Box<dyn std::error::Error>> {
243+ let args = std::env::args().collect::<Vec<_>>();
244+ let Some(config_path) = args.get(1) else {
245+ return Err("Expected configuration file path as first argument.".into());
246+ };
247+ let Some(output_path) = args.get(2) else {
248+ return Err("Expected output dir path as second argument.".into());
249+ };
250+ let root_url_prefix = args.get(3).cloned().unwrap_or_default();
251+
252+ let output_path = std::path::Path::new(&output_path);
253+ if output_path.exists() && !output_path.is_dir() {
254+ return Err("Output path is not a directory.".into());
255+ }
256+
257+ std::fs::create_dir_all(&output_path.join("lists"))?;
258+ std::fs::create_dir_all(&output_path.join("list"))?;
259+ let conf = Configuration::from_file(config_path)
260+ .map_err(|err| format!("Could not load config {config_path}: {err}"))?;
261+
262+ let db = Connection::open_db(conf).map_err(|err| format!("Couldn't open db: {err}"))?;
263+ let lists_values = db.lists()?;
264+ {
265+ //index.html
266+
267+ let lists = lists_values
268+ .iter()
269+ .map(|list| {
270+ let months = db.months(list.pk).unwrap();
271+ let posts = db.list_posts(list.pk, None).unwrap();
272+ minijinja::context! {
273+ title => &list.name,
274+ posts => &posts,
275+ months => &months,
276+ body => &list.description.as_deref().unwrap_or_default(),
277+ root_prefix => &root_url_prefix,
278+ list => Value::from_object(MailingList::from(list.clone())),
279+ }
280+ })
281+ .collect::<Vec<_>>();
282+ let mut file = OpenOptions::new()
283+ .write(true)
284+ .create(true)
285+ .truncate(true)
286+ .open(&output_path.join("index.html"))?;
287+ let crumbs = vec![Crumb {
288+ label: "Lists".into(),
289+ url: format!("{root_url_prefix}/").into(),
290+ }];
291+
292+ let context = minijinja::context! {
293+ title => "mailing list archive",
294+ description => "",
295+ lists => &lists,
296+ root_prefix => &root_url_prefix,
297+ crumbs => crumbs,
298+ };
299+ file.write_all(
300+ TEMPLATES
301+ .get_template("lists.html")?
302+ .render(context)?
303+ .as_bytes(),
304+ )?;
305+ }
306+
307+ let mut lists_path = output_path.to_path_buf();
308+
309+ for list in &lists_values {
310+ lists_path.push("lists");
311+ lists_path.push(list.pk.to_string());
312+ std::fs::create_dir_all(&lists_path)?;
313+ lists_path.push("index.html");
314
315- let conf1 = conf.clone();
316- let list_handler = warp::path!("lists" / i64).map(move |list_pk: i64| {
317- let db = Connection::open_db(conf1.clone()).unwrap();
318- let list = db.list(list_pk).unwrap().unwrap();
319- let post_policy = db.list_post_policy(list.pk).unwrap();
320- let months = db.months(list.pk).unwrap();
321- let posts = db.list_posts(list.pk, None).unwrap();
322+ let list = db.list(list.pk)?.unwrap();
323+ let post_policy = db.list_post_policy(list.pk)?;
324+ let months = db.months(list.pk)?;
325+ let posts = db.list_posts(list.pk, None)?;
326 let mut hist = months
327 .iter()
328 .map(|m| (m.to_string(), [0usize; 31]))
329 @@ -61,26 +124,26 @@ async fn main() {
330 subject_ref = subject_ref[2 + list.id.len()..].trim();
331 }
332 minijinja::context! {
333- pk => post.pk,
334- list => post.list,
335- subject => subject_ref,
336- address=> post.address,
337- message_id => msg_id,
338- message => post.message,
339- timestamp => post.timestamp,
340- datetime => post.datetime,
341- root_prefix => "",
342+ pk => post.pk,
343+ list => post.list,
344+ subject => subject_ref,
345+ address=> post.address,
346+ message_id => msg_id,
347+ message => post.message,
348+ timestamp => post.timestamp,
349+ datetime => post.datetime,
350+ root_prefix => &root_url_prefix,
351 }
352 })
353 .collect::<Vec<_>>();
354 let crumbs = vec![
355 Crumb {
356 label: "Lists".into(),
357- url: "/".into(),
358+ url: format!("{root_url_prefix}/").into(),
359 },
360 Crumb {
361 label: list.name.clone().into(),
362- url: format!("/lists/{}/", list.pk).into(),
363+ url: format!("{root_url_prefix}/lists/{}/", list.pk).into(),
364 },
365 ];
366 let context = minijinja::context! {
367 @@ -92,34 +155,36 @@ async fn main() {
368 hists => &hist,
369 posts=> posts_ctx,
370 body=>&list.description.clone().unwrap_or_default(),
371- root_prefix => "",
372- list => Value::from_object(MailingList::from(list)),
373+ root_prefix => &root_url_prefix,
374+ list => Value::from_object(MailingList::from(list.clone())),
375 crumbs => crumbs,
376 };
377- Ok(warp::reply::html(
378+ let mut file = OpenOptions::new()
379+ .read(true)
380+ .write(true)
381+ .create(true)
382+ .truncate(true)
383+ .open(&lists_path)
384+ .map_err(|err| format!("could not open {lists_path:?}: {err}"))?;
385+ file.write_all(
386 TEMPLATES
387- .get_template("list.html")
388- .unwrap()
389- .render(context)
390- .unwrap_or_else(|err| err.to_string()),
391- ))
392- });
393- let conf2 = conf.clone();
394- let post_handler =
395- warp::path!("list" / i64 / String).map(move |list_pk: i64, message_id: String| {
396- let message_id = percent_decode_str(&message_id).decode_utf8().unwrap();
397- dbg!(&message_id);
398- let db = Connection::open_db(conf2.clone()).unwrap();
399- let list = db.list(list_pk).unwrap().unwrap();
400- let posts = db.list_posts(list_pk, None).unwrap();
401- let post = posts
402- .iter()
403- .find(|p| message_id.contains(p.message_id.as_str().strip_carets()))
404- .unwrap();
405- //let mut msg_id = &post.message_id[1..];
406- //msg_id = &msg_id[..msg_id.len().saturating_sub(1)];
407+ .get_template("list.html")?
408+ .render(context)?
409+ .as_bytes(),
410+ )?;
411+ lists_path.pop();
412+ lists_path.pop();
413+ lists_path.pop();
414+ lists_path.push("list");
415+ lists_path.push(list.pk.to_string());
416+ std::fs::create_dir_all(&lists_path)?;
417+
418+ for post in posts {
419+ let mut msg_id = &post.message_id[1..];
420+ msg_id = &msg_id[..msg_id.len().saturating_sub(1)];
421+ lists_path.push(format!("{msg_id}.html"));
422 let envelope = melib::Envelope::from_bytes(post.message.as_slice(), None)
423- .map_err(|err| format!("Could not parse mail {}: {err}", post.message_id)).unwrap();
424+ .map_err(|err| format!("Could not parse mail {}: {err}", post.message_id))?;
425 let body = envelope.body_bytes(post.message.as_slice());
426 let body_text = body.text();
427 let subject = envelope.subject();
428 @@ -135,21 +200,22 @@ async fn main() {
429 let crumbs = vec![
430 Crumb {
431 label: "Lists".into(),
432- url: "/".into(),
433+ url: format!("{root_url_prefix}/").into(),
434 },
435 Crumb {
436 label: list.name.clone().into(),
437- url: format!("/lists/{}/", list.pk).into(),
438+ url: format!("{root_url_prefix}/lists/{}/", list.pk).into(),
439 },
440 Crumb {
441 label: subject_ref.to_string().into(),
442- url: format!("/lists/{}/{message_id}.html/", list.pk).into(),
443+ url: format!("{root_url_prefix}/lists/{}/{message_id}.html/", list.pk).into(),
444 },
445 ];
446 let context = minijinja::context! {
447 title => &list.name,
448 list => &list,
449 post => &post,
450+ posts => &posts_ctx,
451 body => &body_text,
452 from => &envelope.field_from_to_string(),
453 date => &envelope.date_as_str(),
454 @@ -158,66 +224,34 @@ async fn main() {
455 trimmed_subject => subject_ref,
456 in_reply_to => &envelope.in_reply_to_display().map(|r| r.to_string().as_str().strip_carets().to_string()),
457 references => &envelope .references() .into_iter() .map(|m| m.to_string().as_str().strip_carets().to_string()) .collect::<Vec<String>>(),
458- root_prefix => "",
459+ root_prefix => &root_url_prefix,
460 crumbs => crumbs,
461 };
462- Ok(warp::reply::html(
463- TEMPLATES
464- .get_template("post.html")
465- .unwrap()
466- .render(context)
467- .unwrap_or_else(|err| err.to_string()),
468- ))
469- });
470- let conf3 = conf.clone();
471- let index_handler = warp::path::end().map(move || {
472- let db = Connection::open_db(conf3.clone()).unwrap();
473- let lists_values = db.lists().unwrap();
474- let lists = lists_values
475- .iter()
476- .map(|list| {
477- let months = db.months(list.pk).unwrap();
478- let posts = db.list_posts(list.pk, None).unwrap();
479- minijinja::context! {
480- title => &list.name,
481- posts => &posts,
482- months => &months,
483- body => &list.description.as_deref().unwrap_or_default(),
484- root_prefix => "",
485- list => Value::from_object(MailingList::from(list.clone())),
486- }
487- })
488- .collect::<Vec<_>>();
489- let crumbs = vec![Crumb {
490- label: "Lists".into(),
491- url: "/".into(),
492- }];
493-
494- let context = minijinja::context! {
495- title => "mailing list archive",
496- description => "",
497- lists => &lists,
498- root_prefix => "",
499- crumbs => crumbs,
500- };
501- Ok(warp::reply::html(
502- TEMPLATES
503- .get_template("lists.html")
504- .unwrap()
505- .render(context)
506- .unwrap_or_else(|err| err.to_string()),
507- ))
508- });
509- let routes = warp::get()
510- .and(index_handler)
511- .or(list_handler)
512- .or(post_handler);
513-
514- // Note that composing filters for many routes may increase compile times
515- // (because it uses a lot of generics). If you wish to use dynamic dispatch
516- // instead and speed up compile times while making it slightly slower at
517- // runtime, you can use Filter::boxed().
518+ let mut file = OpenOptions::new()
519+ .read(true)
520+ .write(true)
521+ .create(true)
522+ .truncate(true)
523+ .open(&lists_path)
524+ .map_err(|err| format!("could not open {lists_path:?}: {err}"))?;
525+ file.write_all(
526+ TEMPLATES
527+ .get_template("post.html")?
528+ .render(context)?
529+ .as_bytes(),
530+ )?;
531+ lists_path.pop();
532+ }
533+ lists_path.pop();
534+ lists_path.pop();
535+ }
536+ Ok(())
537+ }
538
539- eprintln!("Running at http://127.0.0.1:3030");
540- warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
541+ fn main() -> std::result::Result<(), i64> {
542+ if let Err(err) = run_app() {
543+ eprintln!("{err}");
544+ return Err(-1);
545+ }
546+ Ok(())
547 }
548 diff --git a/rest-http/Cargo.toml b/rest-http/Cargo.toml
549index f95d6e0..9cd7197 100644
550--- a/rest-http/Cargo.toml
551+++ b/rest-http/Cargo.toml
552 @@ -18,4 +18,3 @@ path = "src/main.rs"
553 [dependencies]
554 mailpot = { version = "^0.1", path = "../core" }
555 tokio = { version = "^1", features = ["full"] }
556- warp = "^0.3"
557 diff --git a/rest-http/README.md b/rest-http/README.md
558index 63a4750..8fac8eb 100644
559--- a/rest-http/README.md
560+++ b/rest-http/README.md
561 @@ -1,5 +1,3 @@
562 # mailpot REST http server
563
564- ```shell
565- cargo run --bin mpot-http
566- ```
567+ Not implemented.
568 diff --git a/rest-http/src/main.rs b/rest-http/src/main.rs
569index 85a1065..17d836f 100644
570--- a/rest-http/src/main.rs
571+++ b/rest-http/src/main.rs
572 @@ -17,73 +17,4 @@
573 * along with this program. If not, see <https://www.gnu.org/licenses/>.
574 */
575
576- use mailpot::*;
577- use warp::Filter;
578-
579- #[tokio::main]
580- async fn main() {
581- let config_path = std::env::args()
582- .nth(1)
583- .expect("Expected configuration file path as first argument.");
584- let conf = Configuration::from_file(config_path).unwrap();
585-
586- let conf1 = conf.clone();
587- // GET /lists/:i64/policy
588- let policy = warp::path!("lists" / i64 / "policy").map(move |list_pk| {
589- let db = Connection::open_db(conf1.clone()).unwrap();
590- db.list_post_policy(list_pk)
591- .ok()
592- .map(|l| warp::reply::json(&l.unwrap()))
593- .unwrap()
594- });
595-
596- let conf2 = conf.clone();
597- //get("/lists")]
598- let lists = warp::path!("lists").map(move || {
599- let db = Connection::open_db(conf2.clone()).unwrap();
600- let lists = db.lists().unwrap();
601- warp::reply::json(&lists)
602- });
603-
604- let conf3 = conf.clone();
605- //get("/lists/<num>")]
606- let lists_num = warp::path!("lists" / i64).map(move |list_pk| {
607- let db = Connection::open_db(conf3.clone()).unwrap();
608- let list = db.list(list_pk).unwrap();
609- warp::reply::json(&list)
610- });
611-
612- let conf4 = conf.clone();
613- //get("/lists/<num>/subscriptions")]
614- let lists_subscriptions = warp::path!("lists" / i64 / "subscriptions").map(move |list_pk| {
615- let db = Connection::open_db(conf4.clone()).unwrap();
616- db.list_subscriptions(list_pk)
617- .ok()
618- .map(|l| warp::reply::json(&l))
619- .unwrap()
620- });
621-
622- //get("/lists/<num>/owners")]
623- let lists_owners = warp::path!("lists" / i64 / "owners").map(move |list_pk| {
624- let db = Connection::open_db(conf.clone()).unwrap();
625- db.list_owners(list_pk)
626- .ok()
627- .map(|l| warp::reply::json(&l))
628- .unwrap()
629- });
630-
631- //post("/lists/<num>/owners/add", data = "<new_owner>")]
632- let lists_owner_add =
633- warp::post().and(warp::path!("lists" / i64 / "owners" / "add").map(|_list_pk| "todo"));
634-
635- let routes = warp::get().and(
636- lists
637- .or(policy)
638- .or(lists_num)
639- .or(lists_subscriptions)
640- .or(lists_owners)
641- .or(lists_owner_add),
642- );
643-
644- warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
645- }
646+ fn main() {}