ezidam: split home routes
This commit is contained in:
parent
92a08db8fe
commit
cdcdba2b9a
8 changed files with 160 additions and 141 deletions
|
|
@ -24,7 +24,7 @@ impl AdminMenu {
|
||||||
MainItem {
|
MainItem {
|
||||||
id: AdminMenu::Exit.id(),
|
id: AdminMenu::Exit.id(),
|
||||||
label: "Exit admin panel",
|
label: "Exit admin panel",
|
||||||
link: uri!(routes::root::homepage).to_string(),
|
link: uri!(routes::root::home::homepage).to_string(),
|
||||||
icon: Icon::Logout.svg,
|
icon: Icon::Logout.svg,
|
||||||
sub: None,
|
sub: None,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ impl UserMenu {
|
||||||
vec![MainItem {
|
vec![MainItem {
|
||||||
id: UserMenu::Home.id(),
|
id: UserMenu::Home.id(),
|
||||||
label: "Home",
|
label: "Home",
|
||||||
link: uri!(routes::root::homepage).to_string(),
|
link: uri!(routes::root::home::homepage).to_string(),
|
||||||
icon: Icon::Home.svg,
|
icon: Icon::Home.svg,
|
||||||
sub: None,
|
sub: None,
|
||||||
}]
|
}]
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,26 @@
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use crate::tokens::{JWT_COOKIE_NAME, REFRESH_TOKEN_COOKIE_NAME};
|
use avatar::*;
|
||||||
use rocket::http::{Cookie, CookieJar};
|
use home::*;
|
||||||
use rocket::{get, post};
|
use logo::*;
|
||||||
use settings::Settings;
|
use logout::*;
|
||||||
use users::User;
|
|
||||||
|
pub mod avatar;
|
||||||
|
pub mod home;
|
||||||
|
pub mod logo;
|
||||||
|
pub mod logout;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![
|
routes![
|
||||||
logo,
|
get_logo,
|
||||||
avatar,
|
user_avatar,
|
||||||
homepage,
|
homepage,
|
||||||
homepage_user,
|
homepage_user,
|
||||||
homepage_redirect,
|
homepage_redirect,
|
||||||
redirect_to_setup,
|
redirect_to_setup,
|
||||||
logout,
|
request_logout,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/logo")]
|
|
||||||
async fn logo(mut db: Connection<Database>) -> Result<FileFromBytes> {
|
|
||||||
// Get settings
|
|
||||||
let settings = Settings::get(&mut *db).await?;
|
|
||||||
|
|
||||||
// HTTP response
|
|
||||||
Ok(FileFromBytes::from(settings.business_logo()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::tests::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn logo() {
|
|
||||||
// Setup http server
|
|
||||||
let client = setup_rocket_testing();
|
|
||||||
|
|
||||||
// Make request
|
|
||||||
let response = client.get(uri!(super::logo)).dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
|
|
||||||
// Assert size of logo
|
|
||||||
let logo_length = response.into_bytes().expect("bytes containing logo").len();
|
|
||||||
use settings::DEFAULT_BUSINESS_LOGO;
|
|
||||||
assert_eq!(
|
|
||||||
logo_length,
|
|
||||||
DEFAULT_BUSINESS_LOGO.len(),
|
|
||||||
"Invalid logo size in bytes, value was `{logo_length}`",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/avatar/<user_id>?<size>")]
|
|
||||||
async fn avatar(
|
|
||||||
mut db: Connection<Database>,
|
|
||||||
_user: JwtUser,
|
|
||||||
user_id: RocketUserID,
|
|
||||||
size: Option<u32>,
|
|
||||||
) -> Result<FileFromBytes> {
|
|
||||||
// Verify existence of user
|
|
||||||
User::get_by_login(&mut *db, user_id.0.as_ref())
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| Error::not_found(user_id.0.to_string()))?;
|
|
||||||
|
|
||||||
// Generate avatar
|
|
||||||
let avatar = task::spawn_blocking(move || {
|
|
||||||
let mut avatar = identicon_rs::Identicon::new(user_id.0);
|
|
||||||
|
|
||||||
// Set optional size
|
|
||||||
if let Some(size) = size {
|
|
||||||
avatar.set_scale(size)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
avatar.export_png_data()
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// HTTP response
|
|
||||||
avatar
|
|
||||||
.map(FileFromBytes::from)
|
|
||||||
.map_err(Error::internal_server_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod content {
|
pub mod content {
|
||||||
use jwt::JwtClaims;
|
use jwt::JwtClaims;
|
||||||
use rocket::serde::Serialize;
|
use rocket::serde::Serialize;
|
||||||
|
|
@ -92,68 +32,3 @@ pub mod content {
|
||||||
pub user: JwtClaims,
|
pub user: JwtClaims,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
async fn redirect_to_setup(_setup: NeedSetup) -> Redirect {
|
|
||||||
Redirect::to(uri!(super::setup::setup))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
|
||||||
async fn homepage(admin: JwtAdmin) -> Page {
|
|
||||||
Page::Homepage(content::Homepage { user: admin.0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/", rank = 3)]
|
|
||||||
async fn homepage_user(user: JwtUser) -> Page {
|
|
||||||
Page::Homepage(content::Homepage { user: user.0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/", rank = 4)]
|
|
||||||
async fn homepage_redirect() -> Redirect {
|
|
||||||
Redirect::to(uri!(super::oauth::authorize::authorize_ezidam))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/logout")]
|
|
||||||
async fn logout(
|
|
||||||
mut db: Connection<Database>,
|
|
||||||
refresh_token: RefreshToken,
|
|
||||||
cookie_jar: &CookieJar<'_>,
|
|
||||||
) -> Result<Redirect> {
|
|
||||||
let mut transaction = db.begin().await?;
|
|
||||||
|
|
||||||
let refresh_token = refresh_tokens::RefreshToken::get_one(&mut transaction, &refresh_token.0)
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| Error::not_found("Unknown refresh token"))?;
|
|
||||||
|
|
||||||
// Delete cookies
|
|
||||||
cookie_jar.remove(Cookie::named(JWT_COOKIE_NAME));
|
|
||||||
cookie_jar.remove(Cookie::named(REFRESH_TOKEN_COOKIE_NAME));
|
|
||||||
|
|
||||||
// If refresh token has already been used
|
|
||||||
if refresh_token.has_been_used() {
|
|
||||||
// Revoke all refresh tokens for user
|
|
||||||
refresh_tokens::RefreshToken::revoke_all_for_user(&mut transaction, refresh_token.user())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
transaction.commit().await?;
|
|
||||||
|
|
||||||
return Err(Error::forbidden("This refresh token has already been used"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If token has been revoked
|
|
||||||
if refresh_token.is_revoked() {
|
|
||||||
return Err(Error::forbidden("This refresh token has been revoked"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If token has expired
|
|
||||||
if refresh_token.has_expired() {
|
|
||||||
return Err(Error::forbidden("This refresh token has expired"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revoke token
|
|
||||||
refresh_token.revoke(&mut transaction).await?;
|
|
||||||
|
|
||||||
transaction.commit().await?;
|
|
||||||
|
|
||||||
Ok(Redirect::to(uri!(homepage)))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
34
crates/ezidam/src/routes/root/avatar.rs
Normal file
34
crates/ezidam/src/routes/root/avatar.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::routes::prelude::*;
|
||||||
|
use rocket::get;
|
||||||
|
use users::User;
|
||||||
|
|
||||||
|
#[get("/avatar/<user_id>?<size>")]
|
||||||
|
pub async fn user_avatar(
|
||||||
|
mut db: Connection<Database>,
|
||||||
|
_user: JwtUser,
|
||||||
|
user_id: RocketUserID,
|
||||||
|
size: Option<u32>,
|
||||||
|
) -> Result<FileFromBytes> {
|
||||||
|
// Verify existence of user
|
||||||
|
User::get_by_login(&mut *db, user_id.0.as_ref())
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| Error::not_found(user_id.0.to_string()))?;
|
||||||
|
|
||||||
|
// Generate avatar
|
||||||
|
let avatar = task::spawn_blocking(move || {
|
||||||
|
let mut avatar = identicon_rs::Identicon::new(user_id.0);
|
||||||
|
|
||||||
|
// Set optional size
|
||||||
|
if let Some(size) = size {
|
||||||
|
avatar.set_scale(size)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
avatar.export_png_data()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// HTTP response
|
||||||
|
avatar
|
||||||
|
.map(FileFromBytes::from)
|
||||||
|
.map_err(Error::internal_server_error)
|
||||||
|
}
|
||||||
23
crates/ezidam/src/routes/root/home.rs
Normal file
23
crates/ezidam/src/routes/root/home.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use super::content;
|
||||||
|
use crate::routes::prelude::*;
|
||||||
|
use rocket::get;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
pub async fn homepage(admin: JwtAdmin) -> Page {
|
||||||
|
Page::Homepage(content::Homepage { user: admin.0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/", rank = 2)]
|
||||||
|
pub async fn homepage_user(user: JwtUser) -> Page {
|
||||||
|
Page::Homepage(content::Homepage { user: user.0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/", rank = 3)]
|
||||||
|
pub async fn redirect_to_setup(_setup: NeedSetup) -> Redirect {
|
||||||
|
Redirect::to(uri!(crate::routes::setup::setup))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/", rank = 4)]
|
||||||
|
pub async fn homepage_redirect() -> Redirect {
|
||||||
|
Redirect::to(uri!(crate::routes::oauth::authorize::authorize_ezidam))
|
||||||
|
}
|
||||||
36
crates/ezidam/src/routes/root/logo.rs
Normal file
36
crates/ezidam/src/routes/root/logo.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use crate::routes::prelude::*;
|
||||||
|
use rocket::get;
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
|
#[get("/logo")]
|
||||||
|
pub async fn get_logo(mut db: Connection<Database>) -> Result<FileFromBytes> {
|
||||||
|
// Get settings
|
||||||
|
let settings = Settings::get(&mut *db).await?;
|
||||||
|
|
||||||
|
// HTTP response
|
||||||
|
Ok(FileFromBytes::from(settings.business_logo()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::tests::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn logo() {
|
||||||
|
// Setup http server
|
||||||
|
let client = setup_rocket_testing();
|
||||||
|
|
||||||
|
// Make request
|
||||||
|
let response = client.get(uri!(super::logo)).dispatch();
|
||||||
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
|
||||||
|
// Assert size of logo
|
||||||
|
let logo_length = response.into_bytes().expect("bytes containing logo").len();
|
||||||
|
use settings::DEFAULT_BUSINESS_LOGO;
|
||||||
|
assert_eq!(
|
||||||
|
logo_length,
|
||||||
|
DEFAULT_BUSINESS_LOGO.len(),
|
||||||
|
"Invalid logo size in bytes, value was `{logo_length}`",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
crates/ezidam/src/routes/root/logout.rs
Normal file
49
crates/ezidam/src/routes/root/logout.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
use crate::routes::prelude::*;
|
||||||
|
use crate::tokens::{JWT_COOKIE_NAME, REFRESH_TOKEN_COOKIE_NAME};
|
||||||
|
use rocket::http::{Cookie, CookieJar};
|
||||||
|
use rocket::post;
|
||||||
|
|
||||||
|
#[post("/logout")]
|
||||||
|
pub async fn request_logout(
|
||||||
|
mut db: Connection<Database>,
|
||||||
|
refresh_token: RefreshToken,
|
||||||
|
cookie_jar: &CookieJar<'_>,
|
||||||
|
) -> Result<Redirect> {
|
||||||
|
let mut transaction = db.begin().await?;
|
||||||
|
|
||||||
|
let refresh_token = refresh_tokens::RefreshToken::get_one(&mut transaction, &refresh_token.0)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| Error::not_found("Unknown refresh token"))?;
|
||||||
|
|
||||||
|
// Delete cookies
|
||||||
|
cookie_jar.remove(Cookie::named(JWT_COOKIE_NAME));
|
||||||
|
cookie_jar.remove(Cookie::named(REFRESH_TOKEN_COOKIE_NAME));
|
||||||
|
|
||||||
|
// If refresh token has already been used
|
||||||
|
if refresh_token.has_been_used() {
|
||||||
|
// Revoke all refresh tokens for user
|
||||||
|
refresh_tokens::RefreshToken::revoke_all_for_user(&mut transaction, refresh_token.user())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
transaction.commit().await?;
|
||||||
|
|
||||||
|
return Err(Error::forbidden("This refresh token has already been used"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If token has been revoked
|
||||||
|
if refresh_token.is_revoked() {
|
||||||
|
return Err(Error::forbidden("This refresh token has been revoked"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If token has expired
|
||||||
|
if refresh_token.has_expired() {
|
||||||
|
return Err(Error::forbidden("This refresh token has expired"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revoke token
|
||||||
|
refresh_token.revoke(&mut transaction).await?;
|
||||||
|
|
||||||
|
transaction.commit().await?;
|
||||||
|
|
||||||
|
Ok(Redirect::to(uri!(super::homepage)))
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ pub fn routes() -> Vec<Route> {
|
||||||
|
|
||||||
#[get("/setup")]
|
#[get("/setup")]
|
||||||
async fn setup_completed(_setup: CompletedSetup) -> Redirect {
|
async fn setup_completed(_setup: CompletedSetup) -> Redirect {
|
||||||
Redirect::to(uri!(super::root::homepage))
|
Redirect::to(uri!(super::root::home::homepage))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/setup", rank = 2)]
|
#[get("/setup", rank = 2)]
|
||||||
|
|
@ -100,7 +100,9 @@ async fn create_first_account(
|
||||||
transaction.commit().await?;
|
transaction.commit().await?;
|
||||||
|
|
||||||
// Redirect to home
|
// Redirect to home
|
||||||
Ok(Either::Left(Redirect::to(uri!(super::root::homepage))))
|
Ok(Either::Left(Redirect::to(uri!(
|
||||||
|
super::root::home::homepage
|
||||||
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue