You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
Refactor templates loading & implement templates hot-reload
This commit is contained in:
103
Cargo.lock
generated
103
Cargo.lock
generated
@ -319,11 +319,24 @@ version = "1.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"iovec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
@ -843,6 +856,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.18"
|
version = "0.3.18"
|
||||||
@ -931,6 +950,7 @@ version = "0.3.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
|
checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures 0.1.31",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
@ -1014,7 +1034,7 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55"
|
checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@ -1073,7 +1093,7 @@ checksum = "a4c4eb0471fcb85846d8b0690695ef354f9afb11cb03cac2e1d7c9253351afb0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"headers-core",
|
"headers-core",
|
||||||
"http",
|
"http",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
@ -1130,7 +1150,7 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
|
checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
@ -1141,7 +1161,7 @@ version = "0.4.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
|
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"http",
|
"http",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
@ -1176,7 +1196,7 @@ version = "0.14.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c"
|
checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1294,6 +1314,15 @@ version = "1.1.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48dc51180a9b377fd75814d0cc02199c20f8e99433d6762f650d39cdbbd3b56f"
|
checksum = "48dc51180a9b377fd75814d0cc02199c20f8e99433d6762f650d39cdbbd3b56f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iovec"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -1435,7 +1464,7 @@ dependencies = [
|
|||||||
"argon2",
|
"argon2",
|
||||||
"clap",
|
"clap",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"futures",
|
"futures 0.3.18",
|
||||||
"hyper",
|
"hyper",
|
||||||
"indoc",
|
"indoc",
|
||||||
"mas-config",
|
"mas-config",
|
||||||
@ -1458,6 +1487,7 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
"warp",
|
"warp",
|
||||||
|
"watchman_client",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1826,7 +1856,7 @@ dependencies = [
|
|||||||
"crossbeam-channel 0.5.1",
|
"crossbeam-channel 0.5.1",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures",
|
"futures 0.3.18",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
@ -1844,7 +1874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d50ceb0b0e8b75cb3e388a2571a807c8228dabc5d6670f317b6eb21301095373"
|
checksum = "d50ceb0b0e8b75cb3e388a2571a807c8228dabc5d6670f317b6eb21301095373"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
@ -1877,7 +1907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f19d4b43842433c420c548c985d158f5628bba5b518e0be64627926d19889992"
|
checksum = "f19d4b43842433c420c548c985d158f5628bba5b518e0be64627926d19889992"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"futures",
|
"futures 0.3.18",
|
||||||
"http",
|
"http",
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
"prost",
|
"prost",
|
||||||
@ -2265,7 +2295,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020"
|
checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"prost-derive",
|
"prost-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2275,7 +2305,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603"
|
checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"heck",
|
"heck",
|
||||||
"itertools",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
@ -2306,7 +2336,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b"
|
checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"prost",
|
"prost",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2432,7 +2462,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "07bea77bc708afa10e59905c3d4af7c8fd43c9214251673095ff8b14345fcbc5"
|
checksum = "07bea77bc708afa10e59905c3d4af7c8fd43c9214251673095ff8b14345fcbc5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2695,6 +2725,19 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_bser"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b929ea725591083cbca8b8ea178ed6efc918eccd40b784e199ce88967104199"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
|
"bytes 0.4.12",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_cbor"
|
name = "serde_cbor"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -2944,7 +2987,7 @@ dependencies = [
|
|||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
"crossbeam-channel 0.5.1",
|
"crossbeam-channel 0.5.1",
|
||||||
@ -3327,7 +3370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144"
|
checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.1",
|
"autocfg 1.0.1",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mio",
|
"mio",
|
||||||
@ -3413,11 +3456,13 @@ version = "0.6.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
|
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3430,7 +3475,7 @@ dependencies = [
|
|||||||
"async-stream",
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
@ -3495,7 +3540,7 @@ dependencies = [
|
|||||||
"async-compression",
|
"async-compression",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
@ -3625,7 +3670,7 @@ checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
@ -3855,7 +3900,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e"
|
checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.1.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"headers",
|
"headers",
|
||||||
@ -3951,6 +3996,24 @@ version = "0.2.78"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "watchman_client"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1afbab1186833c9b34f64132b80ed4b373ed4eab6f9efa1f55430835200f0a28"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bytes 1.1.0",
|
||||||
|
"futures 0.3.18",
|
||||||
|
"maplit",
|
||||||
|
"serde",
|
||||||
|
"serde_bser",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.55"
|
version = "0.3.55"
|
||||||
|
@ -34,12 +34,14 @@ opentelemetry-zipkin = { version = "0.14.0", features = ["reqwest-client", "reqw
|
|||||||
mas-config = { path = "../config" }
|
mas-config = { path = "../config" }
|
||||||
mas-core = { path = "../core" }
|
mas-core = { path = "../core" }
|
||||||
mas-templates = { path = "../templates" }
|
mas-templates = { path = "../templates" }
|
||||||
|
watchman_client = "0.7.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["otlp", "jaeger", "zipkin"]
|
default = ["otlp", "jaeger", "zipkin"]
|
||||||
|
dev = ["mas-templates/dev"]
|
||||||
# Enable OpenTelemetry OTLP exporter. Requires "protoc"
|
# Enable OpenTelemetry OTLP exporter. Requires "protoc"
|
||||||
otlp = ["opentelemetry-otlp"]
|
otlp = ["opentelemetry-otlp"]
|
||||||
# Enable OpenTelemetry Jaeger exporter and propagator.
|
# Enable OpenTelemetry Jaeger exporter and propagator.
|
||||||
|
@ -19,6 +19,7 @@ use std::{
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use futures::{future::TryFutureExt, stream::TryStreamExt};
|
||||||
use hyper::{header, Server, Version};
|
use hyper::{header, Server, Version};
|
||||||
use mas_config::RootConfig;
|
use mas_config::RootConfig;
|
||||||
use mas_core::{
|
use mas_core::{
|
||||||
@ -33,7 +34,7 @@ use tower_http::{
|
|||||||
sensitive_headers::SetSensitiveHeadersLayer,
|
sensitive_headers::SetSensitiveHeadersLayer,
|
||||||
trace::{MakeSpan, OnResponse, TraceLayer},
|
trace::{MakeSpan, OnResponse, TraceLayer},
|
||||||
};
|
};
|
||||||
use tracing::{field, info};
|
use tracing::{error, field, info};
|
||||||
|
|
||||||
use super::RootCommand;
|
use super::RootCommand;
|
||||||
|
|
||||||
@ -42,6 +43,10 @@ pub(super) struct ServerCommand {
|
|||||||
/// Automatically apply pending migrations
|
/// Automatically apply pending migrations
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
migrate: bool,
|
migrate: bool,
|
||||||
|
|
||||||
|
/// Watch for changes for templates on the filesystem
|
||||||
|
#[clap(short, long)]
|
||||||
|
watch: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
@ -136,6 +141,76 @@ async fn shutdown_signal() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Watch for changes in the templates folders
|
||||||
|
async fn watch_templates(
|
||||||
|
client: &watchman_client::Client,
|
||||||
|
templates: &Templates,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
use watchman_client::{
|
||||||
|
fields::NameOnly,
|
||||||
|
pdu::{QueryResult, SubscribeRequest},
|
||||||
|
CanonicalPath, SubscriptionData,
|
||||||
|
};
|
||||||
|
|
||||||
|
let templates = templates.clone();
|
||||||
|
|
||||||
|
// Find which roots we're supposed to watch
|
||||||
|
let roots = templates.watch_roots().await;
|
||||||
|
let mut streams = Vec::new();
|
||||||
|
|
||||||
|
for root in roots {
|
||||||
|
// For each root, create a subscription
|
||||||
|
let resolved = client
|
||||||
|
.resolve_root(CanonicalPath::canonicalize(root)?)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// TODO: we could subscribe to less, properly filter here
|
||||||
|
let (subscription, _) = client
|
||||||
|
.subscribe::<NameOnly>(&resolved, SubscribeRequest::default())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Create a stream out of that subscription
|
||||||
|
let stream = futures::stream::try_unfold(subscription, |mut sub| async move {
|
||||||
|
let next = sub.next().await?;
|
||||||
|
anyhow::Ok(Some((next, sub)))
|
||||||
|
});
|
||||||
|
|
||||||
|
streams.push(Box::pin(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
let files_changed_stream =
|
||||||
|
futures::stream::select_all(streams).try_filter_map(|event| async move {
|
||||||
|
match event {
|
||||||
|
SubscriptionData::FilesChanged(QueryResult {
|
||||||
|
files: Some(files), ..
|
||||||
|
}) => {
|
||||||
|
let files: Vec<_> = files.into_iter().map(|f| f.name.into_inner()).collect();
|
||||||
|
Ok(Some(files))
|
||||||
|
}
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let fut = files_changed_stream
|
||||||
|
.try_for_each(move |files| {
|
||||||
|
let templates = templates.clone();
|
||||||
|
async move {
|
||||||
|
info!(?files, "Files changed, reloading templates");
|
||||||
|
|
||||||
|
templates
|
||||||
|
.clone()
|
||||||
|
.reload()
|
||||||
|
.await
|
||||||
|
.context("Could not reload templates")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.inspect_err(|err| error!(%err, "Error while watching templates, stop watching"));
|
||||||
|
|
||||||
|
tokio::spawn(fut);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
impl ServerCommand {
|
impl ServerCommand {
|
||||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||||
let config: RootConfig = root.load_config()?;
|
let config: RootConfig = root.load_config()?;
|
||||||
@ -160,8 +235,21 @@ impl ServerCommand {
|
|||||||
queue.start();
|
queue.start();
|
||||||
|
|
||||||
// Load and compile the templates
|
// Load and compile the templates
|
||||||
let templates =
|
let templates = Templates::load_from_config(&config.templates)
|
||||||
Templates::load_from_config(&config.templates).context("could not load templates")?;
|
.await
|
||||||
|
.context("could not load templates")?;
|
||||||
|
|
||||||
|
// Watch for changes in templates if the --watch flag is present
|
||||||
|
if self.watch {
|
||||||
|
let client = watchman_client::Connector::new()
|
||||||
|
.connect()
|
||||||
|
.await
|
||||||
|
.context("could not connect to watchman")?;
|
||||||
|
|
||||||
|
watch_templates(&client, &templates)
|
||||||
|
.await
|
||||||
|
.context("could not watch for templates changes")?;
|
||||||
|
}
|
||||||
|
|
||||||
// Start the server
|
// Start the server
|
||||||
let root = mas_core::handlers::root(&pool, &templates, &config);
|
let root = mas_core::handlers::root(&pool, &templates, &config);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use mas_config::TemplatesConfig;
|
||||||
use mas_templates::Templates;
|
use mas_templates::Templates;
|
||||||
|
|
||||||
use super::RootCommand;
|
use super::RootCommand;
|
||||||
@ -59,8 +60,12 @@ impl TemplatesCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SC::Check { path, skip_builtin } => {
|
SC::Check { path, skip_builtin } => {
|
||||||
let templates = Templates::load(Some(path), !skip_builtin)?;
|
let config = TemplatesConfig {
|
||||||
templates.check_render()?;
|
path: Some(path.to_string()),
|
||||||
|
builtin: !skip_builtin,
|
||||||
|
};
|
||||||
|
let templates = Templates::load_from_config(&config).await?;
|
||||||
|
templates.check_render().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ fn default_builtin() -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||||
pub struct TemplatesConfig {
|
pub struct TemplatesConfig {
|
||||||
/// Path to the folder that holds the custom templates
|
/// Path to the folder that holds the custom templates
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -96,7 +96,7 @@ enum ReplyOrBackToClient {
|
|||||||
Error(Box<dyn OAuth2Error>),
|
Error(Box<dyn OAuth2Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn back_to_client<T>(
|
async fn back_to_client<T>(
|
||||||
mut redirect_uri: Url,
|
mut redirect_uri: Url,
|
||||||
response_mode: ResponseMode,
|
response_mode: ResponseMode,
|
||||||
state: Option<String>,
|
state: Option<String>,
|
||||||
@ -175,7 +175,7 @@ where
|
|||||||
ResponseMode::FormPost => {
|
ResponseMode::FormPost => {
|
||||||
let merged = ParamsWithState { state, params };
|
let merged = ParamsWithState { state, params };
|
||||||
let ctx = FormPostContext::new(redirect_uri, merged);
|
let ctx = FormPostContext::new(redirect_uri, merged);
|
||||||
let rendered = templates.render_form_post(&ctx)?;
|
let rendered = templates.render_form_post(&ctx).await?;
|
||||||
Ok(Box::new(html(rendered)))
|
Ok(Box::new(html(rendered)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,19 +288,19 @@ async fn actually_reply(
|
|||||||
|
|
||||||
let client = match client {
|
let client = match client {
|
||||||
Some(client) => client,
|
Some(client) => client,
|
||||||
None => return Ok(Box::new(html(templates.render_error(&error.into())?))),
|
None => return Ok(Box::new(html(templates.render_error(&error.into()).await?))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let redirect_uri: Result<Option<Url>, _> = redirect_uri.map(|r| r.parse()).transpose();
|
let redirect_uri: Result<Option<Url>, _> = redirect_uri.map(|r| r.parse()).transpose();
|
||||||
let redirect_uri = match redirect_uri {
|
let redirect_uri = match redirect_uri {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(_) => return Ok(Box::new(html(templates.render_error(&error.into())?))),
|
Err(_) => return Ok(Box::new(html(templates.render_error(&error.into()).await?))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let redirect_uri = client.resolve_redirect_uri(&redirect_uri);
|
let redirect_uri = client.resolve_redirect_uri(&redirect_uri);
|
||||||
let redirect_uri = match redirect_uri {
|
let redirect_uri = match redirect_uri {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(_) => return Ok(Box::new(html(templates.render_error(&error.into())?))),
|
Err(_) => return Ok(Box::new(html(templates.render_error(&error.into()).await?))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let reply: ErrorResponse = error.into();
|
let reply: ErrorResponse = error.into();
|
||||||
@ -310,7 +310,9 @@ async fn actually_reply(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
back_to_client(redirect_uri, response_mode, state, params, &templates).wrap_error()
|
back_to_client(redirect_uri, response_mode, state, params, &templates)
|
||||||
|
.await
|
||||||
|
.wrap_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get(
|
async fn get(
|
||||||
|
@ -58,7 +58,7 @@ async fn get(
|
|||||||
.maybe_with_session(maybe_session)
|
.maybe_with_session(maybe_session)
|
||||||
.with_csrf(csrf_token.form_value());
|
.with_csrf(csrf_token.form_value());
|
||||||
|
|
||||||
let content = templates.render_index(&ctx)?;
|
let content = templates.render_index(&ctx).await?;
|
||||||
let reply = html(content);
|
let reply = html(content);
|
||||||
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
||||||
Ok(Box::new(reply))
|
Ok(Box::new(reply))
|
||||||
|
@ -138,7 +138,7 @@ async fn get(
|
|||||||
None => ctx,
|
None => ctx,
|
||||||
};
|
};
|
||||||
let ctx = ctx.with_csrf(csrf_token.form_value());
|
let ctx = ctx.with_csrf(csrf_token.form_value());
|
||||||
let content = templates.render_login(&ctx)?;
|
let content = templates.render_login(&ctx).await?;
|
||||||
let reply = html(content);
|
let reply = html(content);
|
||||||
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
||||||
Ok(Box::new(reply))
|
Ok(Box::new(reply))
|
||||||
@ -171,7 +171,7 @@ async fn post(
|
|||||||
let ctx = LoginContext::default()
|
let ctx = LoginContext::default()
|
||||||
.with_form_error(errored_form)
|
.with_form_error(errored_form)
|
||||||
.with_csrf(csrf_token.form_value());
|
.with_csrf(csrf_token.form_value());
|
||||||
let content = templates.render_login(&ctx)?;
|
let content = templates.render_login(&ctx).await?;
|
||||||
let reply = html(content);
|
let reply = html(content);
|
||||||
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
||||||
Ok(Box::new(reply))
|
Ok(Box::new(reply))
|
||||||
|
@ -132,7 +132,7 @@ async fn get(
|
|||||||
};
|
};
|
||||||
let ctx = ctx.with_session(session).with_csrf(csrf_token.form_value());
|
let ctx = ctx.with_session(session).with_csrf(csrf_token.form_value());
|
||||||
|
|
||||||
let content = templates.render_reauth(&ctx)?;
|
let content = templates.render_reauth(&ctx).await?;
|
||||||
let reply = html(content);
|
let reply = html(content);
|
||||||
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
||||||
Ok(reply)
|
Ok(reply)
|
||||||
|
@ -117,7 +117,7 @@ async fn get(
|
|||||||
Ok(Box::new(query.redirect()?))
|
Ok(Box::new(query.redirect()?))
|
||||||
} else {
|
} else {
|
||||||
let ctx = EmptyContext.with_csrf(csrf_token.form_value());
|
let ctx = EmptyContext.with_csrf(csrf_token.form_value());
|
||||||
let content = templates.render_register(&ctx)?;
|
let content = templates.render_register(&ctx).await?;
|
||||||
let reply = html(content);
|
let reply = html(content);
|
||||||
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
||||||
Ok(Box::new(reply))
|
Ok(Box::new(reply))
|
||||||
|
@ -5,6 +5,9 @@ authors = ["Quentin Gliech <quenting@element.io>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dev = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tracing = "0.1.29"
|
tracing = "0.1.29"
|
||||||
tokio = "1.14.0"
|
tokio = "1.14.0"
|
||||||
|
@ -23,14 +23,20 @@
|
|||||||
|
|
||||||
//! Templates rendering
|
//! Templates rendering
|
||||||
|
|
||||||
use std::{collections::HashSet, io::Cursor, path::Path, string::ToString, sync::Arc};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
io::Cursor,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
string::ToString,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Context as _;
|
use anyhow::{bail, Context as _};
|
||||||
use mas_config::TemplatesConfig;
|
use mas_config::TemplatesConfig;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tera::{Context, Error as TeraError, Tera};
|
use tera::{Context, Error as TeraError, Tera};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::{fs::OpenOptions, io::AsyncWriteExt};
|
use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync::RwLock, task::JoinError};
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
#[allow(missing_docs)] // TODO
|
#[allow(missing_docs)] // TODO
|
||||||
@ -47,7 +53,10 @@ pub use self::context::{
|
|||||||
|
|
||||||
/// Wrapper around [`tera::Tera`] helping rendering the various templates
|
/// Wrapper around [`tera::Tera`] helping rendering the various templates
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Templates(Arc<Tera>);
|
pub struct Templates {
|
||||||
|
tera: Arc<RwLock<Tera>>,
|
||||||
|
config: TemplatesConfig,
|
||||||
|
}
|
||||||
|
|
||||||
/// There was an issue while loading the templates
|
/// There was an issue while loading the templates
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@ -56,6 +65,10 @@ pub enum TemplateLoadingError {
|
|||||||
#[error("could not load and compile some templates")]
|
#[error("could not load and compile some templates")]
|
||||||
Compile(#[from] TeraError),
|
Compile(#[from] TeraError),
|
||||||
|
|
||||||
|
/// Could not join blocking task
|
||||||
|
#[error("error from async runtime")]
|
||||||
|
Runtime(#[from] JoinError),
|
||||||
|
|
||||||
/// There are essential templates missing
|
/// There are essential templates missing
|
||||||
#[error("missing templates {missing:?}")]
|
#[error("missing templates {missing:?}")]
|
||||||
MissingTemplates {
|
MissingTemplates {
|
||||||
@ -67,44 +80,104 @@ pub enum TemplateLoadingError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Templates {
|
impl Templates {
|
||||||
/// Load the templates from [the config][`TemplatesConfig`]
|
/// List directories to watch
|
||||||
pub fn load_from_config(config: &TemplatesConfig) -> Result<Self, TemplateLoadingError> {
|
pub async fn watch_roots(&self) -> Vec<PathBuf> {
|
||||||
Self::load(config.path.as_deref(), config.builtin)
|
Self::roots(self.config.path.as_deref(), self.config.builtin)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the templates and check all needed templates are properly loaded
|
async fn roots(path: Option<&str>, builtin: bool) -> Vec<Result<PathBuf, std::io::Error>> {
|
||||||
///
|
let mut paths = Vec::new();
|
||||||
/// # Arguments
|
if builtin && cfg!(feature = "dev") {
|
||||||
///
|
paths.push(PathBuf::from(format!(
|
||||||
/// * `path` - An optional path to where templates should be loaded
|
"{}/src/res",
|
||||||
/// * `builtin` - Set to `true` to load the builtin templates as well
|
env!("CARGO_MANIFEST_DIR")
|
||||||
pub fn load(path: Option<&str>, builtin: bool) -> Result<Self, TemplateLoadingError> {
|
)));
|
||||||
let tera = {
|
}
|
||||||
let mut tera = Tera::default();
|
|
||||||
|
|
||||||
if builtin {
|
if let Some(path) = path {
|
||||||
info!("Loading builtin templates");
|
paths.push(PathBuf::from(path));
|
||||||
|
}
|
||||||
|
|
||||||
for (name, source) in EXTRA_TEMPLATES {
|
let mut ret = Vec::new();
|
||||||
tera.add_raw_template(name, source)?;
|
for path in paths {
|
||||||
}
|
ret.push(tokio::fs::read_dir(&path).await.map(|_| path));
|
||||||
|
}
|
||||||
|
|
||||||
for (name, source) in TEMPLATES {
|
ret
|
||||||
tera.add_raw_template(name, source)?;
|
}
|
||||||
}
|
|
||||||
|
fn load_builtin() -> Result<Tera, TemplateLoadingError> {
|
||||||
|
let mut tera = Tera::default();
|
||||||
|
info!("Loading builtin templates");
|
||||||
|
|
||||||
|
for (name, source) in EXTRA_TEMPLATES {
|
||||||
|
if let Some(source) = source {
|
||||||
|
tera.add_raw_template(name, source)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(path) = path {
|
for (name, source) in TEMPLATES {
|
||||||
let path = format!("{}/**/*.{{html,txt}}", path);
|
if let Some(source) = source {
|
||||||
|
tera.add_raw_template(name, source)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tera)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the templates from [the config][`TemplatesConfig`]
|
||||||
|
pub async fn load_from_config(config: &TemplatesConfig) -> Result<Self, TemplateLoadingError> {
|
||||||
|
let tera = Self::load(config.path.as_deref(), config.builtin).await?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
tera: Arc::new(RwLock::new(tera)),
|
||||||
|
config: config.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load(path: Option<&str>, builtin: bool) -> Result<Tera, TemplateLoadingError> {
|
||||||
|
let mut teras = Vec::new();
|
||||||
|
|
||||||
|
let roots = Self::roots(path, builtin).await;
|
||||||
|
for maybe_root in roots {
|
||||||
|
let root = match maybe_root {
|
||||||
|
Ok(root) => root,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(%err, "Could not open a template folder, skipping it");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This uses blocking I/Os, do that in a blocking task
|
||||||
|
let tera = tokio::task::spawn_blocking(move || {
|
||||||
|
// Using `to_string_lossy` here is probably fine
|
||||||
|
let path = format!("{}/**/*.{{html,txt}}", root.to_string_lossy());
|
||||||
info!(%path, "Loading templates from filesystem");
|
info!(%path, "Loading templates from filesystem");
|
||||||
tera.extend(&Tera::parse(&path)?)?;
|
Tera::parse(&path)
|
||||||
}
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
tera.build_inheritance_chains()?;
|
teras.push(tera);
|
||||||
tera.check_macro_files()?;
|
}
|
||||||
|
|
||||||
tera
|
if builtin {
|
||||||
};
|
teras.push(Self::load_builtin()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merging all Tera instances into a single one
|
||||||
|
let mut tera = teras
|
||||||
|
.into_iter()
|
||||||
|
.try_fold(Tera::default(), |mut acc, tera| {
|
||||||
|
acc.extend(&tera)?;
|
||||||
|
Ok::<_, TemplateLoadingError>(acc)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
tera.build_inheritance_chains()?;
|
||||||
|
tera.check_macro_files()?;
|
||||||
|
|
||||||
let loaded: HashSet<_> = tera.get_template_names().collect();
|
let loaded: HashSet<_> = tera.get_template_names().collect();
|
||||||
let needed: HashSet<_> = std::array::IntoIter::new(TEMPLATES)
|
let needed: HashSet<_> = std::array::IntoIter::new(TEMPLATES)
|
||||||
@ -114,7 +187,7 @@ impl Templates {
|
|||||||
let missing: HashSet<_> = needed.difference(&loaded).collect();
|
let missing: HashSet<_> = needed.difference(&loaded).collect();
|
||||||
|
|
||||||
if missing.is_empty() {
|
if missing.is_empty() {
|
||||||
Ok(Self(Arc::new(tera)))
|
Ok(tera)
|
||||||
} else {
|
} else {
|
||||||
let missing = missing.into_iter().map(ToString::to_string).collect();
|
let missing = missing.into_iter().map(ToString::to_string).collect();
|
||||||
let loaded = loaded.into_iter().map(ToString::to_string).collect();
|
let loaded = loaded.into_iter().map(ToString::to_string).collect();
|
||||||
@ -122,8 +195,23 @@ impl Templates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reload the templates on disk
|
||||||
|
pub async fn reload(&self) -> anyhow::Result<()> {
|
||||||
|
// Prepare the new Tera instance
|
||||||
|
let new_tera = Self::load(self.config.path.as_deref(), self.config.builtin).await?;
|
||||||
|
|
||||||
|
// Swap it
|
||||||
|
*self.tera.write().await = new_tera;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Save the builtin templates to a folder
|
/// Save the builtin templates to a folder
|
||||||
pub async fn save(path: &Path, overwrite: bool) -> anyhow::Result<()> {
|
pub async fn save(path: &Path, overwrite: bool) -> anyhow::Result<()> {
|
||||||
|
if cfg!(feature = "dev") {
|
||||||
|
bail!("Builtin templates are not included in dev binaries")
|
||||||
|
}
|
||||||
|
|
||||||
tokio::fs::create_dir_all(&path)
|
tokio::fs::create_dir_all(&path)
|
||||||
.await
|
.await
|
||||||
.context("could not create destination folder")?;
|
.context("could not create destination folder")?;
|
||||||
@ -140,22 +228,24 @@ impl Templates {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (name, source) in templates {
|
for (name, source) in templates {
|
||||||
let path = path.join(name);
|
if let Some(source) = source {
|
||||||
|
let path = path.join(name);
|
||||||
|
|
||||||
let mut file = match options.open(&path).await {
|
let mut file = match options.open(&path).await {
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
|
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
|
||||||
// Not overwriting a template is a soft error
|
// Not overwriting a template is a soft error
|
||||||
warn!(?path, "Not overwriting template");
|
warn!(?path, "Not overwriting template");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
x => x.context(format!("could not open file {:?}", path))?,
|
x => x.context(format!("could not open file {:?}", path))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buffer = Cursor::new(source);
|
let mut buffer = Cursor::new(source);
|
||||||
file.write_all_buf(&mut buffer)
|
file.write_all_buf(&mut buffer)
|
||||||
.await
|
.await
|
||||||
.context(format!("could not write file {:?}", path))?;
|
.context(format!("could not write file {:?}", path))?;
|
||||||
info!(?path, "Wrote template");
|
info!(?path, "Wrote template");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -215,13 +305,13 @@ register_templates! {
|
|||||||
impl Templates {
|
impl Templates {
|
||||||
/// Render all templates with the generated samples to check if they render
|
/// Render all templates with the generated samples to check if they render
|
||||||
/// properly
|
/// properly
|
||||||
pub fn check_render(&self) -> anyhow::Result<()> {
|
pub async fn check_render(&self) -> anyhow::Result<()> {
|
||||||
check::render_login(self)?;
|
check::render_login(self).await?;
|
||||||
check::render_register(self)?;
|
check::render_register(self).await?;
|
||||||
check::render_index(self)?;
|
check::render_index(self).await?;
|
||||||
check::render_reauth(self)?;
|
check::render_reauth(self).await?;
|
||||||
check::render_form_post::<EmptyContext>(self)?;
|
check::render_form_post::<EmptyContext>(self).await?;
|
||||||
check::render_error(self)?;
|
check::render_error(self).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,9 +320,14 @@ impl Templates {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn check_builtin_templates() {
|
async fn check_builtin_templates() {
|
||||||
let templates = Templates::load(None, true).unwrap();
|
let config = TemplatesConfig {
|
||||||
templates.check_render().unwrap();
|
path: None,
|
||||||
|
builtin: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let templates = Templates::load_from_config(&config).await.unwrap();
|
||||||
|
templates.check_render().await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,26 +49,40 @@ macro_rules! register_templates {
|
|||||||
)*
|
)*
|
||||||
} => {
|
} => {
|
||||||
/// List of registered templates
|
/// List of registered templates
|
||||||
static TEMPLATES: [(&'static str, &'static str); count!( $( $template )* )] = [
|
static TEMPLATES: [(&'static str, Option<&'static str>); count!( $( $template )* )] = [
|
||||||
$( ($template, include_str!(concat!("res/", $template))) ),*
|
$( (
|
||||||
|
$template,
|
||||||
|
if cfg!(feature = "dev") {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(include_str!(concat!("res/", $template)))
|
||||||
|
}
|
||||||
|
) ),*
|
||||||
];
|
];
|
||||||
|
|
||||||
/// List of extra templates used by other templates
|
/// List of extra templates used by other templates
|
||||||
static EXTRA_TEMPLATES: [(&'static str, &'static str); count!( $( $( $extra_template )* )? )] = [
|
static EXTRA_TEMPLATES: [(&'static str, Option<&'static str>); count!( $( $( $extra_template )* )? )] = [
|
||||||
$( $( ($extra_template, include_str!(concat!("res/", $extra_template))) ),* )?
|
$( $( (
|
||||||
|
$extra_template,
|
||||||
|
if cfg!(feature = "dev") {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(include_str!(concat!("res/", $extra_template)))
|
||||||
|
}
|
||||||
|
) ),* )?
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Templates {
|
impl Templates {
|
||||||
$(
|
$(
|
||||||
$(#[$attr])?
|
$(#[$attr])?
|
||||||
pub fn $name
|
pub async fn $name
|
||||||
$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
|
$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
|
||||||
(&self, context: &$param)
|
(&self, context: &$param)
|
||||||
-> Result<String, TemplateError> {
|
-> Result<String, TemplateError> {
|
||||||
let ctx = Context::from_serialize(context)
|
let ctx = Context::from_serialize(context)
|
||||||
.map_err(|source| TemplateError::Context { template: $template, source })?;
|
.map_err(|source| TemplateError::Context { template: $template, source })?;
|
||||||
|
|
||||||
self.0.render($template, &ctx)
|
self.tera.read().await.render($template, &ctx)
|
||||||
.map_err(|source| TemplateError::Render { template: $template, source })
|
.map_err(|source| TemplateError::Render { template: $template, source })
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
@ -80,7 +94,7 @@ macro_rules! register_templates {
|
|||||||
|
|
||||||
$(
|
$(
|
||||||
#[doc = concat!("Render the `", $template, "` template with sample contexts")]
|
#[doc = concat!("Render the `", $template, "` template with sample contexts")]
|
||||||
pub fn $name
|
pub async fn $name
|
||||||
$(< $( $lt $( : $clt $(+ $dlt )* + TemplateContext )? ),+ >)?
|
$(< $( $lt $( : $clt $(+ $dlt )* + TemplateContext )? ),+ >)?
|
||||||
(templates: &Templates)
|
(templates: &Templates)
|
||||||
-> anyhow::Result<()> {
|
-> anyhow::Result<()> {
|
||||||
@ -91,6 +105,7 @@ macro_rules! register_templates {
|
|||||||
let context = serde_json::to_value(&sample)?;
|
let context = serde_json::to_value(&sample)?;
|
||||||
::tracing::info!(name, %context, "Rendering template");
|
::tracing::info!(name, %context, "Rendering template");
|
||||||
templates. $name (&sample)
|
templates. $name (&sample)
|
||||||
|
.await
|
||||||
.with_context(|| format!("Failed to render template {:?} with context {}", name, context))?;
|
.with_context(|| format!("Failed to render template {:?} with context {}", name, context))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user