archive-http: add static gen binary
parent
5409c9de71
commit
1e01a4b3d4
|
@ -15,6 +15,10 @@ default-run = "mpot-archives"
|
|||
name = "mpot-archives"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mpot-gen"
|
||||
path = "src/gen.rs"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "*"
|
||||
mailpot = { version = "0.1.0", path = "../core" }
|
||||
|
|
|
@ -3,3 +3,10 @@
|
|||
```shell
|
||||
cargo run --bin mpot-archives
|
||||
```
|
||||
|
||||
## generate static files
|
||||
|
||||
```shell
|
||||
# mpot-gen CONF_FILE OUTPUT_DIR OPTIONAL_ROOT_URL_PREFIX
|
||||
cargo run --bin mpot-gen -- ../conf.toml ./out/ "/mailpot"
|
||||
```
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* This file is part of mailpot
|
||||
*
|
||||
* Copyright 2020 - Manos Pitsidianakis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
extern crate mailpot;
|
||||
|
||||
pub use mailpot::config::*;
|
||||
pub use mailpot::db::*;
|
||||
pub use mailpot::errors::*;
|
||||
pub use mailpot::models::*;
|
||||
pub use mailpot::*;
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
|
||||
use minijinja::{Environment, Source};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref TEMPLATES: Environment<'static> = {
|
||||
let mut env = Environment::new();
|
||||
env.set_source(Source::from_path("src/templates/"));
|
||||
|
||||
env
|
||||
};
|
||||
}
|
||||
|
||||
fn run_app() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let Some(config_path) = args
|
||||
.get(1) else {
|
||||
return Err("Expected configuration file path as first argument.".into());
|
||||
};
|
||||
let Some(output_path) = args
|
||||
.get(2) else {
|
||||
return Err("Expected output dir path as second argument.".into());
|
||||
};
|
||||
let root_url_prefix = args.get(3).cloned().unwrap_or_default();
|
||||
|
||||
let output_path = std::path::Path::new(&output_path);
|
||||
if output_path.exists() && !output_path.is_dir() {
|
||||
return Err("Output path is not a directory.".into());
|
||||
}
|
||||
|
||||
std::fs::create_dir_all(&output_path.join("lists"))?;
|
||||
std::fs::create_dir_all(&output_path.join("list"))?;
|
||||
let conf = Configuration::from_file(config_path)
|
||||
.map_err(|err| format!("Could not load config {config_path}: {err}"))?;
|
||||
|
||||
let db = Database::open_db(&conf).map_err(|err| format!("Couldn't open db: {err}"))?;
|
||||
let lists_values = db.list_lists()?;
|
||||
{
|
||||
//index.html
|
||||
|
||||
let lists = lists_values
|
||||
.iter()
|
||||
.map(|list| {
|
||||
let months = db.months(list.pk).unwrap();
|
||||
let posts = db.list_posts(list.pk, None).unwrap();
|
||||
minijinja::context! {
|
||||
title => &list.name,
|
||||
list => &list,
|
||||
posts => &posts,
|
||||
months => &months,
|
||||
body => &list.description.as_deref().unwrap_or_default(),
|
||||
root_prefix => &root_url_prefix,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&output_path.join("index.html"))?;
|
||||
|
||||
let context = minijinja::context! {
|
||||
title => "mailing list archive",
|
||||
description => "",
|
||||
lists => &lists,
|
||||
root_prefix => &root_url_prefix,
|
||||
};
|
||||
file.write_all(
|
||||
TEMPLATES
|
||||
.get_template("lists.html")?
|
||||
.render(context)?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut lists_path = output_path.to_path_buf();
|
||||
dbg!(&lists_values);
|
||||
for list in &lists_values {
|
||||
lists_path.push("lists");
|
||||
lists_path.push(list.pk.to_string());
|
||||
std::fs::create_dir_all(&lists_path)?;
|
||||
lists_path.push("index.html");
|
||||
|
||||
let list = db
|
||||
.get_list(list.pk)?
|
||||
.ok_or_else(|| format!("List with pk {} not found in database", list.pk))?;
|
||||
let months = db.months(list.pk)?;
|
||||
let posts = db.list_posts(list.pk, None)?;
|
||||
let posts_ctx = posts
|
||||
.iter()
|
||||
.map(|post| {
|
||||
let envelope = melib::Envelope::from_bytes(post.message.as_slice(), None)
|
||||
.expect("Could not parse mail");
|
||||
let mut msg_id = &post.message_id[1..];
|
||||
msg_id = &msg_id[..msg_id.len().saturating_sub(1)];
|
||||
minijinja::context! {
|
||||
pk => post.pk,
|
||||
list => post.list,
|
||||
subject => envelope.subject(),
|
||||
address=> post.address,
|
||||
message_id => msg_id,
|
||||
message => post.message,
|
||||
timestamp => post.timestamp,
|
||||
datetime => post.datetime,
|
||||
root_prefix => &root_url_prefix,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let context = minijinja::context! {
|
||||
title=> &list.name,
|
||||
list=> &list,
|
||||
months=> &months,
|
||||
posts=> posts_ctx,
|
||||
body=>&list.description.clone().unwrap_or_default(),
|
||||
root_prefix => &root_url_prefix,
|
||||
};
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&lists_path)
|
||||
.map_err(|err| format!("could not open {lists_path:?}: {err}"))?;
|
||||
file.write_all(
|
||||
TEMPLATES
|
||||
.get_template("list.html")?
|
||||
.render(context)?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
lists_path.pop();
|
||||
lists_path.pop();
|
||||
lists_path.pop();
|
||||
lists_path.push("list");
|
||||
lists_path.push(list.pk.to_string());
|
||||
std::fs::create_dir_all(&lists_path)?;
|
||||
|
||||
for post in posts {
|
||||
let mut msg_id = &post.message_id[1..];
|
||||
msg_id = &msg_id[..msg_id.len().saturating_sub(1)];
|
||||
lists_path.push(format!("{msg_id}.html"));
|
||||
let envelope = melib::Envelope::from_bytes(post.message.as_slice(), None)
|
||||
.map_err(|err| format!("Could not parse mail {}: {err}", post.message_id))?;
|
||||
let body = envelope.body_bytes(post.message.as_slice());
|
||||
let body_text = body.text();
|
||||
let context = minijinja::context! {
|
||||
title => &list.name,
|
||||
list => &list,
|
||||
post => &post,
|
||||
posts => &posts_ctx,
|
||||
body => &body_text,
|
||||
from => &envelope.field_from_to_string(),
|
||||
date => &envelope.date_as_str(),
|
||||
to => &envelope.field_to_to_string(),
|
||||
subject => &envelope.subject(),
|
||||
in_reply_to => &envelope.in_reply_to_display().map(|r| r.to_string()),
|
||||
references => &envelope .references() .into_iter() .map(|m| m.to_string()) .collect::<Vec<String>>(),
|
||||
root_prefix => &root_url_prefix,
|
||||
};
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&lists_path)
|
||||
.map_err(|err| format!("could not open {lists_path:?}: {err}"))?;
|
||||
file.write_all(
|
||||
TEMPLATES
|
||||
.get_template("post.html")?
|
||||
.render(context)?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
lists_path.pop();
|
||||
}
|
||||
lists_path.pop();
|
||||
lists_path.pop();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> std::result::Result<(), i64> {
|
||||
if let Err(err) = run_app() {
|
||||
eprintln!("{err}");
|
||||
return Err(-1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -40,56 +40,6 @@ lazy_static::lazy_static! {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Template)]
|
||||
#[template(path = "lists.html")]
|
||||
struct ListsTemplate<'a> {
|
||||
title: &'a str,
|
||||
description: &'a str,
|
||||
lists_len: usize,
|
||||
lists: Vec<ListTemplate<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "list.html")]
|
||||
struct ListTemplate<'a> {
|
||||
title: &'a str,
|
||||
list: &'a DbVal<MailingList>,
|
||||
posts: Vec<DbVal<Post>>,
|
||||
months: Vec<String>,
|
||||
body: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Into<ListTemplate<'a>> for (&'a DbVal<MailingList>, &'a Database) {
|
||||
fn into(self: (&'a DbVal<MailingList>, &'a Database)) -> ListTemplate<'a> {
|
||||
let (list, db) = self;
|
||||
let months = db.months(list.pk).unwrap();
|
||||
let posts = db.list_posts(list.pk, None).unwrap();
|
||||
ListTemplate {
|
||||
title: &list.name,
|
||||
list,
|
||||
posts,
|
||||
months,
|
||||
body: list.description.as_deref().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "post.html")]
|
||||
struct PostTemplate<'a> {
|
||||
title: &'a str,
|
||||
_list: &'a DbVal<MailingList>,
|
||||
_post: DbVal<Post>,
|
||||
body: &'a str,
|
||||
_from: &'a str,
|
||||
_to: &'a str,
|
||||
subject: &'a str,
|
||||
_in_reply_to: Option<String>,
|
||||
_references: Vec<String>,
|
||||
}
|
||||
*/
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let config_path = std::env::args()
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="body">
|
||||
<ul>
|
||||
{% for l in lists %}
|
||||
<li><a href="/lists/{{ l.list.pk }}/">{{l.title}}</a></li>
|
||||
<li><a href="{{ root_prefix|safe }}/lists/{{ l.list.pk }}/">{{l.title}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<h2>Months</h2>
|
||||
<ul>
|
||||
{% for month in months %}
|
||||
<li><a href="/list/{{list.pk}}/{{ month }}">{{ month }}</a></li>
|
||||
<li><a href="{{ root_prefix|safe }}/list/{{list.pk}}/{{ month }}">{{ month }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<h2>Posts</h2>
|
||||
|
@ -12,7 +12,7 @@
|
|||
{% for post in posts %}
|
||||
<li><span>From: <{{ post.address }}></span><br />
|
||||
<span>Date: {{ post.datetime }}</span><br />
|
||||
<span>Subject: <a href="/list/{{post.list}}/{{ post.message_id }}">{{ post.subject }}</a></span></br><span>Message-ID: {{ post.message_id }}</span>
|
||||
<span>Subject: <a href="{{ root_prefix|safe }}/list/{{post.list}}/{{ post.message_id }}.html">{{ post.subject }}</a></span></br><span>Message-ID: {{ post.message_id }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="body">
|
||||
<ul>
|
||||
{% for l in lists %}
|
||||
<li><a href="/lists/{{ l.list.pk }}/">{{l.title}}</a></li>
|
||||
<li><a href="{{ root_prefix|safe }}/lists/{{ l.list.pk }}/">{{l.title}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue