shoutdotdev/src/users.rs

99 lines
2.9 KiB
Rust
Raw Normal View History

2024-11-01 00:07:33 -07:00
use axum::{
extract::FromRequestParts,
http::request::Parts,
response::{IntoResponse, Redirect, Response},
RequestPartsExt,
};
use diesel::{
2025-01-28 18:01:43 -08:00
associations::Identifiable,
deserialize::Queryable,
dsl::{insert_into, AsSelect, Eq, Select},
pg::Pg,
prelude::*,
2024-11-01 00:07:33 -07:00
Selectable,
};
use uuid::Uuid;
2025-01-28 18:01:43 -08:00
use crate::{app_error::AppError, app_state::AppState, auth::AuthInfo, schema::users::dsl::*};
2024-11-01 00:07:33 -07:00
#[derive(Clone, Debug, Identifiable, Insertable, Queryable, Selectable)]
2025-01-28 18:01:43 -08:00
#[diesel(table_name = crate::schema::users)]
2024-11-01 00:07:33 -07:00
#[diesel(check_for_backend(Pg))]
pub struct User {
pub id: Uuid,
pub uid: String,
pub email: String,
}
2025-01-28 18:01:43 -08:00
impl User {
pub fn all() -> Select<users, AsSelect<User, Pg>> {
users.select(User::as_select())
}
pub fn with_uid(uid_value: &str) -> Eq<uid, &str> {
uid.eq(uid_value)
}
}
2024-11-01 00:07:33 -07:00
#[derive(Clone, Debug)]
pub struct CurrentUser(pub User);
impl FromRequestParts<AppState> for CurrentUser {
type Rejection = CurrentUserRejection;
async fn from_request_parts(
parts: &mut Parts,
state: &AppState,
) -> Result<Self, <Self as FromRequestParts<AppState>>::Rejection> {
let auth_info = parts
.extract_with_state::<AuthInfo, AppState>(state)
.await
.map_err(|_| CurrentUserRejection::AuthRequired(state.settings.base_path.clone()))?;
let current_user = state
.db_pool
.get()
.await
.map_err(|err| CurrentUserRejection::InternalServerError(err.into()))?
.interact(move |conn| {
2025-01-28 18:01:43 -08:00
let maybe_current_user = User::all()
.filter(User::with_uid(&auth_info.sub))
2024-11-01 00:07:33 -07:00
.first(conn)
.optional()?;
if let Some(current_user) = maybe_current_user {
return Ok(current_user);
}
let new_user = User {
id: Uuid::now_v7(),
uid: auth_info.sub,
email: auth_info.email,
};
2025-01-28 18:01:43 -08:00
insert_into(users)
.values(new_user)
.on_conflict(uid)
2024-11-01 00:07:33 -07:00
.do_nothing()
2025-01-28 18:01:43 -08:00
.returning(User::as_returning())
2024-11-01 00:07:33 -07:00
.get_result(conn)
})
.await
.unwrap()
.map_err(|err| CurrentUserRejection::InternalServerError(err.into()))?;
Ok(CurrentUser(current_user))
}
}
pub enum CurrentUserRejection {
AuthRequired(String),
InternalServerError(AppError),
}
impl IntoResponse for CurrentUserRejection {
fn into_response(self) -> Response {
match self {
Self::AuthRequired(base_path) => {
Redirect::to(&format!("{}/auth/login", base_path)).into_response()
}
Self::InternalServerError(err) => err.into_response(),
}
}
}