+116 -53 +/-6 browse
1 | diff --git a/Cargo.lock b/Cargo.lock |
2 | index cf366b1..51e6494 100644 |
3 | --- a/Cargo.lock |
4 | +++ b/Cargo.lock |
5 | @@ -2019,14 +2019,17 @@ dependencies = [ |
6 | "axum-sessions", |
7 | "build-info", |
8 | "build-info-build", |
9 | + "cfg-if 1.0.0", |
10 | "chrono", |
11 | "convert_case", |
12 | "dyn-clone", |
13 | "eyre", |
14 | "http", |
15 | + "hyper", |
16 | "indexmap", |
17 | "lazy_static", |
18 | "mailpot", |
19 | + "mailpot-tests", |
20 | "minijinja", |
21 | "percent-encoding", |
22 | "rand", |
23 | @@ -2034,6 +2037,7 @@ dependencies = [ |
24 | "serde_json", |
25 | "tempfile", |
26 | "tokio", |
27 | + "tower", |
28 | "tower-http 0.3.5", |
29 | "tower-service", |
30 | "zstd", |
31 | diff --git a/web/Cargo.toml b/web/Cargo.toml |
32 | index 57c48e5..597c199 100644 |
33 | --- a/web/Cargo.toml |
34 | +++ b/web/Cargo.toml |
35 | @@ -14,16 +14,13 @@ categories = ["email"] |
36 | name = "mpot-web" |
37 | path = "src/main.rs" |
38 | |
39 | - [features] |
40 | - default = ["zstd"] |
41 | - zstd = ["dep:zstd"] |
42 | - |
43 | [dependencies] |
44 | axum = { version = "^0.6" } |
45 | axum-extra = { version = "^0.7", features = ["typed-routing"] } |
46 | axum-login = { version = "^0.5" } |
47 | axum-sessions = { version = "^0.5" } |
48 | build-info = { version = "0.0.31" } |
49 | + cfg-if = { version = "1" } |
50 | chrono = { version = "^0.4" } |
51 | convert_case = { version = "^0.4" } |
52 | dyn-clone = { version = "^1" } |
53 | @@ -41,8 +38,14 @@ tempfile = { version = "^3.5" } |
54 | tokio = { version = "1", features = ["full"] } |
55 | tower-http = { version = "^0.3" } |
56 | tower-service = { version = "^0.3" } |
57 | - zstd = { version = "0.12.3", optional = true, default-features = false } |
58 | + zstd = { version = "0.12", default-features = false } |
59 | + |
60 | + [dev-dependencies] |
61 | + hyper = { version = "0.14" } |
62 | + mailpot-tests = { version = "^0.1", path = "../mailpot-tests" } |
63 | + tempfile = "3.3" |
64 | + tower = { version = "^0.4" } |
65 | |
66 | [build-dependencies] |
67 | build-info-build = { version = "0.0.31" } |
68 | - zstd = { version = "0.12.3", optional = true, default-features = false } |
69 | + zstd = { version = "0.12", default-features = false } |
70 | diff --git a/web/README.md b/web/README.md |
71 | index 5495c3d..c54e80c 100644 |
72 | --- a/web/README.md |
73 | +++ b/web/README.md |
74 | @@ -4,9 +4,7 @@ |
75 | cargo run --bin mpot-web -- /path/to/conf.toml |
76 | ``` |
77 | |
78 | - By default on release builds templates are compressed with `zstd` and bundled in the binary. |
79 | - |
80 | - You can disable this behavior by disabling the `zstd` feature: `cargo build --release --no-default-features` |
81 | + Templates are compressed with `zstd` and bundled in the binary. |
82 | |
83 | ## Configuration |
84 | |
85 | diff --git a/web/build.rs b/web/build.rs |
86 | index bd29123..5008bdc 100644 |
87 | --- a/web/build.rs |
88 | +++ b/web/build.rs |
89 | @@ -44,7 +44,6 @@ fn commit_sha() { |
90 | } |
91 | } |
92 | |
93 | - #[cfg(feature = "zstd")] |
94 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
95 | // Embed HTML templates as zstd compressed byte slices into binary. |
96 | // [tag:embed_templates] |
97 | @@ -104,8 +103,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { |
98 | commit_sha(); |
99 | Ok(()) |
100 | } |
101 | - |
102 | - #[cfg(not(feature = "zstd"))] |
103 | - fn main() { |
104 | - commit_sha(); |
105 | - } |
106 | diff --git a/web/src/main.rs b/web/src/main.rs |
107 | index b0c3223..a6de0c7 100644 |
108 | --- a/web/src/main.rs |
109 | +++ b/web/src/main.rs |
110 | @@ -26,19 +26,7 @@ use minijinja::value::Value; |
111 | use rand::Rng; |
112 | use tokio::sync::RwLock; |
113 | |
114 | - #[tokio::main] |
115 | - async fn main() { |
116 | - let config_path = std::env::args() |
117 | - .nth(1) |
118 | - .expect("Expected configuration file path as first argument."); |
119 | - if ["-v", "--version", "info"].contains(&config_path.as_str()) { |
120 | - println!("{}", crate::get_git_sha()); |
121 | - println!("{CLI_INFO}"); |
122 | - |
123 | - return; |
124 | - } |
125 | - let conf = Configuration::from_file(config_path).unwrap(); |
126 | - |
127 | + fn create_app(conf: Configuration) -> Router { |
128 | let store = MemoryStore::new(); |
129 | let secret = rand::thread_rng().gen::<[u8; 128]>(); |
130 | let session_layer = SessionLayer::new(store, &secret).with_secure(false); |
131 | @@ -60,7 +48,7 @@ async fn main() { |
132 | |
133 | let login_url = |
134 | Arc::new(format!("{}{}", shared_state.root_url_prefix, LoginPath.to_crumb()).into()); |
135 | - let app = Router::new() |
136 | + Router::new() |
137 | .route("/", get(root)) |
138 | .typed_get(list) |
139 | .typed_get(list_post) |
140 | @@ -152,7 +140,22 @@ async fn main() { |
141 | ) |
142 | .layer(auth_layer) |
143 | .layer(session_layer) |
144 | - .with_state(shared_state); |
145 | + .with_state(shared_state) |
146 | + } |
147 | + |
148 | + #[tokio::main] |
149 | + async fn main() { |
150 | + let config_path = std::env::args() |
151 | + .nth(1) |
152 | + .expect("Expected configuration file path as first argument."); |
153 | + if ["-v", "--version", "info"].contains(&config_path.as_str()) { |
154 | + println!("{}", crate::get_git_sha()); |
155 | + println!("{CLI_INFO}"); |
156 | + |
157 | + return; |
158 | + } |
159 | + let conf = Configuration::from_file(config_path).unwrap(); |
160 | + let app = create_app(conf); |
161 | |
162 | let hostname = std::env::var("HOSTNAME").unwrap_or_else(|_| "0.0.0.0".to_string()); |
163 | let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string()); |
164 | @@ -206,3 +209,78 @@ async fn root( |
165 | }; |
166 | Ok(Html(TEMPLATES.get_template("lists.html")?.render(context)?)) |
167 | } |
168 | + |
169 | + #[cfg(test)] |
170 | + mod tests { |
171 | + |
172 | + use axum::{ |
173 | + body::Body, |
174 | + http::{method::Method, Request, StatusCode}, |
175 | + }; |
176 | + use mailpot::{Configuration, Connection, SendMail}; |
177 | + use mailpot_tests::init_stderr_logging; |
178 | + use tempfile::TempDir; |
179 | + use tower::ServiceExt; |
180 | + |
181 | + use super::*; |
182 | + |
183 | + #[tokio::test] |
184 | + async fn test_routes() { |
185 | + init_stderr_logging(); |
186 | + |
187 | + let tmp_dir = TempDir::new().unwrap(); |
188 | + |
189 | + let db_path = tmp_dir.path().join("mpot.db"); |
190 | + std::fs::copy("../mailpot-tests/for_testing.db", &db_path).unwrap(); |
191 | + let config = Configuration { |
192 | + send_mail: SendMail::ShellCommand("/usr/bin/false".to_string()), |
193 | + db_path, |
194 | + data_path: tmp_dir.path().to_path_buf(), |
195 | + administrators: vec![], |
196 | + }; |
197 | + let db = Connection::open_db(config.clone()).unwrap(); |
198 | + let list = db.lists().unwrap().remove(0); |
199 | + |
200 | + // ------------------------------------------------------------ |
201 | + // list() |
202 | + |
203 | + let cl = |url, config| async move { |
204 | + let response = create_app(config) |
205 | + .oneshot( |
206 | + Request::builder() |
207 | + .uri(&url) |
208 | + .method(Method::GET) |
209 | + .body(Body::empty()) |
210 | + .unwrap(), |
211 | + ) |
212 | + .await |
213 | + .unwrap(); |
214 | + |
215 | + assert_eq!(response.status(), StatusCode::OK); |
216 | + |
217 | + hyper::body::to_bytes(response.into_body()).await.unwrap() |
218 | + }; |
219 | + assert_eq!( |
220 | + cl(format!("/list/{}/", list.id), config.clone()).await, |
221 | + cl(format!("/list/{}/", list.pk), config.clone()).await |
222 | + ); |
223 | + |
224 | + // ------------------------------------------------------------ |
225 | + // help(), ssh_signin(), root() |
226 | + |
227 | + for path in ["/help/", "/login/", "/"] { |
228 | + let response = create_app(config.clone()) |
229 | + .oneshot( |
230 | + Request::builder() |
231 | + .uri(path) |
232 | + .method(Method::GET) |
233 | + .body(Body::empty()) |
234 | + .unwrap(), |
235 | + ) |
236 | + .await |
237 | + .unwrap(); |
238 | + |
239 | + assert_eq!(response.status(), StatusCode::OK); |
240 | + } |
241 | + } |
242 | + } |
243 | diff --git a/web/src/minijinja_utils.rs b/web/src/minijinja_utils.rs |
244 | index 684ffb6..04da2d1 100644 |
245 | --- a/web/src/minijinja_utils.rs |
246 | +++ b/web/src/minijinja_utils.rs |
247 | @@ -21,8 +21,6 @@ |
248 | |
249 | use super::*; |
250 | |
251 | - #[cfg(feature = "zstd")] |
252 | - #[cfg(not(debug_assertions))] |
253 | mod compressed; |
254 | |
255 | lazy_static::lazy_static! { |
256 | @@ -54,28 +52,16 @@ lazy_static::lazy_static! { |
257 | post_eml_path |
258 | ); |
259 | add!(filter pluralize); |
260 | - #[cfg(not(feature = "zstd"))] |
261 | - #[cfg(debug_assertions)] |
262 | - env.set_source(minijinja::Source::from_path("web/src/templates/")); |
263 | - #[cfg(feature = "zstd")] |
264 | - #[cfg(debug_assertions)] |
265 | - env.set_source(minijinja::Source::from_path("web/src/templates/")); |
266 | - #[cfg(not(feature = "zstd"))] |
267 | - #[cfg(not(debug_assertions))] |
268 | - env.set_source(minijinja::Source::from_path("web/src/templates/")); |
269 | - #[cfg(feature = "zstd")] |
270 | - #[cfg(not(debug_assertions))] |
271 | - { |
272 | - // Load compressed templates. They are constructed in build.rs. See |
273 | - // [ref:embed_templates] |
274 | - let mut source = minijinja::Source::new(); |
275 | - for (name, bytes) in compressed::COMPRESSED { |
276 | - let mut de_bytes = vec![]; |
277 | - zstd::stream::copy_decode(*bytes,&mut de_bytes).unwrap(); |
278 | - source.add_template(*name, String::from_utf8(de_bytes).unwrap()).unwrap(); |
279 | - } |
280 | - env.set_source(source); |
281 | + // Load compressed templates. They are constructed in build.rs. See |
282 | + // [ref:embed_templates] |
283 | + let mut source = minijinja::Source::new(); |
284 | + for (name, bytes) in compressed::COMPRESSED { |
285 | + let mut de_bytes = vec![]; |
286 | + zstd::stream::copy_decode(*bytes,&mut de_bytes).unwrap(); |
287 | + source.add_template(*name, String::from_utf8(de_bytes).unwrap()).unwrap(); |
288 | } |
289 | + env.set_source(source); |
290 | + |
291 | env.add_global("root_url_prefix", Value::from_safe_string( std::env::var("ROOT_URL_PREFIX").unwrap_or_default())); |
292 | env.add_global("public_url",Value::from_safe_string(std::env::var("PUBLIC_URL").unwrap_or_default())); |
293 | env.add_global("site_title", Value::from_safe_string(std::env::var("SITE_TITLE").unwrap_or_else(|_| "mailing list archive".to_string()))); |