Add logging and prettier error pages.
This commit is contained in:
parent
0826f2f0e3
commit
ba027bb1cd
3 changed files with 82 additions and 25 deletions
|
@ -23,27 +23,35 @@ lazy_static! {
|
||||||
|
|
||||||
#[Error(desc = "Could not create user.")]
|
#[Error(desc = "Could not create user.")]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct CreateUserError(#[from] CreateUserErrorKind);
|
pub struct CreateUserError(#[from] pub CreateUserErrorKind);
|
||||||
|
|
||||||
impl IntoResponse for CreateUserError {
|
impl IntoResponse for CreateUserError {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
(StatusCode::FORBIDDEN, format!("{:?}", self.0)).into_response()
|
let mut resp = SignupErrorPage(format!("{self}")).into_response();
|
||||||
|
*resp.status_mut() = StatusCode::FORBIDDEN;
|
||||||
|
resp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Error]
|
#[Error]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum CreateUserErrorKind {
|
pub enum CreateUserErrorKind {
|
||||||
|
#[error(desc = "That username already exists")]
|
||||||
AlreadyExists,
|
AlreadyExists,
|
||||||
#[error(desc = "Usernames must be between 1 and 50 characters long")]
|
#[error(desc = "Usernames must be between 1 and 50 characters long")]
|
||||||
BadUsername,
|
BadUsername,
|
||||||
|
#[error(desc = "Your passwords didn't match")]
|
||||||
PasswordMismatch,
|
PasswordMismatch,
|
||||||
#[error(desc = "Password must have at least 4 and at most 100 characters")]
|
#[error(desc = "Password must have at least 4 and at most 100 characters")]
|
||||||
BadPassword,
|
BadPassword,
|
||||||
#[error(desc = "Display name must be less than 100 characters long")]
|
#[error(desc = "Display name must be less than 100 characters long")]
|
||||||
BadDisplayname,
|
BadDisplayname,
|
||||||
|
#[error(desc = "Your email is too short, it simply can't be real")]
|
||||||
BadEmail,
|
BadEmail,
|
||||||
|
#[error(desc = "We could not verify your payment")]
|
||||||
BadPayment,
|
BadPayment,
|
||||||
|
#[error(desc = "We couldn't retrieve your info from this browser session")]
|
||||||
|
NoFormFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
|
||||||
|
@ -76,28 +84,38 @@ pub async fn post_signup(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// Handles the case when there was an error in the form
|
||||||
pub async fn get_edit_signup(
|
pub async fn get_edit_signup(
|
||||||
session: Session,
|
session: Session,
|
||||||
receipt: Option<Path<String>>,
|
receipt: Option<Path<String>>,
|
||||||
) -> Result<impl IntoResponse, CreateUserError> {
|
) -> Result<impl IntoResponse, CreateUserError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub async fn post_edit_signup(
|
/// Redirected from Stripe with the receipt of payment.
|
||||||
session: Session,
|
|
||||||
Form(form): Form<SignupForm>,
|
|
||||||
) -> Result<impl IntoResponse, CreateUserError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called from Stripe with the receipt of payment.
|
|
||||||
pub async fn payment_success(session: Session, receipt: Option<Path<String>>) -> impl IntoResponse {
|
pub async fn payment_success(session: Session, receipt: Option<Path<String>>) -> impl IntoResponse {
|
||||||
let user: User = session.get(&SIGNUP_KEY).await.unwrap().unwrap_or_default();
|
let user: User = session.get(&SIGNUP_KEY).await.unwrap().unwrap_or_default();
|
||||||
|
|
||||||
|
if receipt.is_none() {
|
||||||
|
log::info!("Got {:?} from the session, but no receipt.", &user);
|
||||||
|
return CreateUserError(CreateUserErrorKind::BadPayment).into_response();
|
||||||
|
}
|
||||||
|
let Path(receipt) = receipt.unwrap();
|
||||||
|
|
||||||
if user == User::default() {
|
if user == User::default() {
|
||||||
return SignupErrorPage("who you?".to_string()).into_response();
|
log::warn!("Could not find user in session; got receipt {}", receipt);
|
||||||
|
return CreateUserError(CreateUserErrorKind::NoFormFound).into_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check Stripe for the receipt, verify it's legit
|
// TODO: call the forgejo admin api to create the user
|
||||||
|
|
||||||
|
session.delete().await.unwrap_or_else(|e| {
|
||||||
|
log::error!("Got error deleting {} from session, got {}", &user, e);
|
||||||
|
});
|
||||||
|
|
||||||
|
log::info!("Added {:?}", &user);
|
||||||
SignupSuccessPage(user).into_response()
|
SignupSuccessPage(user).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
src/main.rs
41
src/main.rs
|
@ -1,6 +1,9 @@
|
||||||
use std::net::SocketAddr;
|
use std::{io::Write, net::SocketAddr};
|
||||||
|
|
||||||
use axum::{routing::get, Router};
|
use axum::{
|
||||||
|
routing::{get, MethodRouter},
|
||||||
|
Router,
|
||||||
|
};
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};
|
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};
|
||||||
|
|
||||||
|
@ -20,6 +23,7 @@ use user::User;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
init();
|
||||||
// for javascript and css
|
// for javascript and css
|
||||||
let assets_dir = std::env::current_dir().unwrap().join("assets");
|
let assets_dir = std::env::current_dir().unwrap().join("assets");
|
||||||
let assets_svc = ServeDir::new(assets_dir.as_path());
|
let assets_svc = ServeDir::new(assets_dir.as_path());
|
||||||
|
@ -33,7 +37,8 @@ async fn main() {
|
||||||
// the core application, defining the routes and handlers
|
// the core application, defining the routes and handlers
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.nest_service("/assets", assets_svc)
|
.nest_service("/assets", assets_svc)
|
||||||
.route("/signup", get(get_signup).post(post_signup))
|
.stripped_clone("/signup/", get(get_signup).post(post_signup))
|
||||||
|
.stripped_clone("/payment_success/", get(payment_success))
|
||||||
.route("/payment_success/:receipt", get(payment_success))
|
.route("/payment_success/:receipt", get(payment_success))
|
||||||
.layer(session_layer)
|
.layer(session_layer)
|
||||||
.into_make_service();
|
.into_make_service();
|
||||||
|
@ -43,3 +48,33 @@ async fn main() {
|
||||||
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
dotenvy::dotenv().expect("Could not read .env file.");
|
||||||
|
env_logger::builder()
|
||||||
|
.format(|buf, record| {
|
||||||
|
//
|
||||||
|
let ts = buf.timestamp();
|
||||||
|
writeln!(buf, "{}: {}", ts, record.args())
|
||||||
|
})
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds both routes, with and without a trailing slash.
|
||||||
|
trait RouterPathStrip<S>
|
||||||
|
where
|
||||||
|
S: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn stripped_clone(self, path: &str, method_router: MethodRouter<S>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> RouterPathStrip<S> for Router<S>
|
||||||
|
where
|
||||||
|
S: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn stripped_clone(self, path: &str, method_router: MethodRouter<S>) -> Self {
|
||||||
|
assert!(path.ends_with('/'));
|
||||||
|
self.route(path, method_router.clone())
|
||||||
|
.route(path.trim_end_matches('/'), method_router)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
24
src/user.rs
24
src/user.rs
|
@ -13,17 +13,21 @@ pub struct User {
|
||||||
|
|
||||||
impl Debug for User {
|
impl Debug for User {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let pw_check = if self.password == self.pw_verify {
|
if self == &User::default() {
|
||||||
"password matched"
|
write!(f, "Default User")
|
||||||
} else {
|
} else {
|
||||||
"PASSWORD MISMATCH"
|
let pw_check = if self.password == self.pw_verify {
|
||||||
};
|
"password matched"
|
||||||
f.debug_struct("User")
|
} else {
|
||||||
.field("username", &self.username)
|
"PASSWORD MISMATCH"
|
||||||
.field("displayname", &self.displayname)
|
};
|
||||||
.field("email", &self.email)
|
f.debug_struct("User")
|
||||||
.field("pw-check", &pw_check)
|
.field("username", &self.username)
|
||||||
.finish()
|
.field("displayname", &self.displayname)
|
||||||
|
.field("email", &self.email)
|
||||||
|
.field("pw-check", &pw_check)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue