From 11052951b74b9ad4b6a9412ae490086235f9154b Mon Sep 17 00:00:00 2001 From: Joris Date: Sun, 3 Jan 2021 13:40:40 +0100 Subject: Rewrite in Rust --- src/controller/balance.rs | 71 ++++++++++++++ src/controller/categories.rs | 141 +++++++++++++++++++++++++++ src/controller/error.rs | 31 ++++++ src/controller/incomes.rs | 221 +++++++++++++++++++++++++++++++++++++++++ src/controller/login.rs | 86 ++++++++++++++++ src/controller/mod.rs | 9 ++ src/controller/payments.rs | 227 +++++++++++++++++++++++++++++++++++++++++++ src/controller/statistics.rs | 30 ++++++ src/controller/utils.rs | 119 +++++++++++++++++++++++ src/controller/wallet.rs | 13 +++ 10 files changed, 948 insertions(+) create mode 100644 src/controller/balance.rs create mode 100644 src/controller/categories.rs create mode 100644 src/controller/error.rs create mode 100644 src/controller/incomes.rs create mode 100644 src/controller/login.rs create mode 100644 src/controller/mod.rs create mode 100644 src/controller/payments.rs create mode 100644 src/controller/statistics.rs create mode 100644 src/controller/utils.rs create mode 100644 src/controller/wallet.rs (limited to 'src/controller') diff --git a/src/controller/balance.rs b/src/controller/balance.rs new file mode 100644 index 0000000..228ff04 --- /dev/null +++ b/src/controller/balance.rs @@ -0,0 +1,71 @@ +use hyper::{Body, Response}; +use std::collections::HashMap; +use tera::Context; + +use crate::controller::utils; +use crate::controller::wallet::Wallet; +use crate::db; +use crate::model::user::User; +use crate::payer; +use crate::templates; + +pub async fn get(wallet: &Wallet) -> Response { + let users = db::users::list(&wallet.pool).await; + + let incomes_from = db::incomes::defined_for_all(&wallet.pool).await; + let user_incomes = match incomes_from { + Some(from) => db::incomes::cumulative(&wallet.pool, from).await, + None => HashMap::new(), + }; + let template_user_incomes = + get_template_user_incomes(&users, &user_incomes); + let total_income: i64 = user_incomes.values().sum(); + + let user_payments = db::payments::repartition(&wallet.pool).await; + let template_user_payments = + get_template_user_payments(&users, &user_payments); + let total_payments: i64 = + user_payments.clone().into_iter().map(|p| p.1).sum(); + + let exceeding_payers = + payer::exceeding(&users, &user_incomes, &user_payments); + + let mut context = Context::new(); + context.insert("header", &templates::Header::Balance); + context.insert("connected_user", &wallet.user); + context.insert( + "incomes_from", + &incomes_from.map(|d| d.format("%d/%m/%Y").to_string()), + ); + context.insert("total_income", &total_income); + context.insert("user_incomes", &template_user_incomes); + context.insert("total_payments", &total_payments); + context.insert("user_payments", &template_user_payments); + context.insert("exceeding_payers", &exceeding_payers); + + utils::template(&wallet.assets, &wallet.templates, "balance.html", context) +} + +fn get_template_user_payments( + users: &Vec, + user_payments: &HashMap, +) -> Vec<(String, i64)> { + let mut user_payments: Vec<(String, i64)> = users + .into_iter() + .map(|u| (u.name.clone(), *user_payments.get(&u.id).unwrap_or(&0))) + .collect(); + user_payments.sort_by_key(|i| i.1); + user_payments +} + +fn get_template_user_incomes( + users: &Vec, + user_incomes: &HashMap, +) -> Vec<(String, i64)> { + let mut user_incomes: Vec<(String, i64)> = users + .into_iter() + .map(|u| (u.name.clone(), *user_incomes.get(&u.id).unwrap_or(&0))) + .collect(); + user_incomes.sort_by_key(|i| i.1); + user_incomes +} diff --git a/src/controller/categories.rs b/src/controller/categories.rs new file mode 100644 index 0000000..b1a3664 --- /dev/null +++ b/src/controller/categories.rs @@ -0,0 +1,141 @@ +use hyper::{Body, Response}; +use std::collections::HashMap; +use tera::Context; + +use crate::controller::utils; +use crate::controller::wallet::Wallet; +use crate::db; +use crate::queries; +use crate::templates; +use crate::validation; + +pub async fn table( + wallet: &Wallet, + query: queries::Categories, +) -> Response { + let categories = db::categories::list(&wallet.pool).await; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Categories); + context.insert("connected_user", &wallet.user); + context.insert("categories", &categories); + context.insert("highlight", &query.highlight); + + utils::template( + &wallet.assets, + &wallet.templates, + "category/table.html", + context, + ) +} + +pub async fn create_form(wallet: &Wallet) -> Response { + create_form_feedback(wallet, HashMap::new(), None).await +} + +async fn create_form_feedback( + wallet: &Wallet, + form: HashMap, + error: Option, +) -> Response { + let mut context = Context::new(); + context.insert("header", &templates::Header::Categories); + context.insert("connected_user", &wallet.user.clone()); + context.insert("form", &form); + context.insert("error", &error); + + utils::template( + &wallet.assets, + &wallet.templates, + "category/create.html", + context, + ) +} + +pub async fn create( + wallet: &Wallet, + form: HashMap, +) -> Response { + let error = |e: &str| { + create_form_feedback(wallet, form.clone(), Some(e.to_string())) + }; + + match validation::category::create(&form) { + Some(category) => { + match db::categories::create(&wallet.pool, &category).await { + Some(id) => { + utils::redirect(&format!("/categories?highlight={}", id)) + } + None => error("Erreur serveur").await, + } + } + None => error("Erreur lors de la validation du formulaire.").await, + } +} + +pub async fn update_form(id: i64, wallet: &Wallet) -> Response { + update_form_feedback(id, wallet, HashMap::new(), None).await +} + +async fn update_form_feedback( + id: i64, + wallet: &Wallet, + form: HashMap, + error: Option, +) -> Response { + let category = db::categories::get(&wallet.pool, id).await; + let is_category_used = + db::payments::is_category_used(&wallet.pool, id).await; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Categories); + context.insert("connected_user", &wallet.user); + context.insert("id", &id); + context.insert("category", &category); + context.insert("is_category_used", &is_category_used); + context.insert("form", &form); + context.insert("error", &error); + + utils::template( + &wallet.assets, + &wallet.templates, + "category/update.html", + context, + ) +} + +pub async fn update( + id: i64, + wallet: &Wallet, + form: HashMap, +) -> Response { + let error = |e: &str| { + update_form_feedback(id, wallet, form.clone(), Some(e.to_string())) + }; + + match validation::category::update(&form) { + Some(update_category) => { + if db::categories::update(&wallet.pool, id, &update_category).await + { + utils::redirect(&format!("/categories?highlight={}", id)) + } else { + error("Erreur serveur").await + } + } + None => error("Erreur lors de la validation du formulaire.").await, + } +} + +pub async fn delete(id: i64, wallet: &Wallet) -> Response { + if db::categories::delete(&wallet.pool, id).await { + utils::redirect("/categories") + } else { + update_form_feedback( + id, + wallet, + HashMap::new(), + Some("Erreur serveur".to_string()), + ) + .await + } +} diff --git a/src/controller/error.rs b/src/controller/error.rs new file mode 100644 index 0000000..8dad16b --- /dev/null +++ b/src/controller/error.rs @@ -0,0 +1,31 @@ +use hyper::header::CACHE_CONTROL; +use hyper::{Body, Response}; +use std::collections::HashMap; +use tera::{Context, Tera}; + +use crate::controller::utils; +use crate::controller::wallet::Wallet; + +pub fn error(wallet: &Wallet, title: &str, message: &str) -> Response { + utils::with_header( + Response::new( + template(&wallet.assets, &wallet.templates, title, message).into(), + ), + CACHE_CONTROL, + "no-cache", + ) +} + +pub fn template( + assets: &HashMap, + templates: &Tera, + title: &str, + message: &str, +) -> String { + let mut context = Context::new(); + context.insert("title", title); + context.insert("message", message); + context.insert("assets", assets); + + templates.render("error.html", &context).unwrap() +} diff --git a/src/controller/incomes.rs b/src/controller/incomes.rs new file mode 100644 index 0000000..ea7f1cf --- /dev/null +++ b/src/controller/incomes.rs @@ -0,0 +1,221 @@ +use chrono::Datelike; +use chrono::Utc; +use hyper::{Body, Response}; +use std::collections::HashMap; +use tera::Context; + +use crate::controller::utils; +use crate::controller::wallet::Wallet; +use crate::db; +use crate::queries; +use crate::templates; +use crate::validation; + +static PER_PAGE: i64 = 10; + +pub async fn table(wallet: &Wallet, query: queries::Incomes) -> Response { + let page = query.page.unwrap_or(1); + let count = db::incomes::count(&wallet.pool).await; + let incomes = db::incomes::list(&wallet.pool, page, PER_PAGE).await; + let max_page = (count as f32 / PER_PAGE as f32).ceil() as i64; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Incomes); + context.insert("connected_user", &wallet.user); + context.insert("incomes", &incomes); + context.insert("page", &page); + context.insert("max_page", &max_page); + context.insert("highlight", &query.highlight); + + utils::template( + &wallet.assets, + &wallet.templates, + "income/table.html", + context, + ) +} + +static MONTHS: [&str; 12] = [ + "Janvier", + "Février", + "Mars", + "Avril", + "Mai", + "Juin", + "Juillet", + "Août", + "Septembre", + "Octobre", + "Novembre", + "Décembre", +]; + +pub async fn create_form( + wallet: &Wallet, + query: queries::Incomes, +) -> Response { + create_form_feedback(wallet, query, HashMap::new(), None).await +} + +async fn create_form_feedback( + wallet: &Wallet, + query: queries::Incomes, + form: HashMap, + error: Option, +) -> Response { + let users = db::users::list(&wallet.pool).await; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Incomes); + context.insert("connected_user", &wallet.user); + context.insert("users", &users); + context.insert("query", &query); + context.insert("current_month", &Utc::today().naive_utc().month()); + context.insert("months", &MONTHS); + context.insert("form", &form); + context.insert("error", &error); + + utils::template( + &wallet.assets, + &wallet.templates, + "income/create.html", + context, + ) +} + +pub async fn create( + wallet: &Wallet, + query: queries::Incomes, + form: HashMap, +) -> Response { + let error = |e: &str| { + create_form_feedback(wallet, query, form.clone(), Some(e.to_string())) + }; + + match validation::income::create(&form) { + Some(income) => { + if !db::incomes::defined_at( + &wallet.pool, + income.user_id, + income.month, + income.year, + ) + .await + .is_empty() + { + error("Un revenu est déjà défini à cette date.").await + } else { + match db::incomes::create(&wallet.pool, &income).await { + Some(id) => { + let row = db::incomes::get_row(&wallet.pool, id).await; + let page = (row - 1) / PER_PAGE + 1; + utils::redirect(&format!( + "/incomes?page={}&highlight={}", + page, id + )) + } + None => error("Erreur serveur").await, + } + } + } + None => error("Erreur lors de la validation du formulaire.").await, + } +} + +pub async fn update_form( + id: i64, + wallet: &Wallet, + query: queries::Incomes, +) -> Response { + update_form_feedback(id, wallet, query, HashMap::new(), None).await +} + +async fn update_form_feedback( + id: i64, + wallet: &Wallet, + query: queries::Incomes, + form: HashMap, + error: Option, +) -> Response { + let users = db::users::list(&wallet.pool).await; + let income = db::incomes::get(&wallet.pool, id).await; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Incomes); + context.insert("connected_user", &wallet.user); + context.insert("users", &users); + context.insert("id", &id); + context.insert("income", &income); + context.insert("query", &query); + context.insert("months", &MONTHS); + context.insert("form", &form); + context.insert("error", &error); + + utils::template( + &wallet.assets, + &wallet.templates, + "income/update.html", + context, + ) +} + +pub async fn update( + id: i64, + wallet: &Wallet, + query: queries::Incomes, + form: HashMap, +) -> Response { + let error = |e: &str| { + update_form_feedback( + id, + wallet, + query, + form.clone(), + Some(e.to_string()), + ) + }; + + match validation::income::update(&form) { + Some(income) => { + let existing_incomes = db::incomes::defined_at( + &wallet.pool, + income.user_id, + income.month, + income.year, + ) + .await; + if existing_incomes.into_iter().any(|eid| eid != id) { + error("Un revenu est déjà défini à cette date.").await + } else if db::incomes::update(&wallet.pool, id, &income).await { + let row = db::incomes::get_row(&wallet.pool, id).await; + let page = (row - 1) / PER_PAGE + 1; + utils::redirect(&format!( + "/incomes?page={}&highlight={}", + page, id + )) + } else { + error("Erreur serveur").await + } + } + None => error("Erreur lors de la validation du formulaire.").await, + } +} + +pub async fn delete( + id: i64, + wallet: &Wallet, + query: queries::Incomes, +) -> Response { + if db::incomes::delete(&wallet.pool, id).await { + utils::redirect(&format!("/incomes?page={}", query.page.unwrap_or(1))) + } else { + update_form_feedback( + id, + wallet, + query, + HashMap::new(), + Some("Erreur serveur".to_string()), + ) + .await + } +} diff --git a/src/controller/login.rs b/src/controller/login.rs new file mode 100644 index 0000000..ea9db57 --- /dev/null +++ b/src/controller/login.rs @@ -0,0 +1,86 @@ +use bcrypt; +use hyper::{Body, Response}; +use sqlx::sqlite::SqlitePool; +use std::collections::HashMap; +use tera::{Context, Tera}; +use uuid::Uuid; + +use crate::controller::wallet::Wallet; +use crate::controller::{error, utils}; +use crate::db; +use crate::model::config::Config; +use crate::model::user::User; +use crate::validation; + +pub async fn page( + assets: &HashMap, + templates: &Tera, + error: Option, +) -> Response { + let connected_user: Option = None; + + let mut context = Context::new(); + context.insert("connected_user", &connected_user); + context.insert("error", &error); + + utils::template(assets, templates, "login.html", context) +} + +pub async fn login( + config: Config, + assets: &HashMap, + templates: &Tera, + form: HashMap, + pool: SqlitePool, +) -> Response { + let not_authorized = page( + assets, + templates, + Some("Vous n’êtes pas autorisé à vous connecter.".to_string()), + ) + .await; + let server_error = + page(assets, templates, Some("Erreur serveur.".to_string())).await; + match validation::login::login(&form) { + Some(login) => { + match db::users::get_password_hash(&pool, login.email.clone()).await + { + Some(hash) => match bcrypt::verify(login.password, &hash) { + Ok(true) => { + let login_token = Uuid::new_v4(); + if db::users::set_login_token( + &pool, + login.email, + login_token.clone().to_string(), + ) + .await + { + utils::with_login_cookie( + config, + login_token, + utils::redirect("/"), + ) + } else { + server_error + } + } + Ok(false) => not_authorized, + Err(err) => { + error!("Error verifying bcrypt password: {:?}", err); + server_error + } + }, + None => not_authorized, + } + } + None => not_authorized, + } +} + +pub async fn logout(config: Config, wallet: &Wallet) -> Response { + if db::users::remove_login_token(&wallet.pool, wallet.user.id).await { + utils::with_logout_cookie(config, utils::redirect("/")) + } else { + error::error(&wallet, "Erreur serveur", "Erreur serveur") + } +} diff --git a/src/controller/mod.rs b/src/controller/mod.rs new file mode 100644 index 0000000..e2ef561 --- /dev/null +++ b/src/controller/mod.rs @@ -0,0 +1,9 @@ +pub mod balance; +pub mod categories; +pub mod error; +pub mod incomes; +pub mod login; +pub mod payments; +pub mod statistics; +pub mod utils; +pub mod wallet; diff --git a/src/controller/payments.rs b/src/controller/payments.rs new file mode 100644 index 0000000..ab4bd92 --- /dev/null +++ b/src/controller/payments.rs @@ -0,0 +1,227 @@ +use hyper::header::CONTENT_TYPE; +use hyper::{Body, Response}; +use std::collections::HashMap; +use tera::Context; + +use crate::controller::utils; +use crate::controller::wallet::Wallet; +use crate::db; +use crate::model::frequency::Frequency; +use crate::queries; +use crate::templates; +use crate::validation; + +static PER_PAGE: i64 = 10; + +pub async fn table( + wallet: &Wallet, + query: queries::Payments, +) -> Response { + let page = query.page.unwrap_or(1); + let count = db::payments::count(&wallet.pool, &query).await; + let payments = + db::payments::list_for_table(&wallet.pool, &query, PER_PAGE).await; + let max_page = (count.count as f32 / PER_PAGE as f32).ceil() as i64; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Payments); + context.insert("connected_user", &wallet.user); + context.insert("payments", &payments); + context.insert("page", &page); + context.insert("max_page", &max_page); + context.insert("query", &query); + context.insert("count", &count.count); + context.insert("total_cost", &count.total_cost); + + utils::template( + &wallet.assets, + &wallet.templates, + "payment/table.html", + context, + ) +} + +pub async fn create_form( + wallet: &Wallet, + query: queries::Payments, +) -> Response { + create_form_feedback(wallet, query, HashMap::new(), None).await +} + +async fn create_form_feedback( + wallet: &Wallet, + query: queries::Payments, + form: HashMap, + error: Option, +) -> Response { + let users = db::users::list(&wallet.pool).await; + let categories = db::categories::list(&wallet.pool).await; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Payments); + context.insert("connected_user", &wallet.user); + context.insert("users", &users); + context.insert("categories", &categories); + context.insert("query", &query); + context.insert("form", &form); + context.insert("error", &error); + + utils::template( + &wallet.assets, + &wallet.templates, + "payment/create.html", + context, + ) +} + +pub async fn create( + wallet: &Wallet, + query: queries::Payments, + form: HashMap, +) -> Response { + let error = |e: &str| { + create_form_feedback(wallet, query, form.clone(), Some(e.to_string())) + }; + + match validation::payment::create(&form) { + Some(create_payment) => { + match db::payments::create(&wallet.pool, &create_payment).await { + Some(id) => { + let row = db::payments::get_row( + &wallet.pool, + id, + create_payment.frequency, + ) + .await; + let page = (row - 1) / PER_PAGE + 1; + let query = queries::Payments { + page: Some(page), + search: None, + frequency: Some(create_payment.frequency), + highlight: Some(id), + }; + utils::redirect(&format!( + "/{}", + queries::payments_url(query) + )) + } + None => error("Erreur serveur.").await, + } + } + None => error("Erreur lors de la validation du formulaire.").await, + } +} + +pub async fn update_form( + id: i64, + wallet: &Wallet, + query: queries::Payments, +) -> Response { + update_form_feedback(id, wallet, query, HashMap::new(), None).await +} + +async fn update_form_feedback( + id: i64, + wallet: &Wallet, + query: queries::Payments, + form: HashMap, + error: Option, +) -> Response { + let payment = db::payments::get_for_form(&wallet.pool, id).await; + let users = db::users::list(&wallet.pool).await; + let categories = db::categories::list(&wallet.pool).await; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Payments); + context.insert("connected_user", &wallet.user); + context.insert("id", &id); + context.insert("payment", &payment); + context.insert("users", &users); + context.insert("categories", &categories); + context.insert("query", &query); + context.insert("form", &form); + context.insert("error", &error); + + utils::template( + &wallet.assets, + &wallet.templates, + "payment/update.html", + context, + ) +} + +pub async fn update( + id: i64, + wallet: &Wallet, + query: queries::Payments, + form: HashMap, +) -> Response { + let error = |e: &str| { + update_form_feedback( + id, + wallet, + query.clone(), + form.clone(), + Some(e.to_string()), + ) + }; + + match validation::payment::update(&form) { + Some(update_payment) => { + if db::payments::update(&wallet.pool, id, &update_payment).await { + let frequency = query.frequency.unwrap_or(Frequency::Punctual); + let row = + db::payments::get_row(&wallet.pool, id, frequency).await; + let page = (row - 1) / PER_PAGE + 1; + let query = queries::Payments { + page: Some(page), + search: None, + frequency: Some(frequency), + highlight: Some(id), + }; + utils::redirect(&format!("/{}", queries::payments_url(query))) + } else { + error("Erreur serveur").await + } + } + None => error("Erreur lors de la validation du formulaire.").await, + } +} + +pub async fn delete( + id: i64, + wallet: &Wallet, + query: queries::Payments, +) -> Response { + if db::payments::delete(&wallet.pool, id).await { + let query = queries::Payments { + highlight: None, + ..query + }; + utils::redirect(&format!("/{}", queries::payments_url(query))) + } else { + update_form_feedback( + id, + wallet, + query, + HashMap::new(), + Some("Erreur serveur".to_string()), + ) + .await + } +} + +pub async fn search_category( + wallet: &Wallet, + query: queries::PaymentCategory, +) -> Response { + match db::payments::search_category(&wallet.pool, query.payment_name).await + { + Some(category_id) => utils::with_header( + Response::new(format!("{}", category_id).into()), + CONTENT_TYPE, + "application/json", + ), + None => utils::not_found(), + } +} diff --git a/src/controller/statistics.rs b/src/controller/statistics.rs new file mode 100644 index 0000000..38a5787 --- /dev/null +++ b/src/controller/statistics.rs @@ -0,0 +1,30 @@ +use hyper::{Body, Response}; +use tera::Context; + +use crate::controller::utils; +use crate::controller::wallet::Wallet; +use crate::db; +use crate::templates; + +pub async fn get(wallet: &Wallet) -> Response { + let categories = db::categories::list(&wallet.pool).await; + let payments = db::payments::list_for_stats(&wallet.pool).await; + let incomes = db::incomes::total_each_month(&wallet.pool).await; + + let mut context = Context::new(); + context.insert("header", &templates::Header::Statistics); + context.insert("connected_user", &wallet.user); + context.insert( + "json_categories", + &serde_json::to_string(&categories).unwrap(), + ); + context.insert("json_payments", &serde_json::to_string(&payments).unwrap()); + context.insert("json_incomes", &serde_json::to_string(&incomes).unwrap()); + + utils::template( + &wallet.assets, + &wallet.templates, + "statistics.html", + context, + ) +} diff --git a/src/controller/utils.rs b/src/controller/utils.rs new file mode 100644 index 0000000..225f8a4 --- /dev/null +++ b/src/controller/utils.rs @@ -0,0 +1,119 @@ +use hyper::header::{ + HeaderName, HeaderValue, CACHE_CONTROL, CONTENT_TYPE, LOCATION, SET_COOKIE, +}; +use hyper::{Body, Response, StatusCode}; +use std::collections::HashMap; +use tera::{Context, Tera}; +use tokio::fs::File; +use tokio_util::codec::{BytesCodec, FramedRead}; +use uuid::Uuid; + +use crate::controller::error; +use crate::model::config::Config; + +pub fn with_header( + response: Response, + name: HeaderName, + value: &str, +) -> Response { + with_headers(response, vec![(name, value)]) +} + +pub fn with_headers( + response: Response, + headers: Vec<(HeaderName, &str)>, +) -> Response { + let mut response = response; + let response_headers = response.headers_mut(); + for (name, value) in headers { + response_headers.insert(name, HeaderValue::from_str(value).unwrap()); + } + response +} + +pub fn with_login_cookie( + config: Config, + login_token: Uuid, + response: Response, +) -> Response { + let cookie = format!( + "TOKEN={}; SameSite=Strict; HttpOnly; Max-Age=86400{}", + login_token, + if config.secure_cookies { + "; Secure" + } else { + "" + } + ); + + with_header(response, SET_COOKIE, &cookie) +} + +pub fn with_logout_cookie( + config: Config, + response: Response, +) -> Response { + let cookie = format!( + "TOKEN=; SameSite=Strict; HttpOnly; Max-Age=0{}", + if config.secure_cookies { + "; Secure" + } else { + "" + } + ); + + with_header(response, SET_COOKIE, &cookie) +} + +pub fn template( + assets: &HashMap, + templates: &Tera, + path: &str, + context: Context, +) -> Response { + let mut context = context.clone(); + context.insert("assets", assets); + + let response = match templates.render(path, &context) { + Ok(template) => Response::new(template.into()), + Err(err) => Response::new( + error::template( + assets, + templates, + "Erreur serveur", + &format!( + "Erreur lors de la préparation de la page : {:?}", + err + ), + ) + .into(), + ), + }; + + with_headers( + response, + vec![(CONTENT_TYPE, "text/html"), (CACHE_CONTROL, "no-cache")], + ) +} + +pub fn redirect(uri: &str) -> Response { + let mut response = Response::default(); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + with_headers(response, vec![(LOCATION, uri), (CACHE_CONTROL, "no-cache")]) +} + +pub fn not_found() -> Response { + let mut response = Response::default(); + *response.status_mut() = StatusCode::NOT_FOUND; + response +} + +pub async fn file(filename: &str) -> Response { + if let Ok(file) = File::open(filename).await { + let stream = FramedRead::new(file, BytesCodec::new()); + let body = Body::wrap_stream(stream); + with_header(Response::new(body), CACHE_CONTROL, "max-age=3153600000") + } else { + not_found() + } +} diff --git a/src/controller/wallet.rs b/src/controller/wallet.rs new file mode 100644 index 0000000..2a4a593 --- /dev/null +++ b/src/controller/wallet.rs @@ -0,0 +1,13 @@ +use sqlx::sqlite::SqlitePool; +use std::collections::HashMap; +use tera::Tera; + +use crate::model::user::User; + +#[derive(Clone)] +pub struct Wallet { + pub pool: SqlitePool, + pub assets: HashMap, + pub templates: Tera, + pub user: User, +} -- cgit v1.2.3