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}