Compare commits

...

4 Commits

Author SHA1 Message Date
62a7103423 Add signup endpoint
All checks were successful
Push Workflows / rustfmt (push) Successful in 6s
Push Workflows / tailwind-build (push) Successful in 10s
Push Workflows / clippy (push) Successful in 21s
Push Workflows / test (push) Successful in 29s
Push Workflows / docs (push) Successful in 27s
Push Workflows / build (push) Successful in 52s
Push Workflows / nix-build (push) Successful in 5m17s
2026-06-27 22:34:07 -04:00
d028636e43 Add Unauthorized error 2026-06-27 22:33:50 -04:00
7ca7056fac Add Config to router as Extension 2026-06-27 22:32:57 -04:00
ab90cd81f9 Add function to create user 2026-06-27 22:32:36 -04:00
4 changed files with 53 additions and 3 deletions

View File

@@ -8,12 +8,45 @@ if #[cfg(feature = "server")] {
use dioxus::server::axum::Extension; use dioxus::server::axum::Extension;
use crate::server::auth::AuthSession; use crate::server::{auth::{AuthSession, create_user}, config::Config, database::DbPool};
use crate::util::error::{AuthError, Contextualize, Error, ErrorType}; use crate::util::error::{AuthError, Contextualize, Error, ErrorType};
} }
} }
#[post("/api/v1/auth/signup", mut auth: Extension<AuthSession>, db_pool: Extension<DbPool>, config: Extension<Config>)]
pub async fn signup(credentials: UserCredentials) -> Result<User> {
if !config.auth.open_signup {
return Err(Error::new_here(ErrorType::Auth(AuthError::Unauthorized)));
}
// Don't allow signup when already logged in
if auth.user.is_some() {
return Err(Error::new_here(ErrorType::Auth(AuthError::Unauthorized)));
}
let hashed_creds = credentials
.try_hash()
.map_err(|e| Error::message_here(e.to_string()))
.err_context("Error hashing new user credentials")?;
let mut db_conn = db_pool
.get()
.await
.err_context("Failed to get database pool connection")?;
let new_user = create_user(&mut db_conn, &hashed_creds)
.await
.err_context("Error creating user")?;
// Don't return this to the client, logging in immediately isn't strictly necessary
if let Err(e) = auth.login(&new_user).await {
tracing::warn!("Failed to log in user after creating: {e}");
}
Ok(new_user.into())
}
#[post("/api/v1/auth/login", mut auth: Extension<AuthSession>)] #[post("/api/v1/auth/login", mut auth: Extension<AuthSession>)]
pub async fn login(credentials: UserCredentials) -> Result<User> { pub async fn login(credentials: UserCredentials) -> Result<User> {
let db_user = match auth.authenticate(credentials).await { let db_user = match auth.authenticate(credentials).await {

View File

@@ -3,7 +3,7 @@ use diesel::prelude::*;
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use tower_sessions_redis_store::RedisStore; use tower_sessions_redis_store::RedisStore;
use crate::models::user::{DbUser, UserCredentials}; use crate::models::user::{DbUser, HashedUserCredentials, UserCredentials};
use crate::server::{ use crate::server::{
database::{DbConn, DbPool}, database::{DbConn, DbPool},
key_val_store::KeyValPool, key_val_store::KeyValPool,
@@ -76,6 +76,17 @@ impl AuthnBackend for AuthBackend {
} }
} }
pub async fn create_user(
db_conn: &mut DbConn,
credentials: &HashedUserCredentials,
) -> Result<DbUser> {
diesel::insert_into(crate::schema::users::table)
.values(credentials)
.get_result(db_conn)
.await
.err_context("Error creating user")
}
pub async fn get_user_by_id(db_conn: &mut DbConn, id: i32) -> Result<Option<DbUser>> { pub async fn get_user_by_id(db_conn: &mut DbConn, id: i32) -> Result<Option<DbUser>> {
crate::schema::users::table crate::schema::users::table
.find(id) .find(id)

View File

@@ -31,6 +31,7 @@ async fn router_setup() -> Result<Router> {
let auth_layer = build_auth_layer(db_pool.clone(), key_val_pool); let auth_layer = build_auth_layer(db_pool.clone(), key_val_pool);
let router = dioxus::server::router(App) let router = dioxus::server::router(App)
.layer(Extension(config))
.layer(Extension(db_pool)) .layer(Extension(db_pool))
.layer(auth_layer); .layer(auth_layer);

View File

@@ -239,7 +239,9 @@ impl From<ServerFnError> for Error {
impl dioxus_fullstack::AsStatusCode for Error { impl dioxus_fullstack::AsStatusCode for Error {
fn as_status_code(&self) -> StatusCode { fn as_status_code(&self) -> StatusCode {
match &self.source { match &self.source {
ErrorType::Auth(AuthError::InvalidCredentials) => StatusCode::UNAUTHORIZED, ErrorType::Auth(AuthError::InvalidCredentials | AuthError::Unauthorized) => {
StatusCode::UNAUTHORIZED
}
ErrorType::Database(msg) if *msg == (diesel::result::Error::NotFound).to_string() => { ErrorType::Database(msg) if *msg == (diesel::result::Error::NotFound).to_string() => {
StatusCode::NOT_FOUND StatusCode::NOT_FOUND
} }
@@ -343,4 +345,7 @@ pub enum AuthError {
#[error("{0}")] #[error("{0}")]
Error(String), Error(String),
#[error("Unauthorized")]
Unauthorized,
} }