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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|