admin.rs

  1use crate::{auth::RequestExt as _, db, AppState, LayoutData, Request, RequestExt as _};
  2use async_trait::async_trait;
  3use serde::{Deserialize, Serialize};
  4use std::sync::Arc;
  5use surf::http::mime;
  6
  7#[async_trait]
  8pub trait RequestExt {
  9    async fn require_admin(&self) -> tide::Result<()>;
 10}
 11
 12#[async_trait]
 13impl RequestExt for Request {
 14    async fn require_admin(&self) -> tide::Result<()> {
 15        let current_user = self
 16            .current_user()
 17            .await?
 18            .ok_or_else(|| tide::Error::from_str(401, "not logged in"))?;
 19
 20        if current_user.is_admin {
 21            Ok(())
 22        } else {
 23            Err(tide::Error::from_str(
 24                403,
 25                "authenticated user is not an admin",
 26            ))
 27        }
 28    }
 29}
 30
 31pub fn add_routes(app: &mut tide::Server<Arc<AppState>>) {
 32    app.at("/admin").get(get_admin_page);
 33    app.at("/admin/users").post(post_user);
 34    app.at("/admin/users/:id").put(put_user);
 35    app.at("/admin/users/:id/delete").post(delete_user);
 36    app.at("/admin/signups/:id/delete").post(delete_signup);
 37}
 38
 39#[derive(Serialize)]
 40struct AdminData {
 41    #[serde(flatten)]
 42    layout: Arc<LayoutData>,
 43    users: Vec<db::User>,
 44    signups: Vec<db::Signup>,
 45}
 46
 47async fn get_admin_page(mut request: Request) -> tide::Result {
 48    request.require_admin().await?;
 49
 50    let data = AdminData {
 51        layout: request.layout_data().await?,
 52        users: request.db().get_all_users().await?,
 53        signups: request.db().get_all_signups().await?,
 54    };
 55
 56    Ok(tide::Response::builder(200)
 57        .body(request.state().render_template("admin.hbs", &data)?)
 58        .content_type(mime::HTML)
 59        .build())
 60}
 61
 62async fn post_user(mut request: Request) -> tide::Result {
 63    request.require_admin().await?;
 64
 65    #[derive(Deserialize)]
 66    struct Form {
 67        github_login: String,
 68        #[serde(default)]
 69        admin: bool,
 70    }
 71
 72    let form = request.body_form::<Form>().await?;
 73    let github_login = form
 74        .github_login
 75        .strip_prefix("@")
 76        .unwrap_or(&form.github_login);
 77
 78    if !github_login.is_empty() {
 79        request.db().create_user(github_login, form.admin).await?;
 80    }
 81
 82    Ok(tide::Redirect::new("/admin").into())
 83}
 84
 85async fn put_user(mut request: Request) -> tide::Result {
 86    request.require_admin().await?;
 87
 88    let user_id = request.param("id")?.parse()?;
 89
 90    #[derive(Deserialize)]
 91    struct Body {
 92        admin: bool,
 93    }
 94
 95    let body: Body = request.body_json().await?;
 96
 97    request
 98        .db()
 99        .set_user_is_admin(db::UserId(user_id), body.admin)
100        .await?;
101
102    Ok(tide::Response::builder(200).build())
103}
104
105async fn delete_user(request: Request) -> tide::Result {
106    request.require_admin().await?;
107    let user_id = db::UserId(request.param("id")?.parse()?);
108    request.db().destroy_user(user_id).await?;
109    Ok(tide::Redirect::new("/admin").into())
110}
111
112async fn delete_signup(request: Request) -> tide::Result {
113    request.require_admin().await?;
114    let signup_id = db::SignupId(request.param("id")?.parse()?);
115    request.db().destroy_signup(signup_id).await?;
116    Ok(tide::Redirect::new("/admin").into())
117}