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-10-02 06:03:18 +02:00
use std ::{ env , io ::Write , net , time } ;
use url ::Url ;
use crate ::{ err , redis_to_client_stream ::redis_cmd } ;
2019-07-06 02:08:50 +02:00
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 04:34:45 +02:00
// Postgres
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-09-05 03:31:52 +02:00
const DEFAULT_DB_SSLMODE : & str = " prefer " ;
2019-08-28 04:34:45 +02:00
// Redis
2019-10-02 06:03:18 +02:00
const DEFAULT_REDIS_HOST : & str = " 127.0.0.1 " ;
const DEFAULT_REDIS_PORT : & str = " 6379 " ;
const _DEFAULT_REDIS_NAMESPACE : & str = " " ;
// Deployment
2019-07-08 13:31:42 +02:00
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 ;
2019-09-05 03:31:52 +02:00
/// **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.
2019-07-08 21:21:02 +02:00
const DEFAULT_REDIS_POLL_INTERVAL : u64 = 100 ;
2019-09-05 03:31:52 +02:00
fn default ( var : & str , default_var : & str ) -> String {
2019-08-28 00:26:37 +02:00
env ::var ( var )
. unwrap_or_else ( | _ | {
warn! (
2019-10-02 06:03:18 +02:00
" No {} env variable set. Using default value: {} " ,
2019-08-28 00:26:37 +02:00
var , default_var
) ;
default_var . to_string ( )
} )
. to_string ( )
}
2019-07-08 21:21:02 +02:00
lazy_static! {
2019-10-02 06:03:18 +02:00
static ref POSTGRES_ADDR : String = match & env ::var ( " DATABASE_URL " ) {
2019-08-28 00:26:37 +02:00
Ok ( url ) = > {
2019-10-02 06:03:18 +02:00
warn! ( " DATABASE_URL env variable set. Connecting to Postgres with that URL and ignoring any values set in DB_HOST, DB_USER, DB_NAME, DB_PASS, or DB_PORT. " ) ;
2019-08-28 00:26:37 +02:00
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 " ) {
2019-09-05 03:31:52 +02:00
Err ( _ ) = > default ( " DB_USER " , DEFAULT_DB_USER ) ,
Ok ( user ) = > default ( " DB_USER " , user )
2019-08-28 00:26:37 +02:00
}
} ) ;
2019-09-05 03:31:52 +02:00
let host = & env ::var ( " DB_HOST " )
. unwrap_or_else ( | _ | default ( " DB_HOST " , DEFAULT_DB_HOST ) ) ;
let db_name = & env ::var ( " DB_NAME " )
. unwrap_or_else ( | _ | default ( " DB_NAME " , DEFAULT_DB_NAME ) ) ;
let port = & env ::var ( " DB_PORT " )
. unwrap_or_else ( | _ | default ( " DB_PORT " , DEFAULT_DB_PORT ) ) ;
let ssl_mode = & env ::var ( " DB_SSLMODE " )
. unwrap_or_else ( | _ | default ( " DB_SSLMODE " , DEFAULT_DB_SSLMODE ) ) ;
2019-07-10 04:20:11 +02:00
2019-08-28 00:26:37 +02:00
match & env ::var ( " DB_PASS " ) {
2019-09-05 03:31:52 +02:00
Ok ( password ) = > {
format! ( " postgres:// {} : {} @ {} : {} / {} ?sslmode= {} " ,
user , password , host , port , db_name , ssl_mode ) } ,
2019-08-28 00:26:37 +02:00
Err ( _ ) = > {
warn! ( " No DB_PASSWORD set. Attempting to connect to Postgres without a password. (This is correct if you are using the `ident` method.) " ) ;
2019-09-05 03:31:52 +02:00
format! ( " postgres:// {} @ {} : {} / {} ?sslmode= {} " ,
user , host , port , db_name , ssl_mode )
2019-08-28 00:26:37 +02:00
} ,
}
}
} ;
2019-10-02 06:03:18 +02:00
static ref REDIS_ADDR : RedisConfig = match & env ::var ( " REDIS_URL " ) {
Ok ( url ) = > {
warn! ( r " REDIS_URL env variable set.
Connecting to Redis with that URL and ignoring any values set in REDIS_HOST or DB_PORT . " );
let url = Url ::parse ( url ) . unwrap ( ) ;
fn none_if_empty ( item : & str ) -> Option < String > {
if item . is_empty ( ) { None } else { Some ( item . to_string ( ) ) }
} ;
let user = none_if_empty ( url . username ( ) ) ;
let mut password = url . password ( ) . as_ref ( ) . map ( | str | str . to_string ( ) ) ;
let host = err ::unwrap_or_die ( url . host_str ( ) , " Missing/invalid host in REDIS_URL " ) ;
let port = err ::unwrap_or_die ( url . port ( ) , " Missing/invalid port in REDIS_URL " ) ;
let mut db = none_if_empty ( url . path ( ) ) ;
let query_pairs = url . query_pairs ( ) ;
for ( key , value ) in query_pairs {
match key . to_string ( ) . as_str ( ) {
" password " = > { password = Some ( value . to_string ( ) ) ; } ,
" db " = > { db = Some ( value . to_string ( ) ) }
_ = > { err ::die_with_msg ( format! ( " Unsupported parameter {} in REDIS_URL. \n Flodgatt supports only `password` and `db` parameters. " , key ) ) }
}
}
RedisConfig {
user ,
password ,
host ,
port ,
db
}
}
Err ( _ ) = > {
let host = env ::var ( " REDIS_HOST " )
. unwrap_or_else ( | _ | default ( " REDIS_HOST " , DEFAULT_REDIS_HOST ) ) ;
let port = env ::var ( " REDIS_PORT " )
. unwrap_or_else ( | _ | default ( " REDIS_PORT " , DEFAULT_REDIS_PORT ) ) ;
RedisConfig {
user : None ,
password : None ,
host ,
port ,
db : None ,
}
}
} ;
pub static ref REDIS_NAMESPACE : Option < String > = match env ::var ( " REDIS_NAMESPACE " ) {
Ok ( ns ) = > {
log ::warn! ( " Using `{}:` as a Redis namespace. " , ns ) ;
Some ( ns )
} ,
_ = > None
} ;
2019-07-08 21:21:02 +02:00
pub static ref SERVER_ADDR : net ::SocketAddr = env ::var ( " SERVER_ADDR " )
. unwrap_or_else ( | _ | DEFAULT_SERVER_ADDR . to_owned ( ) )
. parse ( )
. expect ( " static string " ) ;
2019-09-05 03:31:52 +02:00
/// Interval, in ms, at which `ClientAgent` polls `Receiver` for updates to send via SSE.
2019-07-08 21:21:02 +02:00
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 ) ;
2019-09-05 03:31:52 +02:00
/// Interval, in ms, at which `ClientAgent` polls `Receiver` for updates to send via WS.
2019-07-08 21:21:02 +02:00
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.
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
2019-09-05 03:31:52 +02:00
pub fn postgres ( ) -> postgres ::Client {
2019-09-10 17:46:05 +02:00
// use openssl::ssl::{SslConnector, SslMethod};
// use postgres_openssl::MakeTlsConnector;
// let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
// builder.set_ca_file("/etc/ssl/cert.pem").unwrap();
// let connector = MakeTlsConnector::new(builder.build());
// TODO: add TLS support, remove `NoTls`
postgres ::Client ::connect ( & POSTGRES_ADDR . to_string ( ) , postgres ::NoTls )
2019-07-06 02:08:50 +02:00
. expect ( " Can connect to local Postgres " )
}
2019-10-02 06:03:18 +02:00
#[ derive(Default) ]
struct RedisConfig {
user : Option < String > ,
password : Option < String > ,
port : String ,
host : String ,
db : Option < String > ,
}
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-10-02 06:03:18 +02:00
let redis = & REDIS_ADDR ;
let addr = format! ( " {} : {} " , redis . host , redis . port ) ;
if let Some ( user ) = & redis . user {
log ::error! (
" Username {} provided, but Redis does not need a username. Ignoring it " ,
user
) ;
} ;
let mut pubsub_connection =
net ::TcpStream ::connect ( addr . clone ( ) ) . 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 " ) ;
2019-09-28 23:57:37 +02:00
pubsub_connection
. set_nonblocking ( true )
. expect ( " set_nonblocking call failed " ) ;
2019-10-02 06:03:18 +02:00
let mut secondary_redis_connection =
net ::TcpStream ::connect ( addr ) . 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 " ) ;
2019-10-02 06:03:18 +02:00
if let Some ( password ) = & REDIS_ADDR . password {
pubsub_connection
. write_all ( & redis_cmd ::cmd ( " auth " , & password ) )
. unwrap ( ) ;
secondary_redis_connection
. write_all ( & redis_cmd ::cmd ( " auth " , password ) )
. unwrap ( ) ;
} else {
warn! ( " No REDIS_PASSWORD set. Attempting to connect to Redis without a password. (This is correct if you are following the default setup.) " ) ;
2019-07-08 13:31:42 +02:00
}
2019-10-02 06:03:18 +02:00
if let Some ( db ) = & REDIS_ADDR . db {
pubsub_connection
. write_all ( & redis_cmd ::cmd ( " SELECT " , & db ) )
. unwrap ( ) ;
secondary_redis_connection
. write_all ( & redis_cmd ::cmd ( " SELECT " , & db ) )
. unwrap ( ) ;
2019-07-08 13:31:42 +02:00
}
2019-10-02 06:03:18 +02:00
( pubsub_connection , secondary_redis_connection )
2019-07-08 13:31:42 +02:00
}