api.rs

 1use crate::{auth, AppState, Request, RequestExt as _};
 2use async_trait::async_trait;
 3use serde_json::json;
 4use std::sync::Arc;
 5
 6pub fn add_routes(app: &mut tide::Server<Arc<AppState>>) {
 7    app.at("/users/:github_login").get(get_user);
 8    app.at("/users/:github_login/access_tokens")
 9        .post(create_access_token);
10}
11
12async fn get_user(request: Request) -> tide::Result {
13    request.require_token().await?;
14
15    let user = request
16        .db()
17        .get_user_by_github_login(request.param("github_login")?)
18        .await?
19        .ok_or_else(|| surf::Error::from_str(404, "user not found"))?;
20
21    Ok(tide::Response::builder(200)
22        .body(tide::Body::from_json(&user)?)
23        .build())
24}
25
26async fn create_access_token(request: Request) -> tide::Result {
27    request.require_token().await?;
28
29    let user = request
30        .db()
31        .get_user_by_github_login(request.param("github_login")?)
32        .await?
33        .ok_or_else(|| surf::Error::from_str(404, "user not found"))?;
34    let token = auth::create_access_token(request.db(), user.id).await?;
35
36    Ok(tide::Response::builder(200)
37        .body(json!({"user_id": user.id, "access_token": token}))
38        .build())
39}
40
41#[async_trait]
42pub trait RequestExt {
43    async fn require_token(&self) -> tide::Result<()>;
44}
45
46#[async_trait]
47impl RequestExt for Request {
48    async fn require_token(&self) -> tide::Result<()> {
49        let token = self
50            .header("Authorization")
51            .and_then(|header| header.get(0))
52            .and_then(|header| header.as_str().strip_prefix("token "))
53            .ok_or_else(|| surf::Error::from_str(403, "invalid authorization header"))?;
54
55        if token == self.state().config.api_token {
56            Ok(())
57        } else {
58            Err(tide::Error::from_str(403, "invalid authorization token"))
59        }
60    }
61}