Commit
+314 -207 +/-7 browse
1 | diff --git a/ayllu-mail/src/server.rs b/ayllu-mail/src/server.rs |
2 | index 6f26d66..87f89e0 100644 |
3 | --- a/ayllu-mail/src/server.rs |
4 | +++ b/ayllu-mail/src/server.rs |
5 | @@ -1,5 +1,4 @@ |
6 | use std::error::Error as StdError; |
7 | - use std::io::{Read, Write}; |
8 | |
9 | use anyhow::{format_err, Result}; |
10 | use capnp::{capability::Promise, Error as CapnpError}; |
11 | @@ -8,16 +7,13 @@ use mailpot::{ |
12 | models::{DbVal, Post}, |
13 | Connection, Error as MailpotError, |
14 | }; |
15 | - use melib::{ |
16 | - mbox::{MboxFormat, MboxMetadata}, |
17 | - Envelope, |
18 | - }; |
19 | + use melib::Envelope; |
20 | use tracing::log; |
21 | |
22 | use crate::config::Config; |
23 | use ayllu_api::mail_capnp::server::{ |
24 | - Client, ExportParams, ExportResults, ListThreadsParams, ListThreadsResults, ReadPostParams, |
25 | - ReadPostResults, ReadThreadParams, ReadThreadResults, Server, |
26 | + Client, ExportParams, ExportResults, ListThreadsParams, ListThreadsResults, ListsParams, |
27 | + ListsResults, ReadPostParams, ReadPostResults, ReadThreadParams, ReadThreadResults, Server, |
28 | }; |
29 | use ayllu_rpc::Server as RpcHelper; |
30 | |
31 | @@ -208,6 +204,53 @@ impl Server for ServerImpl { |
32 | Err(e) => Promise::err(CapnpError::failed(e.to_string())), |
33 | } |
34 | } |
35 | + |
36 | + fn lists( |
37 | + &mut self, |
38 | + _: ListsParams, |
39 | + results: ListsResults, |
40 | + ) -> ::capnp::capability::Promise<(), ::capnp::Error> { |
41 | + let read_lists = |mut results: ListsResults| { |
42 | + let lists = self.db.lists()?; |
43 | + let mut lists_array = results.get().init_lists(lists.len() as u32); |
44 | + for (i, list) in lists.iter().enumerate() { |
45 | + lists_array |
46 | + .reborrow() |
47 | + .get(i as u32) |
48 | + .set_id(list.id.as_str().into()); |
49 | + lists_array |
50 | + .reborrow() |
51 | + .get(i as u32) |
52 | + .set_name(list.name.as_str().into()); |
53 | + lists_array |
54 | + .reborrow() |
55 | + .get(i as u32) |
56 | + .set_address(list.address.as_str().into()); |
57 | + lists_array |
58 | + .reborrow() |
59 | + .get(i as u32) |
60 | + .set_request_address(list.request_subaddr().as_str().into()); |
61 | + if let Some(description) = &list.description { |
62 | + lists_array |
63 | + .reborrow() |
64 | + .get(i as u32) |
65 | + .set_name(description.as_str().into()); |
66 | + } |
67 | + let mut topics = lists_array |
68 | + .reborrow() |
69 | + .get(i as u32) |
70 | + .init_topics(list.topics.len() as u32); |
71 | + for (j, topic) in list.topics.iter().enumerate() { |
72 | + topics.set(j as u32, topic.as_str().into()); |
73 | + } |
74 | + } |
75 | + Ok::<(), MailpotError>(()) |
76 | + }; |
77 | + match read_lists(results) { |
78 | + Ok(_) => Promise::ok(()), |
79 | + Err(e) => Promise::err(CapnpError::failed(e.to_string())), |
80 | + } |
81 | + } |
82 | } |
83 | |
84 | pub async fn serve(cfg: &Config) -> Result<(), Box<dyn StdError>> { |
85 | diff --git a/crates/api/v1/mail.capnp b/crates/api/v1/mail.capnp |
86 | index b66637a..8e7c791 100644 |
87 | --- a/crates/api/v1/mail.capnp |
88 | +++ b/crates/api/v1/mail.capnp |
89 | @@ -1,10 +1,5 @@ |
90 | @0xe282ac1f72de3195; |
91 | |
92 | - struct ThreadedMessage { |
93 | - depth @0 :Int64; |
94 | - message @1 :Message; |
95 | - } |
96 | - |
97 | # a single email |
98 | struct Message { |
99 | # primary key of the message in the mailpot db |
100 | @@ -31,13 +26,30 @@ struct Thread { |
101 | hasPatch @2 :Bool; |
102 | } |
103 | |
104 | + struct MailingList { |
105 | + # unique list identifier |
106 | + id @0 :Text; |
107 | + # freindly mailing list name |
108 | + name @1 :Text; |
109 | + # address to send "commands" to |
110 | + requestAddress @2 :Text; |
111 | + # RFC 5322 email address |
112 | + address @3 :Text; |
113 | + # description of the mailing list purpose |
114 | + description @4 :Text; |
115 | + # free form array of tags |
116 | + topics @5 :List(Text); |
117 | + } |
118 | + |
119 | interface Server { |
120 | + # return all managed mailing lists |
121 | + lists @0 () -> (lists: List(MailingList)); |
122 | # list all of the threads associated with a mailing list |
123 | - listThreads @0 (id: Text, offset: Int64, limit: Int64) -> (threads: List(Thread)); |
124 | + listThreads @1 (id: Text, offset: Int64, limit: Int64) -> (threads: List(Thread)); |
125 | # list all of the responses associated with a post starting from messageId |
126 | - readThread @1 (id: Text, messageId: Text, offset: Int64, limit: Int64) -> (thread: List(Message)); |
127 | + readThread @2 (id: Text, messageId: Text, offset: Int64, limit: Int64) -> (thread: List(Message)); |
128 | # read a single post |
129 | - readPost @2 (id: Text, messageId: Text) -> Message; |
130 | + readPost @3 (id: Text, messageId: Text) -> Message; |
131 | # export one or more messages in mbox format |
132 | - export @3 (id: Text, messageId: Text, asThread: Bool) -> (thread :Data); |
133 | + export @4 (id: Text, messageId: Text, asThread: Bool) -> (thread :Data); |
134 | } |
135 | diff --git a/src/config.rs b/src/config.rs |
136 | index 0895b9d..66a91ed 100644 |
137 | --- a/src/config.rs |
138 | +++ b/src/config.rs |
139 | @@ -175,28 +175,9 @@ impl Xmpp { |
140 | } |
141 | |
142 | #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
143 | - pub struct SubscriptionPolicy { |
144 | - pub send_confirmation: bool, |
145 | - pub kind: String, |
146 | - } |
147 | - |
148 | - #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
149 | - pub struct MailingList { |
150 | - pub id: String, |
151 | - pub name: Option<String>, |
152 | - pub address: String, |
153 | - pub request_address: String, |
154 | - pub description: String, |
155 | - pub topics: Vec<String>, |
156 | - pub post_policy: String, |
157 | - pub subscription_policy: SubscriptionPolicy, |
158 | - } |
159 | - |
160 | - #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
161 | pub struct Mail { |
162 | #[serde(default = "Mail::default_socket_path")] |
163 | pub socket_path: String, |
164 | - pub lists: Vec<MailingList>, |
165 | } |
166 | |
167 | impl Mail { |
168 | diff --git a/src/web2/routes/mail.rs b/src/web2/routes/mail.rs |
169 | index c67a43a..2991176 100644 |
170 | --- a/src/web2/routes/mail.rs |
171 | +++ b/src/web2/routes/mail.rs |
172 | @@ -7,7 +7,6 @@ use axum::{ |
173 | }; |
174 | use serde::{Deserialize, Serialize}; |
175 | |
176 | - use crate::config::Config; |
177 | use crate::languages::Hint; |
178 | use crate::web2::error::Error; |
179 | use crate::web2::highlight::Highlighter; |
180 | @@ -15,6 +14,7 @@ use crate::web2::middleware::rpc_initiator::{Initiator, Kind as InitiatorKind}; |
181 | use crate::web2::middleware::template::Template; |
182 | use crate::web2::util::navigation; |
183 | use ayllu_api::mail_capnp::server::Client as MailClient; |
184 | + use ayllu_rpc::Client; |
185 | |
186 | #[derive(Deserialize)] |
187 | pub struct Params { |
188 | @@ -45,14 +45,83 @@ struct Message { |
189 | pub is_patch: bool, |
190 | } |
191 | |
192 | + #[derive(Debug, Serialize, Default, PartialEq)] |
193 | + struct List { |
194 | + pub id: String, |
195 | + pub name: String, |
196 | + pub description: Option<String>, |
197 | + pub address: String, |
198 | + pub topics: Vec<String>, |
199 | + pub request_address: String, |
200 | + } |
201 | + |
202 | + async fn read_list(mail_client: Client, list_id: String) -> Result<Option<List>, Error> { |
203 | + let list = mail_client |
204 | + .invoke(move |c: MailClient| async move { |
205 | + let req = c.lists_request(); |
206 | + let result = req.send().promise.await?; |
207 | + let rpc_lists = result.get()?.get_lists()?; |
208 | + for list in rpc_lists.iter() { |
209 | + let id = list.get_id()?.to_string().unwrap(); |
210 | + let topics: Vec<String> = list |
211 | + .get_topics()? |
212 | + .iter() |
213 | + .map(|topic| topic.unwrap().to_string().unwrap()) |
214 | + .collect(); |
215 | + if list_id == id { |
216 | + return Ok(Some(List { |
217 | + id: list_id.clone(), |
218 | + name: list.get_name()?.to_string().unwrap(), |
219 | + description: None, |
220 | + address: list.get_address()?.to_string().unwrap(), |
221 | + topics, |
222 | + request_address: list.get_request_address()?.to_string().unwrap(), |
223 | + })); |
224 | + } |
225 | + } |
226 | + Ok(None) |
227 | + }) |
228 | + .await?; |
229 | + Ok(list) |
230 | + } |
231 | + |
232 | pub async fn lists( |
233 | - Extension(cfg): Extension<Config>, |
234 | + Extension(initiator): Extension<Initiator>, |
235 | Extension((templates, mut ctx)): Extension<Template>, |
236 | ) -> Result<Html<String>, Error> { |
237 | ctx.insert("title", "lists"); |
238 | ctx.insert("nav_elements", &navigation::global("mail", true)); |
239 | - // TODO: add stats method and display like xmpp |
240 | - ctx.insert("lists", &cfg.mail.unwrap().lists.clone()); |
241 | + let mail_client = initiator.client(InitiatorKind::Mail).unwrap(); |
242 | + let lists = mail_client |
243 | + .invoke(move |c: MailClient| async move { |
244 | + let mut lists: Vec<List> = Vec::new(); |
245 | + let req = c.lists_request(); |
246 | + let result = req.send().promise.await?; |
247 | + let rpc_lists = result.get()?.get_lists()?; |
248 | + for list in rpc_lists.iter() { |
249 | + let description = if list.has_description() { |
250 | + Some(list.get_description()?.to_string().unwrap()) |
251 | + } else { |
252 | + None |
253 | + }; |
254 | + let topics: Vec<String> = list |
255 | + .get_topics()? |
256 | + .iter() |
257 | + .map(|topic| topic.unwrap().to_string().unwrap()) |
258 | + .collect(); |
259 | + lists.push(List { |
260 | + id: list.get_id()?.to_string().unwrap(), |
261 | + name: list.get_name()?.to_string().unwrap(), |
262 | + description, |
263 | + address: list.get_name()?.to_string().unwrap(), |
264 | + topics, |
265 | + request_address: list.get_request_address()?.to_string().unwrap(), |
266 | + }) |
267 | + } |
268 | + Ok(lists) |
269 | + }) |
270 | + .await?; |
271 | + ctx.insert("lists", &lists); |
272 | // ctx.insert("lists", &cfg.mail.unwrap().lists); |
273 | let body = templates.render("lists.html", &ctx)?; |
274 | Ok(Html(body)) |
275 | @@ -63,76 +132,133 @@ pub async fn threads( |
276 | Path(params): Path<Params>, |
277 | // Extension(db): Extension<Arc<Database>>, |
278 | Extension(initiator): Extension<Initiator>, |
279 | - Extension(cfg): Extension<Config>, |
280 | Extension((templates, mut ctx)): Extension<Template>, |
281 | ) -> Result<Html<String>, Error> { |
282 | - let lists = cfg.mail.unwrap().lists; |
283 | - let list = match lists.iter().find(|list| list.id == params.list_id) { |
284 | - Some(list) => Ok(list), |
285 | - None => Err(Error::Message(format!( |
286 | - "no list associated with: {}", |
287 | + let client = initiator.client(InitiatorKind::Mail).unwrap(); |
288 | + let mailing_list = read_list(client.clone(), params.list_id.clone()).await?; |
289 | + if let Some(mailing_list) = mailing_list { |
290 | + ctx.insert("title", &format!("list {}", mailing_list.name)); |
291 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
292 | + ctx.insert("list", &mailing_list); |
293 | + ctx.insert("title", &format!("list {}", mailing_list.address)); |
294 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
295 | + ctx.insert("list", &mailing_list); |
296 | + let mut threads = client |
297 | + .invoke(move |c: MailClient| async move { |
298 | + let mut threads: Vec<Thread> = Vec::new(); |
299 | + let mut req = c.list_threads_request(); |
300 | + req.get().set_id(params.list_id.as_str().into()); |
301 | + let result = req.send().promise.await?; |
302 | + let rpc_threads = result.get()?.get_threads()?; |
303 | + for thread in rpc_threads.iter() { |
304 | + let message = thread.get_first()?; |
305 | + let message_id = message.get_message_id()?.to_string().unwrap(); |
306 | + threads.push(Thread { |
307 | + id: message.get_id(), |
308 | + message_id, |
309 | + from: message.get_from()?.to_string().unwrap(), |
310 | + subject: message.get_subject()?.to_string().unwrap(), |
311 | + n_replies: thread.get_n_replies(), |
312 | + timestamp: thread.get_first().unwrap().get_timestamp(), |
313 | + }) |
314 | + } |
315 | + Ok(threads) |
316 | + }) |
317 | + .await?; |
318 | + threads.sort_by(|first, second| second.timestamp.cmp(&first.timestamp)); |
319 | + ctx.insert("threads", &threads); |
320 | + ctx.insert("request_address", &mailing_list.request_address); |
321 | + let body = templates.render("threads.html", &ctx)?; |
322 | + Ok(Html(body)) |
323 | + } else { |
324 | + Err(Error::NotFound(format!( |
325 | + "no mailing list with id: {} exists", |
326 | params.list_id |
327 | - ))), |
328 | - }?; |
329 | - ctx.insert("title", &format!("list {}", list.address)); |
330 | - ctx.insert("nav_elements", &navigation::global("mail", true)); |
331 | - ctx.insert("list", list); |
332 | - |
333 | - let mail_client = initiator.client(InitiatorKind::Mail).unwrap(); |
334 | - let mut threads = mail_client |
335 | - .invoke(move |c: MailClient| async move { |
336 | - let mut threads: Vec<Thread> = Vec::new(); |
337 | - let mut req = c.list_threads_request(); |
338 | - req.get().set_id(params.list_id.as_str().into()); |
339 | - let result = req.send().promise.await?; |
340 | - let rpc_threads = result.get()?.get_threads()?; |
341 | - for thread in rpc_threads.iter() { |
342 | - let message = thread.get_first()?; |
343 | - let message_id = message.get_message_id()?.to_string().unwrap(); |
344 | - threads.push(Thread { |
345 | - id: message.get_id(), |
346 | - message_id, |
347 | - from: message.get_from()?.to_string().unwrap(), |
348 | - subject: message.get_subject()?.to_string().unwrap(), |
349 | - n_replies: thread.get_n_replies(), |
350 | - timestamp: thread.get_first().unwrap().get_timestamp(), |
351 | - }) |
352 | - } |
353 | - Ok(threads) |
354 | - }) |
355 | - .await?; |
356 | - threads.sort_by(|first, second| second.timestamp.cmp(&first.timestamp)); |
357 | - ctx.insert("threads", &threads); |
358 | - ctx.insert("request_email", &list.request_address); |
359 | - let body = templates.render("threads.html", &ctx)?; |
360 | - Ok(Html(body)) |
361 | + ))) |
362 | + } |
363 | } |
364 | |
365 | pub async fn thread( |
366 | Path(params): Path<Params>, |
367 | Extension(initiator): Extension<Initiator>, |
368 | Extension(highlighter): Extension<Highlighter>, |
369 | - Extension(cfg): Extension<Config>, |
370 | Extension((templates, mut ctx)): Extension<Template>, |
371 | ) -> Result<Html<String>, Error> { |
372 | - let lists = cfg.mail.unwrap().lists; |
373 | - let list = match lists.iter().find(|list| list.id == params.list_id) { |
374 | - Some(list) => Ok(list), |
375 | - None => Err(Error::Message(format!( |
376 | - "no list associated with: {}", |
377 | + let client = initiator.client(InitiatorKind::Mail).unwrap(); |
378 | + let mailing_list = read_list(client.clone(), params.list_id.clone()).await?; |
379 | + if let Some(mailing_list) = mailing_list { |
380 | + let thread_id = params.thread_id.clone(); |
381 | + let messages = client |
382 | + .invoke(move |c: MailClient| async move { |
383 | + let mut messages: Vec<Message> = Vec::new(); |
384 | + let mut req = c.read_thread_request(); |
385 | + req.get().set_id(params.list_id.as_str().into()); |
386 | + req.get().set_message_id(thread_id.unwrap().as_str().into()); |
387 | + let result = req.send().promise.await?; |
388 | + for message in result.get()?.get_thread()? { |
389 | + let text = message.get_text().unwrap().to_string().unwrap(); |
390 | + let text = if message.get_is_patch() { |
391 | + let (_, diff) = highlighter.highlight( |
392 | + &text, |
393 | + None, |
394 | + None, |
395 | + Some(Hint::from("DIFF")), |
396 | + false, |
397 | + ); |
398 | + diff |
399 | + } else { |
400 | + text |
401 | + }; |
402 | + messages.push(Message { |
403 | + id: message.get_id().to_string(), |
404 | + subject: message.get_subject()?.to_string().unwrap(), |
405 | + message_id: message.get_message_id()?.to_string().unwrap(), |
406 | + created_at: message.get_timestamp(), |
407 | + from_address: message.get_address()?.to_string().unwrap(), |
408 | + body: message.get_body()?.to_string().unwrap(), |
409 | + text, |
410 | + is_patch: message.get_is_patch(), |
411 | + }) |
412 | + } |
413 | + Ok(messages) |
414 | + }) |
415 | + .await?; |
416 | + let subject = messages.first().map(|message| message.subject.clone()); |
417 | + ctx.insert("title", &format!("list {}", mailing_list.address)); |
418 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
419 | + ctx.insert("list", &mailing_list); |
420 | + ctx.insert("list_id", &mailing_list.id); |
421 | + ctx.insert("thread_id", ¶ms.thread_id.clone()); |
422 | + ctx.insert("messages", &messages); |
423 | + ctx.insert("subject", &subject); |
424 | + let body = templates.render("thread.html", &ctx)?; |
425 | + Ok(Html(body)) |
426 | + } else { |
427 | + Err(Error::NotFound(format!( |
428 | + "no mailing list with id: {} exists", |
429 | params.list_id |
430 | - ))), |
431 | - }?; |
432 | - let thread_id = params.thread_id.clone(); |
433 | - let mail_client = initiator.client(InitiatorKind::Mail).unwrap(); |
434 | - let messages = mail_client |
435 | - .invoke(move |c: MailClient| async move { |
436 | - let mut messages: Vec<Message> = Vec::new(); |
437 | - let mut req = c.read_thread_request(); |
438 | - req.get().set_id(params.list_id.as_str().into()); |
439 | - req.get().set_message_id(thread_id.unwrap().as_str().into()); |
440 | - let result = req.send().promise.await?; |
441 | - for message in result.get()?.get_thread()? { |
442 | + ))) |
443 | + } |
444 | + } |
445 | + |
446 | + pub async fn message( |
447 | + Path(params): Path<Params>, |
448 | + Extension(initiator): Extension<Initiator>, |
449 | + Extension(highlighter): Extension<Highlighter>, |
450 | + Extension((templates, mut ctx)): Extension<Template>, |
451 | + ) -> Result<Html<String>, Error> { |
452 | + let client = initiator.client(InitiatorKind::Mail).unwrap(); |
453 | + let mailing_list = read_list(client.clone(), params.list_id.clone()).await?; |
454 | + if let Some(mailing_list) = mailing_list { |
455 | + let message = client |
456 | + .invoke(move |c: MailClient| async move { |
457 | + let mut req = c.read_post_request(); |
458 | + req.get().set_id(params.list_id.as_str().into()); |
459 | + req.get() |
460 | + .set_message_id(params.message_id.unwrap().as_str().into()); |
461 | + // TODO: detect a missing message and return 404 appropriately |
462 | + let result = req.send().promise.await?; |
463 | + let message = result.get()?; |
464 | let text = message.get_text().unwrap().to_string().unwrap(); |
465 | let text = if message.get_is_patch() { |
466 | let (_, diff) = |
467 | @@ -141,117 +267,64 @@ pub async fn thread( |
468 | } else { |
469 | text |
470 | }; |
471 | - messages.push(Message { |
472 | + Ok(Message { |
473 | id: message.get_id().to_string(), |
474 | subject: message.get_subject()?.to_string().unwrap(), |
475 | message_id: message.get_message_id()?.to_string().unwrap(), |
476 | created_at: message.get_timestamp(), |
477 | - from_address: message.get_address()?.to_string().unwrap(), |
478 | body: message.get_body()?.to_string().unwrap(), |
479 | text, |
480 | + from_address: message.get_address()?.to_string().unwrap(), |
481 | is_patch: message.get_is_patch(), |
482 | }) |
483 | - } |
484 | - Ok(messages) |
485 | - }) |
486 | - .await?; |
487 | - let subject = messages.first().map(|message| message.subject.clone()); |
488 | - ctx.insert("title", &format!("list {}", list.address)); |
489 | - ctx.insert("nav_elements", &navigation::global("mail", true)); |
490 | - ctx.insert("list", list); |
491 | - ctx.insert("list_id", &list.id); |
492 | - ctx.insert("thread_id", ¶ms.thread_id.clone()); |
493 | - ctx.insert("messages", &messages); |
494 | - ctx.insert("subject", &subject); |
495 | - let body = templates.render("thread.html", &ctx)?; |
496 | - Ok(Html(body)) |
497 | - } |
498 | - |
499 | - pub async fn message( |
500 | - Path(params): Path<Params>, |
501 | - Extension(initiator): Extension<Initiator>, |
502 | - Extension(highlighter): Extension<Highlighter>, |
503 | - Extension(cfg): Extension<Config>, |
504 | - Extension((templates, mut ctx)): Extension<Template>, |
505 | - ) -> Result<Html<String>, Error> { |
506 | - let lists = cfg.mail.unwrap().lists; |
507 | - let list = match lists.iter().find(|list| list.id == params.list_id) { |
508 | - Some(list) => Ok(list), |
509 | - None => Err(Error::Message(format!( |
510 | - "no list associated with: {}", |
511 | - params.list_id |
512 | - ))), |
513 | - }?; |
514 | - let mail_client = initiator.client(InitiatorKind::Mail).unwrap(); |
515 | - let message = mail_client |
516 | - .invoke(move |c: MailClient| async move { |
517 | - let mut req = c.read_post_request(); |
518 | - req.get().set_id(params.list_id.as_str().into()); |
519 | - req.get() |
520 | - .set_message_id(params.message_id.unwrap().as_str().into()); |
521 | - let result = req.send().promise.await?; |
522 | - let message = result.get()?; |
523 | - let text = message.get_text().unwrap().to_string().unwrap(); |
524 | - let text = if message.get_is_patch() { |
525 | - let (_, diff) = |
526 | - highlighter.highlight(&text, None, None, Some(Hint::from("DIFF")), false); |
527 | - diff |
528 | - } else { |
529 | - text |
530 | - }; |
531 | - Ok(Message { |
532 | - id: message.get_id().to_string(), |
533 | - subject: message.get_subject()?.to_string().unwrap(), |
534 | - message_id: message.get_message_id()?.to_string().unwrap(), |
535 | - created_at: message.get_timestamp(), |
536 | - body: message.get_body()?.to_string().unwrap(), |
537 | - text, |
538 | - from_address: message.get_address()?.to_string().unwrap(), |
539 | - is_patch: message.get_is_patch(), |
540 | }) |
541 | - }) |
542 | - .await?; |
543 | - ctx.insert("title", &format!("list {}", list.address)); |
544 | - ctx.insert("nav_elements", &navigation::global("mail", true)); |
545 | - ctx.insert("list", list); |
546 | - ctx.insert("list_id", &list.id); |
547 | - ctx.insert("thread_id", ¶ms.thread_id); |
548 | - ctx.insert("message", &message); |
549 | - let body = templates.render("post.html", &ctx)?; |
550 | - Ok(Html(body)) |
551 | + .await?; |
552 | + ctx.insert("title", &format!("list {}", mailing_list.name)); |
553 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
554 | + ctx.insert("list", &mailing_list); |
555 | + ctx.insert("list_id", &mailing_list.id.clone()); |
556 | + ctx.insert("thread_id", ¶ms.thread_id); |
557 | + ctx.insert("message", &message); |
558 | + let body = templates.render("post.html", &ctx)?; |
559 | + Ok(Html(body)) |
560 | + } else { |
561 | + Err(Error::NotFound(format!( |
562 | + "no mailing list with id: {} exists", |
563 | + params.list_id |
564 | + ))) |
565 | + } |
566 | } |
567 | |
568 | pub async fn export( |
569 | Path(params): Path<Params>, |
570 | Extension(initiator): Extension<Initiator>, |
571 | - Extension(cfg): Extension<Config>, |
572 | ) -> Result<Response, Error> { |
573 | - let lists = cfg.mail.unwrap().lists; |
574 | - let list = match lists.iter().find(|list| list.id == params.list_id) { |
575 | - Some(list) => Ok(list), |
576 | - None => Err(Error::Message(format!( |
577 | - "no list associated with: {}", |
578 | + let client = initiator.client(InitiatorKind::Mail).unwrap(); |
579 | + let mailing_list = read_list(client.clone(), params.list_id.clone()).await?; |
580 | + if let Some(mailing_list) = mailing_list { |
581 | + let list_id = mailing_list.id.clone(); |
582 | + let thread = client |
583 | + .invoke(move |c: MailClient| async move { |
584 | + let mut req = c.export_request(); |
585 | + req.get().set_id(list_id.as_str().into()); |
586 | + if let Some(message_id) = params.message_id { |
587 | + req.get().set_message_id(message_id.as_str().into()); |
588 | + req.get().set_as_thread(true); |
589 | + } |
590 | + let result = req.send().promise.await?; |
591 | + let post = result.get()?.get_thread()?; |
592 | + Ok(post.to_vec()) |
593 | + }) |
594 | + .await?; |
595 | + let mut response = Bytes::from(thread).into_response(); |
596 | + response |
597 | + .headers_mut() |
598 | + .insert(CONTENT_TYPE, "application/mbox".parse().unwrap()); |
599 | + Ok(response) |
600 | + } else { |
601 | + Err(Error::NotFound(format!( |
602 | + "no mailing list with id: {} exists", |
603 | params.list_id |
604 | - ))), |
605 | - }?; |
606 | - let list_id = list.id.clone(); |
607 | - let mail_client = initiator.client(InitiatorKind::Mail).unwrap(); |
608 | - let thread = mail_client |
609 | - .invoke(move |c: MailClient| async move { |
610 | - let mut req = c.export_request(); |
611 | - req.get().set_id(list_id.as_str().into()); |
612 | - if let Some(message_id) = params.message_id { |
613 | - req.get().set_message_id(message_id.as_str().into()); |
614 | - req.get().set_as_thread(true); |
615 | - } |
616 | - let result = req.send().promise.await?; |
617 | - let post = result.get()?.get_thread()?; |
618 | - Ok(post.to_vec()) |
619 | - }) |
620 | - .await?; |
621 | - let mut response = Bytes::from(thread).into_response(); |
622 | - response |
623 | - .headers_mut() |
624 | - .insert(CONTENT_TYPE, "application/mbox".parse().unwrap()); |
625 | - Ok(response) |
626 | + ))) |
627 | + } |
628 | } |
629 | diff --git a/src/web2/routes/repo.rs b/src/web2/routes/repo.rs |
630 | index 1b4049a..43d156a 100644 |
631 | --- a/src/web2/routes/repo.rs |
632 | +++ b/src/web2/routes/repo.rs |
633 | @@ -246,6 +246,7 @@ pub async fn serve( |
634 | ctx.insert("chat_links", &chat_links); |
635 | |
636 | // find any associated mailing lists associated with this repository |
637 | + /* |
638 | let email_links = preamble.config.clone().mail.map(|mail| { |
639 | let links: Vec<EmailLink> = mail |
640 | .iter() |
641 | @@ -271,6 +272,7 @@ pub async fn serve( |
642 | }); |
643 | |
644 | ctx.insert("email_links", &email_links); |
645 | + */ |
646 | |
647 | let materialized = make_view( |
648 | &db, |
649 | diff --git a/themes/default/templates/lists.html b/themes/default/templates/lists.html |
650 | index 0196b96..afbcce5 100644 |
651 | --- a/themes/default/templates/lists.html |
652 | +++ b/themes/default/templates/lists.html |
653 | @@ -16,9 +16,9 @@ |
654 | <tbody> |
655 | {% for list in lists %} |
656 | <tr> |
657 | - <td>{{ list.id }}</td> |
658 | + <td><a href="/mail/{{list.id}}">{{ list.id }}</a></td> |
659 | <td>{{ list.name }}</td> |
660 | - <td><a href="/mail/{{list.id}}">{{ list.description }}</a></td> |
661 | + <td>{{ list.description }}</td> |
662 | <td>{{ list.address }}</td> |
663 | </tr> |
664 | {% endfor %} |
665 | diff --git a/themes/default/templates/threads.html b/themes/default/templates/threads.html |
666 | index 83470aa..1b2c733 100644 |
667 | --- a/themes/default/templates/threads.html |
668 | +++ b/themes/default/templates/threads.html |
669 | @@ -5,23 +5,19 @@ |
670 | <article> |
671 | <header> |
672 | <h1> {{ list.name }} </h1> |
673 | + <span class="right labels"> |
674 | + {% for topic in list.topics %} |
675 | + <span class="feature">{{topic}}</span> |
676 | + {% endfor %} |
677 | + </span> |
678 | </header> |
679 | <div class="mailing-list-details"> |
680 | <h4> {{ list.description }} </h4> |
681 | </br> |
682 | - <span class="labels"> |
683 | - {% for topic in list.topics %} |
684 | - <span class="feature">{{topic}}</span> |
685 | - {% endfor %} |
686 | - </br><b>Post Policy = {{list.post_policy}} </b> |
687 | - </br><b>Subscription Policy = {{list.subscription_policy.kind}}</b> |
688 | - </br><b>Send Confirmation = {{list.subscription_policy.send_confirmation}}</b> |
689 | - </br> |
690 | - </span></br> |
691 | <h4> Subscribe </h4> |
692 | - <p> Send an e-mail to <a href="mailto:{{ request_email }}?subject=subscribe">{{request_email}}</a> with the following subject: <code>subscribe</code> </p> |
693 | + <p> Send an e-mail to <a href="mailto:{{request_address}}?subject=subscribe">{{request_address}}</a> with the following subject: <code>subscribe</code> </p> |
694 | <h4> Unsubscribe </h4> |
695 | - <p> Send an e-mail to <a href="mailto:{{ request_email }}?subject=unsubscribe">{{request_email}}</a> with the following subject: <code>unsubscribe</code> </p> |
696 | + <p> Send an e-mail to <a href="mailto:{{ request_address}}?subject=unsubscribe">{{request_address}}</a> with the following subject: <code>unsubscribe</code> </p> |
697 | <h4> Export List </h4> |
698 | <p><a href="/mail/export/{{list.id}}">mbox</a></p> |
699 | </div> |