Working invites.

This commit is contained in:
Joe Ardent 2024-01-14 16:55:35 -08:00
parent a8efdac3dd
commit df01960be5
3 changed files with 14 additions and 18 deletions

View file

@ -62,13 +62,11 @@ pub async fn app(db_pool: sqlx::SqlitePool) -> IntoMakeService<axum::Router> {
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());
tracing_subscriber::fmt().with_target(false).pretty().init();
axum::Router::new() axum::Router::new()
.route("/", get(handle_slash).post(handle_slash)) .route("/", get(handle_slash).post(handle_slash))
.nest_service("/assets", assets_svc) .nest_service("/assets", assets_svc)
.route("/signup", post(post_create_user))
.route("/signup/:invitation", get(get_create_user)) .route("/signup/:invitation", get(get_create_user))
.route("/signup/", post(post_create_user))
.route("/signup_success/:user", get(get_signup_success)) .route("/signup_success/:user", get(get_signup_success))
.route("/login", get(get_login).post(post_login)) .route("/login", get(get_login).post(post_login))
.route("/logout", get(get_logout).post(post_logout)) .route("/logout", get(get_logout).post(post_logout))

View file

@ -15,8 +15,6 @@ use unicode_segmentation::UnicodeSegmentation;
use super::{templates::*, Invitation}; use super::{templates::*, Invitation};
use crate::{util::empty_string_as_none, User}; use crate::{util::empty_string_as_none, User};
pub(crate) const CREATE_QUERY: &str =
"insert into users (username, displayname, email, pwhash, invited_by) values ($1, $2, $3, $4, $5) returning *";
const ID_QUERY: &str = "select * from users where id = $1"; const ID_QUERY: &str = "select * from users where id = $1";
//-************************************************************************ //-************************************************************************
@ -98,8 +96,6 @@ pub async fn post_create_user(
State(pool): State<SqlitePool>, State(pool): State<SqlitePool>,
Form(signup): Form<SignupForm>, Form(signup): Form<SignupForm>,
) -> Result<impl IntoResponse, CreateUserError> { ) -> Result<impl IntoResponse, CreateUserError> {
dbg!(&signup);
use crate::util::validate_optional_length; use crate::util::validate_optional_length;
let username = signup.username.trim(); let username = signup.username.trim();
let password = signup.password.trim(); let password = signup.password.trim();
@ -151,7 +147,6 @@ pub async fn get_signup_success(
Path(id): Path<String>, Path(id): Path<String>,
State(pool): State<SqlitePool>, State(pool): State<SqlitePool>,
) -> Response { ) -> Response {
dbg!(&id);
let id = id.trim(); let id = id.trim();
let id = Julid::from_string(id).unwrap_or_default(); let id = Julid::from_string(id).unwrap_or_default();
let user: User = { let user: User = {
@ -185,6 +180,9 @@ pub(crate) async fn create_user(
pool: &SqlitePool, pool: &SqlitePool,
invitation: &str, invitation: &str,
) -> Result<User, CreateUserError> { ) -> Result<User, CreateUserError> {
const CREATE_QUERY: &str =
"insert into users (username, displayname, email, pwhash, invited_by) values ($1, $2, $3, $4, $5) returning *";
// Argon2 with default params (Argon2id v19) // Argon2 with default params (Argon2id v19)
let argon2 = Argon2::default(); let argon2 = Argon2::default();
let salt = SaltString::generate(&mut OsRng); let salt = SaltString::generate(&mut OsRng);
@ -209,9 +207,10 @@ pub(crate) async fn create_user(
.bind(email) .bind(email)
.bind(&pwhash) .bind(&pwhash)
.bind(invited_by) .bind(invited_by)
.fetch_one(pool) .fetch_one(&mut *tx)
.await .await
.map_err(|e| { .map_err(|e| {
tracing::info!("Got error inserting new user: {e}");
match e { match e {
sqlx::Error::Database(db) => { sqlx::Error::Database(db) => {
let exit = db.code().unwrap_or_default().parse().unwrap_or(0); let exit = db.code().unwrap_or_default().parse().unwrap_or(0);
@ -306,8 +305,6 @@ mod test {
.content_type(FORM_CONTENT_TYPE) .content_type(FORM_CONTENT_TYPE)
.await; .await;
dbg!(&resp.text());
assert_eq!(StatusCode::SEE_OTHER, resp.status_code()); assert_eq!(StatusCode::SEE_OTHER, resp.status_code());
// get the new user from the db // get the new user from the db
@ -327,16 +324,18 @@ mod test {
let path = format!("/signup/{invitation}"); let path = format!("/signup/{invitation}");
let resp = server.get(&path).await; let resp = server.get(&path).await;
let body = std::str::from_utf8(resp.as_bytes()).unwrap(); let body = std::str::from_utf8(resp.as_bytes()).unwrap();
let expected = SignupPage::default().to_string(); let expected = SignupPage {
invitation,
..Default::default()
}
.to_string();
assert_eq!(&expected, body); assert_eq!(&expected, body);
}); });
} }
#[test] #[test]
fn handle_signup_success() { fn handle_signup_success() {
dbg!("getting the pool");
let pool = get_db_pool(); let pool = get_db_pool();
dbg!("got the pool");
let rt = Runtime::new().unwrap(); let rt = Runtime::new().unwrap();
rt.block_on(async { rt.block_on(async {
let server = server_with_pool(&pool).await; let server = server_with_pool(&pool).await;
@ -354,9 +353,7 @@ mod test {
assert_eq!(StatusCode::SEE_OTHER, resp.status_code()); assert_eq!(StatusCode::SEE_OTHER, resp.status_code());
// get the new user from the db // get the new user from the db
let user = User::try_get("good_user", &pool).await.unwrap(); let user = User::try_get("good_user", &pool).await.unwrap().unwrap();
dbg!(&user);
let user = user.unwrap();
let id = user.id; let id = user.id;
@ -625,7 +622,7 @@ mod test {
let path = "/signup_success/nope"; let path = "/signup_success/nope";
let resp = server.get(&path).expect_failure().await; let resp = server.get(path).expect_failure().await;
assert_eq!(resp.status_code(), StatusCode::SEE_OTHER); assert_eq!(resp.status_code(), StatusCode::SEE_OTHER);
}); });
} }

View file

@ -8,6 +8,7 @@
<p> <p>
<form action="/signup" enctype="application/x-www-form-urlencoded" method="post"> <form action="/signup" enctype="application/x-www-form-urlencoded" method="post">
<input type="hidden" name="invitation" value="{{self.invitation}}">
<label for="username">Username</label> <label for="username">Username</label>
<input type="text" name="username" id="username" minlength="1" maxlength="20" required></br> <input type="text" name="username" id="username" minlength="1" maxlength="20" required></br>
<label for="displayname">Displayname (optional)</label> <label for="displayname">Displayname (optional)</label>