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}