1mod api;
2mod auth;
3mod db;
4mod env;
5mod errors;
6mod rpc;
7
8use ::rpc::Peer;
9use async_std::net::TcpListener;
10use async_trait::async_trait;
11use db::{Db, PostgresDb};
12use serde::Deserialize;
13use std::sync::Arc;
14use tide_compress::CompressMiddleware;
15
16type Request = tide::Request<Arc<AppState>>;
17
18#[derive(Default, Deserialize)]
19pub struct Config {
20 pub http_port: u16,
21 pub database_url: String,
22 pub api_token: String,
23}
24
25pub struct AppState {
26 db: Arc<dyn Db>,
27 config: Config,
28}
29
30impl AppState {
31 async fn new(config: Config) -> tide::Result<Arc<Self>> {
32 let db = PostgresDb::new(&config.database_url, 5).await?;
33
34 let this = Self {
35 db: Arc::new(db),
36 config,
37 };
38 Ok(Arc::new(this))
39 }
40}
41
42#[async_trait]
43trait RequestExt {
44 fn db(&self) -> &Arc<dyn Db>;
45}
46
47#[async_trait]
48impl RequestExt for Request {
49 fn db(&self) -> &Arc<dyn Db> {
50 &self.state().db
51 }
52}
53
54#[async_std::main]
55async fn main() -> tide::Result<()> {
56 if std::env::var("LOG_JSON").is_ok() {
57 json_env_logger::init();
58 } else {
59 tide::log::start();
60 }
61
62 if let Err(error) = env::load_dotenv() {
63 log::error!(
64 "error loading .env.toml (this is expected in production): {}",
65 error
66 );
67 }
68
69 let config = envy::from_env::<Config>().expect("error loading config");
70 let state = AppState::new(config).await?;
71 let rpc = Peer::new();
72 run_server(
73 state.clone(),
74 rpc,
75 TcpListener::bind(&format!("0.0.0.0:{}", state.config.http_port)).await?,
76 )
77 .await?;
78 Ok(())
79}
80
81pub async fn run_server(
82 state: Arc<AppState>,
83 rpc: Arc<Peer>,
84 listener: TcpListener,
85) -> tide::Result<()> {
86 let mut app = tide::with_state(state.clone());
87 rpc::add_routes(&mut app, &rpc);
88
89 let mut web = tide::with_state(state.clone());
90 web.with(CompressMiddleware::new());
91 api::add_routes(&mut web);
92
93 app.at("/").nest(web);
94
95 app.listen(listener).await?;
96
97 Ok(())
98}