idkthings_core 0.1.0

core stuff for idkthings
Documentation
use crate::auth::MaybeCurrentUser;
use idkthings_core_macros::entity;
use mongodb::bson::doc;
use mongodb::Database;
use poem::{Endpoint, Middleware, Request, Result};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

pub struct MaybeCurrentUserMiddleware {
    pub database: Database,
}

impl<E: Endpoint> Middleware<E> for MaybeCurrentUserMiddleware {
    type Output = MaybeCurrentUserMiddlewareImpl<E>;

    fn transform(&self, ep: E) -> Self::Output {
        MaybeCurrentUserMiddlewareImpl {
            ep,
            database: self.database.clone(),
        }
    }
}

pub struct MaybeCurrentUserMiddlewareImpl<E> {
    ep: E,
    database: Database,
}

#[poem::async_trait]
impl<E: Endpoint> Endpoint for MaybeCurrentUserMiddlewareImpl<E> {
    type Output = E::Output;

    async fn call(&self, mut req: Request) -> Result<Self::Output> {
        let maybe_current_user = if let Some(value) = req
            .headers()
            .get("Authorization")
            .and_then(|value| value.to_str().ok())
        {
            let token = value.to_string();
            let current_user = maybe_current_user_from_token(&token, self.database.clone()).await;
            current_user
        } else {
            MaybeCurrentUser(None)
        };
        req.extensions_mut()
            .insert::<MaybeCurrentUser>(maybe_current_user);

        self.ep.call(req).await
    }
}

async fn maybe_current_user_from_token(token: &str, database: Database) -> MaybeCurrentUser {
    let token = token.trim_start_matches("Bearer ");
    let client = reqwest::Client::new();
    let user_info: UserInfo = client
        .get("https://idkthings-dev.au.auth0.com/userinfo")
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .unwrap()
        .json()
        .await
        .unwrap();

    let user: Option<User> = database
        .collection("users")
        .find_one(doc! { "email": &user_info.email }, None)
        .await
        .unwrap();

    MaybeCurrentUser(user.map(|user| user.into()))
}

#[derive(Debug, Serialize, Deserialize)]
struct UserInfo {
    pub email: String,
}

#[entity]
pub struct User {
    pub id: Uuid,
    pub email: String,
}