starfish_api/
lib.rs

1#![allow(missing_docs)]
2
3pub mod db;
4mod handler;
5
6use async_trait::async_trait;
7use db::pool;
8use rocket::fairing::{self, AdHoc, Fairing, Info, Kind};
9use rocket::http::Header;
10use rocket::serde::json::{json, Value};
11use rocket::tokio::runtime;
12use rocket::{catch, Build, Request, Responder, Response, Rocket};
13use sea_orm::DbErr;
14use sea_orm_rocket::Database;
15use starfish_core::{
16    migrator::{Migrator, MigratorTrait},
17    sea_orm,
18};
19
20async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
21    let conn = &pool::Db::fetch(&rocket).unwrap().conn;
22    Migrator::up(conn, None).await.unwrap();
23    Ok(rocket)
24}
25
26pub fn rocket() -> Rocket<Build> {
27    use figment::{
28        providers::{Env, Format, Toml},
29        Figment,
30    };
31
32    let figment = Figment::new()
33        .merge(rocket::Config::default())
34        .merge(Toml::file("Rocket.toml").nested())
35        .merge(Env::prefixed("ROCKET_APP_").split("_"));
36
37    rocket::custom(figment)
38        .attach(pool::Db::init())
39        .attach(AdHoc::try_on_ignite("Migrations", run_migrations))
40        .attach(Cors)
41        .mount("/", handler::core::routes())
42}
43
44#[catch(404)]
45#[allow(dead_code)]
46fn not_found() -> Value {
47    json!({
48        "status": "error",
49        "reason": "Resource was not found."
50    })
51}
52
53#[derive(Responder)]
54#[response(status = 500, content_type = "json")]
55struct ErrorResponder {
56    message: String,
57}
58
59#[allow(clippy::from_over_into)]
60impl Into<ErrorResponder> for DbErr {
61    fn into(self) -> ErrorResponder {
62        ErrorResponder {
63            message: self.to_string(),
64        }
65    }
66}
67
68#[allow(clippy::from_over_into)]
69impl Into<ErrorResponder> for &str {
70    fn into(self) -> ErrorResponder {
71        ErrorResponder {
72            message: self.to_string(),
73        }
74    }
75}
76
77fn check_auth_match(auth: Option<String>) -> Result<(), ErrorResponder> {
78    let err = Err("Authorization failed.".into());
79    match (auth, std::env::var("API_AUTH_KEY").ok()) {
80        (Some(auth), Some(expected)) => {
81            if !auth.eq(&expected) {
82                err
83            } else {
84                Ok(())
85            }
86        }
87        (None, Some(_)) => err,
88        (_, None) => Ok(()),
89    }
90}
91
92#[derive(Debug)]
93pub struct Cors;
94
95#[async_trait]
96impl Fairing for Cors {
97    fn info(&self) -> Info {
98        Info {
99            name: "Cross-Origin-Resource-Sharing Middleware",
100            kind: Kind::Response,
101        }
102    }
103
104    async fn on_response<'r>(&self, _: &'r Request<'_>, response: &mut Response<'r>) {
105        response.set_header(Header::new("access-control-allow-origin", "*"));
106        response.set_header(Header::new(
107            "access-control-allow-methods",
108            "GET, POST, PATCH, OPTIONS",
109        ));
110    }
111}
112
113pub fn main() {
114    runtime::Builder::new_multi_thread()
115        .enable_all()
116        .build()
117        .unwrap()
118        .block_on(rocket().launch())
119        .unwrap();
120}