2019-07-08 21:21:02 +02:00
//! Configuration defaults. All settings with the prefix of `DEFAULT_` can be overridden
//! by an environmental variable of the same name without that prefix (either by setting
//! the variable at runtime or in the `.env` file)
2019-07-06 02:08:50 +02:00
use dotenv ::dotenv ;
2019-07-08 21:21:02 +02:00
use lazy_static ::lazy_static ;
2019-07-06 02:08:50 +02:00
use log ::warn ;
2019-07-08 13:31:42 +02:00
use serde_derive ::Serialize ;
2019-07-06 02:08:50 +02:00
use std ::{ env , net , time } ;
2019-07-08 13:31:42 +02:00
const CORS_ALLOWED_METHODS : [ & str ; 2 ] = [ " GET " , " OPTIONS " ] ;
const CORS_ALLOWED_HEADERS : [ & str ; 3 ] = [ " Authorization " , " Accept " , " Cache-Control " ] ;
2019-08-28 00:26:37 +02:00
const DEFAULT_DB_HOST : & str = " localhost " ;
const DEFAULT_DB_USER : & str = " postgres " ;
const DEFAULT_DB_NAME : & str = " mastodon_development " ;
const DEFAULT_DB_PORT : & str = " 5432 " ;
2019-07-08 13:31:42 +02:00
const DEFAULT_REDIS_ADDR : & str = " 127.0.0.1:6379 " ;
const DEFAULT_SERVER_ADDR : & str = " 127.0.0.1:4000 " ;
2019-07-08 21:21:02 +02:00
const DEFAULT_SSE_UPDATE_INTERVAL : u64 = 100 ;
const DEFAULT_WS_UPDATE_INTERVAL : u64 = 100 ;
const DEFAULT_REDIS_POLL_INTERVAL : u64 = 100 ;
2019-08-28 00:26:37 +02:00
fn env_var_or_default ( var : & str , default_var : & str ) -> String {
env ::var ( var )
. unwrap_or_else ( | _ | {
warn! (
" No {} env variable set for Postgres. Using default value: {} " ,
var , default_var
) ;
default_var . to_string ( )
} )
. to_string ( )
}
2019-07-08 21:21:02 +02:00
lazy_static! {
2019-08-28 00:26:37 +02:00
static ref POSTGRES_ADDR : String = match & env ::var ( " POSTGRESS_ADDR " ) {
Ok ( url ) = > {
warn! ( " DATABASE_URL env variable set. Trying to connect to Postgres with that URL instead of any values set in DB_HOST, DB_USER, DB_NAME, DB_PASS, or DB_PORT. " ) ;
url . to_string ( )
2019-07-10 04:20:11 +02:00
}
2019-08-28 00:26:37 +02:00
Err ( _ ) = > {
let user = & env ::var ( " DB_USER " ) . unwrap_or_else ( | _ | {
match & env ::var ( " USER " ) {
Err ( _ ) = > env_var_or_default ( " DB_USER " , DEFAULT_DB_USER ) ,
Ok ( user ) = > env_var_or_default ( " DB_USER " , user )
}
} ) ;
let host = & env ::var ( " DB_HOST " ) . unwrap_or_else ( | _ | env_var_or_default ( " DB_HOST " , DEFAULT_DB_HOST ) ) ;
let db_name = & env ::var ( " DB_NAME " ) . unwrap_or_else ( | _ | env_var_or_default ( " DB_NAME " , DEFAULT_DB_NAME ) ) ;
let port = & env ::var ( " DB_PORT " ) . unwrap_or_else ( | _ | env_var_or_default ( " DB_PORT " , DEFAULT_DB_PORT ) ) ;
2019-07-10 04:20:11 +02:00
2019-08-28 00:26:37 +02:00
match & env ::var ( " DB_PASS " ) {
Ok ( password ) = > format! ( " postgres:// {user} : {password} @ {host} : {port} / {db_name} " ,
user = user , password = password , host = host , port = port , db_name = db_name ) ,
Err ( _ ) = > {
warn! ( " No DB_PASSWORD set. Attempting to connect to Postgres without a password. (This is correct if you are using the `ident` method.) " ) ;
format! ( " postgres:// {user} @ {host} : {port} / {db_name} " ,
user = user , host = host , port = port , db_name = db_name )
} ,
}
}
} ;
2019-07-08 21:21:02 +02:00
static ref REDIS_ADDR : String = env ::var ( " REDIS_ADDR " ) . unwrap_or_else ( | _ | DEFAULT_REDIS_ADDR . to_owned ( ) ) ;
pub static ref SERVER_ADDR : net ::SocketAddr = env ::var ( " SERVER_ADDR " )
. unwrap_or_else ( | _ | DEFAULT_SERVER_ADDR . to_owned ( ) )
. parse ( )
. expect ( " static string " ) ;
/// Interval, in ms, at which the `ClientAgent` polls the `Receiver` for updates to send via SSE.
pub static ref SSE_UPDATE_INTERVAL : u64 = env ::var ( " SSE_UPDATE_INTERVAL " )
. map ( | s | s . parse ( ) . expect ( " Valid config " ) )
. unwrap_or ( DEFAULT_SSE_UPDATE_INTERVAL ) ;
/// Interval, in ms, at which the `ClientAgent` polls the `Receiver` for updates to send via WS.
pub static ref WS_UPDATE_INTERVAL : u64 = env ::var ( " WS_UPDATE_INTERVAL " )
. map ( | s | s . parse ( ) . expect ( " Valid config " ) )
. unwrap_or ( DEFAULT_WS_UPDATE_INTERVAL ) ;
/// Interval, in ms, at which the `Receiver` polls Redis.
/// **NOTE**: Polling Redis is much more time consuming than polling the `Receiver`
/// (on the order of 10ms rather than 50μs). Thus, changing this setting
/// would be a good place to start for performance improvements at the cost
/// of delaying all updates.
pub static ref REDIS_POLL_INTERVAL : u64 = env ::var ( " REDIS_POLL_INTERVAL " )
. map ( | s | s . parse ( ) . expect ( " Valid config " ) )
. unwrap_or ( DEFAULT_REDIS_POLL_INTERVAL ) ;
}
2019-07-08 13:31:42 +02:00
2019-07-06 02:08:50 +02:00
/// Configure CORS for the API server
pub fn cross_origin_resource_sharing ( ) -> warp ::filters ::cors ::Cors {
warp ::cors ( )
. allow_any_origin ( )
2019-07-08 13:31:42 +02:00
. allow_methods ( CORS_ALLOWED_METHODS . to_vec ( ) )
. allow_headers ( CORS_ALLOWED_HEADERS . to_vec ( ) )
2019-07-06 02:08:50 +02:00
}
/// Initialize logging and read values from `src/.env`
pub fn logging_and_env ( ) {
dotenv ( ) . ok ( ) ;
2019-07-10 02:13:37 +02:00
pretty_env_logger ::init ( ) ;
2019-07-10 04:20:11 +02:00
POSTGRES_ADDR . to_string ( ) ;
2019-07-06 02:08:50 +02:00
}
/// Configure Postgres and return a connection
pub fn postgres ( ) -> postgres ::Connection {
2019-07-08 21:21:02 +02:00
postgres ::Connection ::connect ( POSTGRES_ADDR . to_string ( ) , postgres ::TlsMode ::None )
2019-07-06 02:08:50 +02:00
. expect ( " Can connect to local Postgres " )
}
2019-07-08 13:31:42 +02:00
/// Configure Redis
2019-07-06 02:08:50 +02:00
pub fn redis_addr ( ) -> ( net ::TcpStream , net ::TcpStream ) {
2019-07-08 21:21:02 +02:00
let pubsub_connection =
net ::TcpStream ::connect ( & REDIS_ADDR . to_string ( ) ) . expect ( " Can connect to Redis " ) ;
2019-07-06 02:08:50 +02:00
pubsub_connection
. set_read_timeout ( Some ( time ::Duration ::from_millis ( 10 ) ) )
. expect ( " Can set read timeout for Redis connection " ) ;
let secondary_redis_connection =
2019-07-08 21:21:02 +02:00
net ::TcpStream ::connect ( & REDIS_ADDR . to_string ( ) ) . expect ( " Can connect to Redis " ) ;
2019-07-06 02:08:50 +02:00
secondary_redis_connection
. set_read_timeout ( Some ( time ::Duration ::from_millis ( 10 ) ) )
. expect ( " Can set read timeout for Redis connection " ) ;
( pubsub_connection , secondary_redis_connection )
}
2019-07-08 13:31:42 +02:00
#[ derive(Serialize) ]
pub struct ErrorMessage {
error : String ,
}
impl ErrorMessage {
fn new ( msg : impl std ::fmt ::Display ) -> Self {
Self {
error : msg . to_string ( ) ,
}
}
}
/// Recover from Errors by sending appropriate Warp::Rejections
pub fn handle_errors (
rejection : warp ::reject ::Rejection ,
) -> Result < impl warp ::Reply , warp ::reject ::Rejection > {
let err_txt = match rejection . cause ( ) {
Some ( text ) if text . to_string ( ) = = " Missing request header 'authorization' " = > {
" Error: Missing access token " . to_string ( )
}
Some ( text ) = > text . to_string ( ) ,
None = > " Error: Nonexistant endpoint " . to_string ( ) ,
} ;
let json = warp ::reply ::json ( & ErrorMessage ::new ( err_txt ) ) ;
Ok ( warp ::reply ::with_status (
json ,
warp ::http ::StatusCode ::UNAUTHORIZED ,
) )
}
pub struct CustomError { }
impl CustomError {
pub fn unauthorized_list ( ) -> warp ::reject ::Rejection {
warp ::reject ::custom ( " Error: Access to list not authorized " )
}
}