2020-06-17 16:57:28 +02:00
use crate ::search ::TokenizerKind as SearchTokenizer ;
2019-03-21 10:30:33 +01:00
use rocket ::config ::Limits ;
2019-03-22 19:51:36 +01:00
use rocket ::Config as RocketConfig ;
2021-01-11 21:27:52 +01:00
use std ::collections ::HashSet ;
2019-03-22 19:51:36 +01:00
use std ::env ::{ self , var } ;
2019-03-21 10:30:33 +01:00
#[ cfg(not(test)) ]
2019-03-22 19:51:36 +01:00
const DB_NAME : & str = " plume " ;
2019-03-21 10:30:33 +01:00
#[ cfg(test) ]
const DB_NAME : & str = " plume_tests " ;
pub struct Config {
pub base_url : String ,
pub database_url : String ,
2019-03-21 11:51:41 +01:00
pub db_name : & 'static str ,
2020-05-06 19:27:59 +02:00
pub db_max_size : Option < u32 > ,
pub db_min_idle : Option < u32 > ,
2019-03-21 10:30:33 +01:00
pub search_index : String ,
2020-06-17 16:57:28 +02:00
pub search_tokenizers : SearchTokenizerConfig ,
2019-03-21 10:30:33 +01:00
pub rocket : Result < RocketConfig , RocketError > ,
2019-03-21 11:51:41 +01:00
pub logo : LogoConfig ,
2019-08-21 00:42:04 +02:00
pub default_theme : String ,
2019-10-28 22:28:28 +01:00
pub media_directory : String ,
2020-10-04 12:18:22 +02:00
pub ldap : Option < LdapConfig > ,
2021-01-11 21:27:52 +01:00
pub proxy : Option < ProxyConfig > ,
}
impl Config {
pub fn proxy ( & self ) -> Option < & reqwest ::Proxy > {
self . proxy . as_ref ( ) . map ( | p | & p . proxy )
}
2019-03-21 10:30:33 +01:00
}
2019-03-22 19:51:36 +01:00
#[ derive(Debug, Clone) ]
2019-03-21 10:30:33 +01:00
pub enum RocketError {
InvalidEnv ,
InvalidAddress ,
InvalidSecretKey ,
}
fn get_rocket_config ( ) -> Result < RocketConfig , RocketError > {
let mut c = RocketConfig ::active ( ) . map_err ( | _ | RocketError ::InvalidEnv ) ? ;
let address = var ( " ROCKET_ADDRESS " ) . unwrap_or_else ( | _ | " localhost " . to_owned ( ) ) ;
2019-03-22 19:51:36 +01:00
let port = var ( " ROCKET_PORT " )
. ok ( )
. map ( | s | s . parse ::< u16 > ( ) . unwrap ( ) )
. unwrap_or ( 7878 ) ;
2019-03-21 10:30:33 +01:00
let secret_key = var ( " ROCKET_SECRET_KEY " ) . map_err ( | _ | RocketError ::InvalidSecretKey ) ? ;
2019-03-22 19:51:36 +01:00
let form_size = var ( " FORM_SIZE " )
2019-04-19 13:00:39 +02:00
. unwrap_or_else ( | _ | " 128 " . to_owned ( ) )
2019-03-22 19:51:36 +01:00
. parse ::< u64 > ( )
. unwrap ( ) ;
let activity_size = var ( " ACTIVITY_SIZE " )
. unwrap_or_else ( | _ | " 1024 " . to_owned ( ) )
. parse ::< u64 > ( )
. unwrap ( ) ;
c . set_address ( address )
. map_err ( | _ | RocketError ::InvalidAddress ) ? ;
2019-03-21 10:30:33 +01:00
c . set_port ( port ) ;
2019-03-22 19:51:36 +01:00
c . set_secret_key ( secret_key )
. map_err ( | _ | RocketError ::InvalidSecretKey ) ? ;
2019-03-21 10:30:33 +01:00
2019-03-22 19:51:36 +01:00
c . set_limits (
Limits ::new ( )
. limit ( " forms " , form_size * 1024 )
. limit ( " json " , activity_size * 1024 ) ,
) ;
2019-03-21 10:30:33 +01:00
Ok ( c )
}
2019-03-21 11:51:41 +01:00
pub struct LogoConfig {
pub main : String ,
pub favicon : String ,
2019-03-26 12:45:17 +01:00
pub other : Vec < Icon > , //url, size, type
2019-03-21 11:51:41 +01:00
}
#[ derive(Serialize) ]
pub struct Icon {
pub src : String ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub sizes : Option < String > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
#[ serde(rename = " type " ) ]
pub image_type : Option < String > ,
}
impl Icon {
pub fn with_prefix ( & self , prefix : & str ) -> Icon {
Icon {
src : format ! ( " {}/{} " , prefix , self . src ) ,
sizes : self . sizes . clone ( ) ,
image_type : self . image_type . clone ( ) ,
}
}
}
impl Default for LogoConfig {
fn default ( ) -> Self {
let to_icon = | ( src , sizes , image_type ) : & ( & str , Option < & str > , Option < & str > ) | Icon {
src : str ::to_owned ( src ) ,
sizes : sizes . map ( str ::to_owned ) ,
2019-03-26 12:45:17 +01:00
image_type : image_type . map ( str ::to_owned ) ,
2019-03-21 11:51:41 +01:00
} ;
let icons = [
(
" icons/trwnh/feather/plumeFeather48.png " ,
Some ( " 48x48 " ) ,
Some ( " image/png " ) ,
) ,
(
" icons/trwnh/feather/plumeFeather72.png " ,
Some ( " 72x72 " ) ,
Some ( " image/png " ) ,
) ,
(
" icons/trwnh/feather/plumeFeather96.png " ,
Some ( " 96x96 " ) ,
Some ( " image/png " ) ,
) ,
(
" icons/trwnh/feather/plumeFeather144.png " ,
Some ( " 144x144 " ) ,
Some ( " image/png " ) ,
) ,
(
" icons/trwnh/feather/plumeFeather160.png " ,
Some ( " 160x160 " ) ,
Some ( " image/png " ) ,
) ,
(
" icons/trwnh/feather/plumeFeather192.png " ,
Some ( " 192x192 " ) ,
Some ( " image/png " ) ,
) ,
(
" icons/trwnh/feather/plumeFeather256.png " ,
Some ( " 256x256 " ) ,
Some ( " image/png " ) ,
) ,
(
" icons/trwnh/feather/plumeFeather512.png " ,
Some ( " 512x512 " ) ,
Some ( " image/png " ) ,
) ,
2019-03-26 12:45:17 +01:00
( " icons/trwnh/feather/plumeFeather.svg " , None , None ) ,
]
. iter ( )
. map ( to_icon )
. collect ( ) ;
2019-03-21 11:51:41 +01:00
let custom_main = var ( " PLUME_LOGO " ) . ok ( ) ;
2019-03-26 12:45:17 +01:00
let custom_favicon = var ( " PLUME_LOGO_FAVICON " )
. ok ( )
. or_else ( | | custom_main . clone ( ) ) ;
2019-03-21 11:51:41 +01:00
let other = if let Some ( main ) = custom_main . clone ( ) {
let ext = | path : & str | match path . rsplitn ( 2 , '.' ) . next ( ) {
Some ( " png " ) = > Some ( " image/png " . to_owned ( ) ) ,
2019-03-26 12:45:17 +01:00
Some ( " jpg " ) | Some ( " jpeg " ) = > Some ( " image/jpeg " . to_owned ( ) ) ,
2019-03-21 11:51:41 +01:00
Some ( " svg " ) = > Some ( " image/svg+xml " . to_owned ( ) ) ,
Some ( " webp " ) = > Some ( " image/webp " . to_owned ( ) ) ,
_ = > None ,
} ;
let mut custom_icons = env ::vars ( )
2019-03-26 12:45:17 +01:00
. filter_map ( | ( var , val ) | {
if var . starts_with ( " PLUME_LOGO_ " ) {
Some ( ( var [ 11 .. ] . to_owned ( ) , val ) )
} else {
None
}
} )
. filter_map ( | ( var , val ) | var . parse ::< u64 > ( ) . ok ( ) . map ( | var | ( var , val ) ) )
. map ( | ( dim , src ) | Icon {
2019-03-21 11:51:41 +01:00
image_type : ext ( & src ) ,
src ,
sizes : Some ( format! ( " {} x {} " , dim , dim ) ) ,
} )
. collect ::< Vec < _ > > ( ) ;
custom_icons . push ( Icon {
image_type : ext ( & main ) ,
src : main ,
sizes : None ,
} ) ;
custom_icons
} else {
icons
} ;
LogoConfig {
2019-03-26 12:45:17 +01:00
main : custom_main
. unwrap_or_else ( | | " icons/trwnh/feather/plumeFeather256.png " . to_owned ( ) ) ,
favicon : custom_favicon . unwrap_or_else ( | | {
" icons/trwnh/feather-filled/plumeFeatherFilled64.png " . to_owned ( )
} ) ,
2019-03-21 11:51:41 +01:00
other ,
}
}
}
2020-06-17 16:57:28 +02:00
pub struct SearchTokenizerConfig {
pub tag_tokenizer : SearchTokenizer ,
pub content_tokenizer : SearchTokenizer ,
pub property_tokenizer : SearchTokenizer ,
}
impl SearchTokenizerConfig {
pub fn init ( ) -> Self {
use SearchTokenizer ::* ;
match var ( " SEARCH_LANG " ) . ok ( ) . as_deref ( ) {
Some ( " ja " ) = > {
#[ cfg(not(feature = " search-lindera " )) ]
panic! ( " You need build Plume with search-lindera feature, or execute it with SEARCH_TAG_TOKENIZER=ngram and SEARCH_CONTENT_TOKENIZER=ngram to enable Japanese search feature " ) ;
#[ cfg(feature = " search-lindera " ) ]
Self {
tag_tokenizer : Self ::determine_tokenizer ( " SEARCH_TAG_TOKENIZER " , Lindera ) ,
content_tokenizer : Self ::determine_tokenizer (
" SEARCH_CONTENT_TOKENIZER " ,
Lindera ,
) ,
property_tokenizer : Ngram ,
}
}
_ = > Self {
tag_tokenizer : Self ::determine_tokenizer ( " SEARCH_TAG_TOKENIZER " , Whitespace ) ,
content_tokenizer : Self ::determine_tokenizer ( " SEARCH_CONTENT_TOKENIZER " , Simple ) ,
property_tokenizer : Ngram ,
} ,
}
}
fn determine_tokenizer ( var_name : & str , default : SearchTokenizer ) -> SearchTokenizer {
use SearchTokenizer ::* ;
match var ( var_name ) . ok ( ) . as_deref ( ) {
Some ( " simple " ) = > Simple ,
Some ( " ngram " ) = > Ngram ,
Some ( " whitespace " ) = > Whitespace ,
Some ( " lindera " ) = > {
#[ cfg(not(feature = " search-lindera " )) ]
panic! ( " You need build Plume with search-lindera feature to use Lindera tokenizer " ) ;
#[ cfg(feature = " search-lindera " ) ]
Lindera
}
_ = > default ,
}
}
}
2020-10-04 12:18:22 +02:00
pub struct LdapConfig {
pub addr : String ,
pub base_dn : String ,
pub tls : bool ,
pub user_name_attr : String ,
pub mail_attr : String ,
}
fn get_ldap_config ( ) -> Option < LdapConfig > {
let addr = var ( " LDAP_ADDR " ) . ok ( ) ;
let base_dn = var ( " LDAP_BASE_DN " ) . ok ( ) ;
2020-10-08 20:24:03 +02:00
match ( addr , base_dn ) {
( Some ( addr ) , Some ( base_dn ) ) = > {
let tls = var ( " LDAP_TLS " ) . unwrap_or_else ( | _ | " false " . to_owned ( ) ) ;
let tls = match tls . as_ref ( ) {
" 1 " | " true " | " TRUE " = > true ,
" 0 " | " false " | " FALSE " = > false ,
_ = > panic! ( " Invalid LDAP configuration : tls " ) ,
} ;
let user_name_attr = var ( " LDAP_USER_NAME_ATTR " ) . unwrap_or_else ( | _ | " cn " . to_owned ( ) ) ;
let mail_attr = var ( " LDAP_USER_MAIL_ATTR " ) . unwrap_or_else ( | _ | " mail " . to_owned ( ) ) ;
Some ( LdapConfig {
addr ,
base_dn ,
tls ,
user_name_attr ,
mail_attr ,
} )
}
( None , None ) = > None ,
( _ , _ ) = > {
panic! ( " Invalid LDAP configuration : both LDAP_ADDR and LDAP_BASE_DN must be set " )
}
2020-10-04 12:18:22 +02:00
}
}
2021-01-11 21:27:52 +01:00
pub struct ProxyConfig {
pub url : reqwest ::Url ,
pub only_domains : Option < HashSet < String > > ,
pub proxy : reqwest ::Proxy ,
}
fn get_proxy_config ( ) -> Option < ProxyConfig > {
let url : reqwest ::Url = var ( " PROXY_URL " ) . ok ( ) ? . parse ( ) . expect ( " Invalid PROXY_URL " ) ;
let proxy_url = url . clone ( ) ;
let only_domains : Option < HashSet < String > > = var ( " PROXY_DOMAINS " )
. ok ( )
. map ( | ods | ods . split ( " , " ) . map ( str ::to_owned ) . collect ( ) ) ;
let proxy = if let Some ( ref only_domains ) = only_domains {
let only_domains = only_domains . clone ( ) ;
reqwest ::Proxy ::custom ( move | url | {
if let Some ( domain ) = url . domain ( ) {
if only_domains . contains ( domain )
| | only_domains
. iter ( )
. filter ( | target | domain . ends_with ( & format! ( " . {} " , target ) ) )
. next ( )
. is_some ( )
{
Some ( proxy_url . clone ( ) )
} else {
None
}
} else {
None
}
} )
} else {
reqwest ::Proxy ::all ( proxy_url )
. ok ( )
. expect ( " Invalid PROXY_URL " )
} ;
Some ( ProxyConfig {
url ,
only_domains ,
proxy ,
} )
}
2019-03-21 10:30:33 +01:00
lazy_static! {
pub static ref CONFIG : Config = Config {
base_url : var ( " BASE_URL " ) . unwrap_or_else ( | _ | format! (
2019-03-22 19:51:36 +01:00
" 127.0.0.1:{} " ,
2019-04-06 17:41:57 +02:00
var ( " ROCKET_PORT " ) . unwrap_or_else ( | _ | " 7878 " . to_owned ( ) )
2019-03-22 19:51:36 +01:00
) ) ,
2019-03-21 10:30:33 +01:00
db_name : DB_NAME ,
2020-05-06 19:27:59 +02:00
db_max_size : var ( " DB_MAX_SIZE " ) . map_or ( None , | s | Some (
s . parse ::< u32 > ( )
. expect ( " Couldn't parse DB_MAX_SIZE into u32 " )
) ) ,
db_min_idle : var ( " DB_MIN_IDLE " ) . map_or ( None , | s | Some (
s . parse ::< u32 > ( )
. expect ( " Couldn't parse DB_MIN_IDLE into u32 " )
) ) ,
2019-03-21 10:30:33 +01:00
#[ cfg(feature = " postgres " ) ]
2019-03-22 19:51:36 +01:00
database_url : var ( " DATABASE_URL " )
. unwrap_or_else ( | _ | format! ( " postgres://plume:plume@localhost/ {} " , DB_NAME ) ) ,
2019-03-21 10:30:33 +01:00
#[ cfg(feature = " sqlite " ) ]
2019-03-22 19:51:36 +01:00
database_url : var ( " DATABASE_URL " ) . unwrap_or_else ( | _ | format! ( " {} .sqlite " , DB_NAME ) ) ,
2019-03-21 10:30:33 +01:00
search_index : var ( " SEARCH_INDEX " ) . unwrap_or_else ( | _ | " search_index " . to_owned ( ) ) ,
2020-06-17 16:57:28 +02:00
search_tokenizers : SearchTokenizerConfig ::init ( ) ,
2019-03-21 11:51:41 +01:00
rocket : get_rocket_config ( ) ,
logo : LogoConfig ::default ( ) ,
2019-08-21 00:42:04 +02:00
default_theme : var ( " DEFAULT_THEME " ) . unwrap_or_else ( | _ | " default-light " . to_owned ( ) ) ,
2019-10-28 22:28:28 +01:00
media_directory : var ( " MEDIA_UPLOAD_DIRECTORY " )
. unwrap_or_else ( | _ | " static/media " . to_owned ( ) ) ,
2020-10-04 12:18:22 +02:00
ldap : get_ldap_config ( ) ,
2021-01-11 21:27:52 +01:00
proxy : get_proxy_config ( ) ,
2019-03-21 10:30:33 +01:00
} ;
}