mylloon.fr/src/routes/contact.rs
Mylloon a624761182
* move api logic into separate files
* move routes logic into own directory
* api: add some date in human readable format
2025-01-10 19:14:51 +01:00

144 lines
3.9 KiB
Rust

use actix_web::{get, routes, web, HttpRequest, Responder};
use cached::proc_macro::cached;
use ramhorns::Content;
use crate::{
config::Config,
logic::contact::{find_links, read, remove_paragraphs},
template::{InfosPage, NavBar},
utils::{
markdown::{File, FilePath},
metadata::MType,
misc::{lang, make_kw, read_file_fallback, Html, Lang},
},
};
const CONTACT_DIR: &str = "contacts";
pub fn pages(cfg: &mut web::ServiceConfig) {
// Here define the services used
let routes = |route_path| {
web::scope(route_path)
.service(page)
.service(service_redirection)
};
// Here define the routes aliases
cfg.service(routes("/contact")).service(routes("/c"));
}
#[get("")]
async fn page(req: HttpRequest, config: web::Data<Config>) -> impl Responder {
Html(build_page(config.get_ref().to_owned(), lang(req.headers())))
}
#[routes]
#[get("/{service}")]
#[get("/{service}/{scope}")]
async fn service_redirection(config: web::Data<Config>, req: HttpRequest) -> impl Responder {
let info = req.match_info();
let link = find_links(format!("{}/{}", config.locations.data_dir, CONTACT_DIR))
.iter()
// Find requested service
.filter(|&x| x.service == *info.query("service"))
// Search for a potential scope
.filter(|&x| match (info.get("scope"), x.scope.clone()) {
// The right scope is accepted
(Some(str_value), Some(string_value)) if str_value == string_value.as_str() => true,
// No scope provided is accepted
(None, None) => true,
// Else we reject
_ => false,
})
// Returns the link
.map(|data| data.url.clone())
.collect::<Vec<String>>();
// This shouldn't be more than one link here
match link.len() {
// Redirect to the desired service
1 => actix_web::web::Redirect::to(link[0].clone()),
// By default, returns to the contact page
_ => actix_web::web::Redirect::to("/contact"),
}
}
#[derive(Content, Debug)]
struct NetworksTemplate {
navbar: NavBar,
about: Option<File>,
socials_exists: bool,
socials: Vec<File>,
forges_exists: bool,
forges: Vec<File>,
others_exists: bool,
others: Vec<File>,
}
#[cached(time = 60)]
fn build_page(config: Config, lang: Lang) -> String {
let contacts_dir = format!("{}/{}", config.locations.data_dir, CONTACT_DIR);
let ext = ".md";
// Get about
let (about, html_lang) = read_file_fallback(
FilePath {
base: contacts_dir.clone(),
path: "about.md".to_owned(),
},
MType::Generic,
&lang,
);
let mut socials = read(&FilePath {
base: contacts_dir.clone(),
path: format!("socials/*{ext}"),
});
let mut forges = read(&FilePath {
base: contacts_dir.clone(),
path: format!("forges/*{ext}"),
});
let mut others = read(&FilePath {
base: contacts_dir,
path: format!("others/*{ext}"),
});
// Remove paragraphs in custom statements
[&mut socials, &mut forges, &mut others]
.iter_mut()
.for_each(|it| remove_paragraphs(it));
config.tmpl.render(
"contact/index.html",
NetworksTemplate {
navbar: NavBar {
contact: true,
..NavBar::default()
},
about,
socials_exists: !socials.is_empty(),
socials,
forges_exists: !forges.is_empty(),
forges,
others_exists: !others.is_empty(),
others,
},
InfosPage {
title: Some("Contacts".into()),
desc: Some(format!("Réseaux d'{}", config.fc.name.unwrap_or_default())),
kw: Some(make_kw(&[
"réseaux sociaux",
"email",
"contact",
"linktree",
])),
},
Some(html_lang),
)
}