algohub-server 0.1.18

Extremely fast async online judge backend based on Rust
Documentation
use std::path::Path;

use rocket::{get, post, serde::json::Json, tokio::fs::remove_dir_all, State};
use surrealdb::{engine::remote::ws::Client, Surreal};

use crate::{
    models::{
        account::{Login, MergeProfile, Profile, Register},
        error::Error,
        response::{Empty, Response},
        OwnedCredentials, Record, Token,
    },
    utils::{account, session},
    Result,
};

#[post("/create", data = "<register>")]
pub async fn register(
    db: &State<Surreal<Client>>,
    register: Json<Register>,
) -> Result<OwnedCredentials> {
    match account::create(db, register.into_inner()).await? {
        Some(account) => {
            let token = match session::create(db, account.id.clone().unwrap()).await {
                Ok(session) => session.unwrap().token,
                Err(e) => return Err(Error::ServerError(Json(e.to_string().into()))),
            };
            let id = account.id.unwrap().id.to_string();
            Ok(Response {
                success: true,
                message: format!("Account with id {} created successfully", &id),
                data: Some(OwnedCredentials { id, token }),
            }
            .into())
        }
        None => Ok(Response {
            success: false,
            message: "Specified username or email already exists".to_string(),
            data: None,
        }
        .into()),
    }
}

#[post("/profile", data = "<profile>")]
pub async fn profile(
    db: &State<Surreal<Client>>,
    profile: Json<MergeProfile<'_>>,
) -> Result<Empty> {
    account::get_by_id::<Record>(db, profile.id)
        .await?
        .ok_or(Error::NotFound(Json("Account not found".into())))?;

    if session::verify(db, profile.id, profile.token).await {
        account::merge_profile(db, profile.id, profile.profile.clone()).await?;
        Ok(Response {
            success: true,
            message: "Profile updated successfully".into(),
            data: None,
        }
        .into())
    } else {
        Err(Error::Unauthorized(Json("Invalid credentials".into())))
    }
}

#[get("/profile/<id>")]
pub async fn get_profile(db: &State<Surreal<Client>>, id: &str) -> Result<Profile> {
    let profile = account::get_by_identity::<Profile>(db, id)
        .await?
        .ok_or(Error::NotFound(Json("Account not found".into())))?;

    Ok(Response {
        success: true,
        message: "Profile fetched successfully".into(),
        data: Some(profile),
    }
    .into())
}

#[post("/delete/<id>", data = "<auth>")]
pub async fn delete(db: &State<Surreal<Client>>, id: &str, auth: Json<Token<'_>>) -> Result<Empty> {
    if !session::verify(db, id, auth.token).await {
        return Err(Error::Unauthorized(Json(
            "Failed to grant access permission".into(),
        )));
    }

    account::delete(db, id).await?;

    remove_dir_all(Path::new("content/").join(id)).await?;

    Ok(Response {
        success: true,
        message: "Account deleted successfully".into(),
        data: None,
    }
    .into())
}

#[post("/login", data = "<login>")]
pub async fn login(
    db: &State<Surreal<Client>>,
    login: Json<Login<'_>>,
) -> Result<OwnedCredentials> {
    let session = session::authenticate(db, login.identity, login.password)
        .await?
        .ok_or(Error::Unauthorized(Json("Invalid credentials".into())))?;
    Ok(Response {
        success: true,
        message: "Login successful".into(),
        data: Some(OwnedCredentials {
            id: session.account_id.id.to_string(),
            token: session.token.clone(),
        }),
    }
    .into())
}

#[post("/verify", data = "<data>")]
pub async fn verify(db: &State<Surreal<Client>>, data: Json<OwnedCredentials>) -> Result<Empty> {
    if session::verify(db, &data.id, &data.token).await {
        Ok(Response {
            success: true,
            message: "Verified successfully".into(),
            data: None,
        }
        .into())
    } else {
        Err(Error::Unauthorized(Json("Invalid credentials".into())))
    }
}

pub fn routes() -> Vec<rocket::Route> {
    use rocket::routes;
    routes![register, profile, get_profile, delete, login, verify]
}