1mod api;
2mod auth;
3mod db;
4mod env;
5mod rpc;
6
7use ::rpc::Peer;
8use axum::{body::Body, http::StatusCode, response::IntoResponse, Router};
9use db::{Db, PostgresDb};
10
11use serde::Deserialize;
12use std::{net::TcpListener, sync::Arc};
13
14// type Request = tide::Request<Arc<AppState>>;
15
16#[derive(Default, Deserialize)]
17pub struct Config {
18 pub http_port: u16,
19 pub database_url: String,
20 pub api_token: String,
21}
22
23pub struct AppState {
24 db: Arc<dyn Db>,
25 config: Config,
26}
27
28impl AppState {
29 async fn new(config: Config) -> Result<Arc<Self>> {
30 let db = PostgresDb::new(&config.database_url, 5).await?;
31
32 let this = Self {
33 db: Arc::new(db),
34 config,
35 };
36 Ok(Arc::new(this))
37 }
38}
39
40// trait RequestExt {
41// fn db(&self) -> &Arc<dyn Db>;
42// }
43
44// impl RequestExt for Request<Body> {
45// fn db(&self) -> &Arc<dyn Db> {
46// &self.data::<Arc<AppState>>().unwrap().db
47// }
48// }
49
50#[tokio::main]
51async fn main() -> Result<()> {
52 if std::env::var("LOG_JSON").is_ok() {
53 json_env_logger::init();
54 } else {
55 env_logger::init();
56 }
57
58 if let Err(error) = env::load_dotenv() {
59 log::error!(
60 "error loading .env.toml (this is expected in production): {}",
61 error
62 );
63 }
64
65 let config = envy::from_env::<Config>().expect("error loading config");
66 let state = AppState::new(config).await?;
67 let rpc = Peer::new();
68 run_server(
69 state.clone(),
70 rpc,
71 TcpListener::bind(&format!("0.0.0.0:{}", state.config.http_port))
72 .expect("failed to bind TCP listener"),
73 )
74 .await?;
75 Ok(())
76}
77
78pub async fn run_server(
79 state: Arc<AppState>,
80 peer: Arc<Peer>,
81 listener: TcpListener,
82) -> Result<()> {
83 let app = Router::<Body>::new();
84 // TODO: Compression on API routes?
85 // TODO: Authenticate API routes.
86
87 let app = api::add_routes(app, state);
88 // TODO: Add rpc routes
89
90 axum::Server::from_tcp(listener)?
91 .serve(app.into_make_service())
92 .await?;
93
94 Ok(())
95}
96
97type Result<T> = std::result::Result<T, Error>;
98
99struct Error(anyhow::Error);
100
101impl<E> From<E> for Error
102where
103 E: Into<anyhow::Error>,
104{
105 fn from(error: E) -> Self {
106 Self(error.into())
107 }
108}
109
110impl IntoResponse for Error {
111 fn into_response(self) -> axum::response::Response {
112 (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", &self.0)).into_response()
113 }
114}
115
116impl std::fmt::Debug for Error {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 self.0.fmt(f)
119 }
120}
121
122impl std::fmt::Display for Error {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 self.0.fmt(f)
125 }
126}