use crate::{ authentication::AuthenticatedUser, domain::{CommentEntry, PostEntry, SubscriberEntry, UserEntry}, routes::{AppError, DashboardStats}, }; use askama::Template; use axum::response::{Html, IntoResponse}; use uuid::Uuid; pub struct HtmlTemplate(pub T); impl IntoResponse for HtmlTemplate where T: Template, { fn into_response(self) -> axum::response::Response { match self.0.render() { Ok(html) => Html(html).into_response(), Err(err) => AppError::unexpected_page(anyhow::anyhow!(err)).into_response(), } } } #[derive(Template)] #[template(path = "user/profile.html")] pub struct UserTemplate { pub user: UserEntry, pub session_username: Option, pub posts: Vec, } #[derive(Template)] #[template(path = "user/edit.html")] pub struct UserEditTemplate { pub user: UserEntry, } #[derive(Template)] #[template(path = "message.html")] pub struct MessageTemplate { pub message: String, pub error: bool, } impl MessageTemplate { pub fn success(message: String) -> Self { Self { message, error: false, } } pub fn error(message: String) -> Self { Self { message, error: true, } } } #[derive(Template)] #[template(path = "../templates/login.html")] pub struct LoginTemplate; #[derive(Template)] #[template(path = "dashboard/dashboard.html")] pub struct DashboardTemplate { pub user: AuthenticatedUser, pub idempotency_key_1: String, pub idempotency_key_2: String, pub stats: DashboardStats, pub subscribers: Vec, pub current_page: i64, pub max_page: i64, pub count: i64, pub users: Vec, pub posts: Vec, pub posts_current_page: i64, pub posts_max_page: i64, pub posts_count: i64, pub comments: Vec, pub comments_current_page: i64, pub comments_max_page: i64, pub comments_count: i64, } #[derive(Template)] #[template(path = "dashboard/posts/list.html", block = "posts")] pub struct PostsPageDashboardTemplate { pub posts: Vec, pub posts_current_page: i64, pub posts_max_page: i64, } #[derive(Template)] #[template(path = "dashboard/comments/list.html", block = "comments")] pub struct CommentsPageDashboardTemplate { pub comments: Vec, pub comments_current_page: i64, pub comments_max_page: i64, } #[derive(Template)] #[template(path = "home.html")] pub struct HomeTemplate; #[derive(Template)] #[template(path = "posts/list.html")] pub struct PostsTemplate { pub posts: Vec, pub next_page: Option, } #[derive(Template)] #[template(path = "posts/list.html", block = "posts")] pub struct PostListTemplate { pub posts: Vec, pub next_page: Option, } #[derive(Template)] #[template(path = "posts/page.html")] pub struct PostTemplate { pub post: PostEntry, pub post_html: String, pub idempotency_key: String, pub comments: Vec, pub current_page: i64, pub max_page: i64, pub comments_count: i64, pub session_user_id: Option, pub session_username: Option, } #[derive(Template)] #[template(path = "posts/comments/list.html", block = "comments")] pub struct CommentsList { pub comments: Vec, pub current_page: i64, pub max_page: i64, } #[derive(Template)] #[template(path = "dashboard/subscribers/list.html", block = "subs")] pub struct SubListTemplate { pub subscribers: Vec, pub current_page: i64, pub max_page: i64, } #[derive(Template)] #[template(path = "subscribe/confirm.html")] pub struct ConfirmTemplate; #[derive(Template)] pub enum ErrorTemplate { #[template(path = "error/404.html")] NotFound, #[template(path = "error/500.html")] InternalServer, #[template(path = "error/403.html")] Forbidden, } #[derive(Template)] #[template(path = "unsubscribe/confirm.html")] pub struct UnsubscribeConfirmTemplate; #[derive(Template)] #[template(path = "unsubscribe/form.html")] pub struct UnsubscribeTemplate; #[derive(Template)] #[template(path = "email/new_post.html")] pub struct NewPostEmailTemplate<'a> { pub base_url: &'a str, pub post_title: &'a str, pub post_id: &'a Uuid, pub post_excerpt: &'a str, } #[derive(Template)] #[template(path = "email/standalone.html")] pub struct StandaloneEmailTemplate<'a> { pub base_url: &'a str, pub html_content: &'a str, pub text_content: &'a str, } pub trait EmailTemplate: Sync { fn html(&self) -> String; fn text(&self) -> String; } impl<'a> EmailTemplate for NewPostEmailTemplate<'a> { fn html(&self) -> String { self.render().unwrap() } fn text(&self) -> String { format!( r#"New post available! Hello there! I just published a new post that I think you'll find interesting: "{}" Read the full post: {}/posts/{}?origin=EMAIL_ID This post covers practical insights and real-world examples that I hope will be valuable for your backend development journey. Thanks for being a subscriber! Best regards, Alphonse --- zero2prod - Building better backends with Rust Visit the blog: {} Unsubscribe: {}/unsubscribe/confirm?token=UNSUBSCRIBE_TOKEN You're receiving this because you subscribed to the zero2prod newsletter."#, self.post_title, self.base_url, self.post_id, self.base_url, self.base_url, ) } } impl<'a> EmailTemplate for StandaloneEmailTemplate<'a> { fn html(&self) -> String { self.render().unwrap() } fn text(&self) -> String { format!( r#"{} --- zero2prod - Building better backends with Rust Visit the blog: {} Unsubscribe: {}/unsubscribe/confirm?token=UNSUBSCRIBE_TOKEN You're receiving this because you subscribed to the zero2prod newsletter."#, self.text_content, self.base_url, self.base_url ) } }