use templates generated from website.git
This commit is contained in:
parent
f7f9785d8b
commit
21c8b35bba
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "website"]
|
||||||
|
path = website
|
||||||
|
url = https://git.libre-chip.org/libre-chip/website.git
|
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -2507,7 +2507,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subscribe-list"
|
name = "subscribe-list"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-session",
|
"actix-session",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2525,6 +2525,7 @@ dependencies = [
|
||||||
"listenfd",
|
"listenfd",
|
||||||
"log",
|
"log",
|
||||||
"openidconnect",
|
"openidconnect",
|
||||||
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "subscribe-list"
|
name = "subscribe-list"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Jacob Lifshay <programmerjake@gmail.com>"]
|
authors = ["Jacob Lifshay <programmerjake@gmail.com>"]
|
||||||
description = "web server for managing subscriptions"
|
description = "web server for managing subscriptions"
|
||||||
|
@ -33,6 +33,9 @@ tinytemplate = "1.2.1"
|
||||||
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
|
||||||
toml = { version = "0.8.12", features = ["preserve_order"] }
|
toml = { version = "0.8.12", features = ["preserve_order"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
regex = "1.10.4"
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
depends = ["$auto", "adduser (>= 3.11)"]
|
depends = ["$auto", "adduser (>= 3.11)"]
|
||||||
assets = [
|
assets = [
|
||||||
|
|
80
build.rs
80
build.rs
|
@ -1,3 +1,79 @@
|
||||||
fn main() {
|
use regex::Regex;
|
||||||
println!("cargo:rerun-if-changed=migrations");
|
use std::{collections::HashSet, env, error::Error, fs, path::Path, process::Command};
|
||||||
|
|
||||||
|
fn escape_template(v: &str) -> Result<String, String> {
|
||||||
|
let cant_escape = "\\{";
|
||||||
|
if v.contains(cant_escape) {
|
||||||
|
return Err(format!("can't escape {cant_escape}"));
|
||||||
|
}
|
||||||
|
Ok(v.replace("{", "\\{"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
println!("cargo:rerun-if-changed=migrations");
|
||||||
|
println!("cargo:rerun-if-changed=website");
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let website_dir = Path::new(&out_dir).join("website");
|
||||||
|
let templates_dir = Path::new(&out_dir).join("templates");
|
||||||
|
fs::create_dir_all(&templates_dir)?;
|
||||||
|
let status = Command::new("mdbook")
|
||||||
|
.arg("build")
|
||||||
|
.arg("--dest-dir")
|
||||||
|
.arg(&website_dir)
|
||||||
|
.current_dir("website")
|
||||||
|
.status()
|
||||||
|
.map_err(|e| format!("Can't run mdbook: {e}"))?;
|
||||||
|
if !status.success() {
|
||||||
|
return Err(format!("mdbook exited with an error: {status:?}").into());
|
||||||
|
}
|
||||||
|
let sub_template_path = website_dir.join("subscription/index.html");
|
||||||
|
let sub_template = fs::read_to_string(&sub_template_path)
|
||||||
|
.map_err(|e| format!("Error reading {}: {e}", sub_template_path.display()))?;
|
||||||
|
let regex = Regex::new(r#"(?s)<div class="subscribe_list_template_section" id="(?<id>[^"]*)"/>(?<template>.*?)<div class="subscribe_list_template_section"/>"#).unwrap();
|
||||||
|
let matches = Vec::from_iter(regex.captures_iter(&sub_template));
|
||||||
|
let mut between_matches = vec![];
|
||||||
|
let mut last_end = 0;
|
||||||
|
for m in &matches {
|
||||||
|
let m = m.get(0).unwrap();
|
||||||
|
between_matches.push(&sub_template[last_end..m.start()]);
|
||||||
|
last_end = m.end();
|
||||||
|
}
|
||||||
|
between_matches.push(&sub_template[last_end..]);
|
||||||
|
let prefix = between_matches.remove(0);
|
||||||
|
let Some(suffix) = between_matches.pop() else {
|
||||||
|
return Err(format!(
|
||||||
|
"no subscribe_list_template_section `div`s found: {}",
|
||||||
|
sub_template_path.display()
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
};
|
||||||
|
let prefix = escape_template(prefix)?;
|
||||||
|
let suffix = escape_template(suffix)?;
|
||||||
|
for i in between_matches {
|
||||||
|
if i.trim_start() != "" {
|
||||||
|
return Err(format!(
|
||||||
|
r#"can't have non-whitespace text between
|
||||||
|
<div class="subscribe_list_template_section"/>
|
||||||
|
and next
|
||||||
|
<div class="subscribe_list_template_section" id="..">"#
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut identifiers = HashSet::new();
|
||||||
|
for m in &matches {
|
||||||
|
let id = &m["id"];
|
||||||
|
if id.contains(|ch: char| !ch.is_ascii_alphanumeric() && ch != '_') {
|
||||||
|
return Err(format!("invalid identifier {id:?}").into());
|
||||||
|
}
|
||||||
|
if !identifiers.insert(id) {
|
||||||
|
return Err(format!("duplicate identifier {id:?}").into());
|
||||||
|
}
|
||||||
|
let template = &m["template"];
|
||||||
|
fs::write(
|
||||||
|
templates_dir.join(format!("{id}.txt")),
|
||||||
|
prefix.clone() + template + &suffix,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
35
src/app.rs
35
src/app.rs
|
@ -9,6 +9,7 @@ use actix_session::Session;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
get,
|
get,
|
||||||
http::header::{ContentType, LOCATION},
|
http::header::{ContentType, LOCATION},
|
||||||
|
routes,
|
||||||
web::{self, ServiceConfig},
|
web::{self, ServiceConfig},
|
||||||
HttpResponse, Responder,
|
HttpResponse, Responder,
|
||||||
};
|
};
|
||||||
|
@ -51,7 +52,7 @@ impl<T: Template> DynTemplate for PhantomData<T> {
|
||||||
macro_rules! templates {
|
macro_rules! templates {
|
||||||
(
|
(
|
||||||
$(
|
$(
|
||||||
#[text = $text:literal]
|
#[text = $text:expr]
|
||||||
$(#[$meta:meta])*
|
$(#[$meta:meta])*
|
||||||
$vis:vis struct $name:ident $fields:tt
|
$vis:vis struct $name:ident $fields:tt
|
||||||
)*
|
)*
|
||||||
|
@ -76,28 +77,12 @@ pub struct SubscriptionLoggedOutTemplateProviders {
|
||||||
}
|
}
|
||||||
|
|
||||||
templates! {
|
templates! {
|
||||||
#[text = r#"<head><title>Subscription</title></head>
|
#[text = include_str!(concat!(env!("OUT_DIR"), "/templates/SubscriptionLoggedOut.txt"))]
|
||||||
<body>
|
|
||||||
<h1>Register or Sign In:</h1>
|
|
||||||
As an anti-spam measure, you need to register using a 3rd-party account:
|
|
||||||
<ul>
|
|
||||||
{{- for provider in providers -}}
|
|
||||||
<li><a href="{provider.url}">{provider.pretty_name}</a></li>
|
|
||||||
{{- endfor -}}
|
|
||||||
</ul>
|
|
||||||
</body>"#]
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct SubscriptionLoggedOutTemplate {
|
pub struct SubscriptionLoggedOutTemplate {
|
||||||
pub providers: Vec<SubscriptionLoggedOutTemplateProviders>,
|
pub providers: Vec<SubscriptionLoggedOutTemplateProviders>,
|
||||||
}
|
}
|
||||||
#[text = r#"<head><title>Subscription</title></head>
|
#[text = include_str!(concat!(env!("OUT_DIR"), "/templates/SubscriptionLoggedIn.txt"))]
|
||||||
<body>
|
|
||||||
<p>Signed in as {email}</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/subscription/logout">Logout</a></li>
|
|
||||||
<li><b><a href="/subscription/unsubscribe">Unsubscribe and Delete Account</a></b></li>
|
|
||||||
</ul>
|
|
||||||
</body>"#]
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct SubscriptionLoggedInTemplate {
|
pub struct SubscriptionLoggedInTemplate {
|
||||||
pub email: EndUserEmail,
|
pub email: EndUserEmail,
|
||||||
|
@ -363,7 +348,10 @@ pub async fn unsubscribe(session: Session, db: web::Data<DbThread>) -> impl Resp
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[routes]
|
||||||
#[get("/subscription")]
|
#[get("/subscription")]
|
||||||
|
#[get("/subscription/")]
|
||||||
|
#[get("/subscription/index.html")]
|
||||||
pub async fn subscription(
|
pub async fn subscription(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
session: Session,
|
session: Session,
|
||||||
|
@ -391,13 +379,20 @@ pub async fn subscription(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn page_not_found() -> impl Responder {
|
||||||
|
HttpResponse::NotFound()
|
||||||
|
.content_type(ContentType::html())
|
||||||
|
.body(include_str!(concat!(env!("OUT_DIR"), "/website/404.html")))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn all_services(cfg: &mut ServiceConfig) {
|
pub fn all_services(cfg: &mut ServiceConfig) {
|
||||||
cfg.service(subscription)
|
cfg.service(subscription)
|
||||||
.service(login)
|
.service(login)
|
||||||
.service(callback)
|
.service(callback)
|
||||||
.service(logout)
|
.service(logout)
|
||||||
.service(unsubscribe)
|
.service(unsubscribe)
|
||||||
.service(email_unsubscribe);
|
.service(email_unsubscribe)
|
||||||
|
.default_service(web::to(page_not_found));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
1
website
Submodule
1
website
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit bfa8bd253cb9ba1bcc1358891767f8c2411a63fe
|
Loading…
Reference in a new issue