2020-05-26 16:09:34 +03:00
import Config
2021-06-16 12:03:01 +03:00
import Plausible.ConfigHelpers
2022-12-05 18:59:16 +03:00
require Logger
2020-05-26 16:09:34 +03:00
2021-01-13 17:15:03 +03:00
if config_env ( ) in [ :dev , :test ] do
2021-06-16 12:16:11 +03:00
Envy . load ( [ " config/.env. #{ config_env ( ) } " ] )
2021-01-13 16:41:16 +03:00
end
2020-05-26 16:09:34 +03:00
2024-04-29 09:05:33 +03:00
if config_env ( ) == :ce_dev do
2023-11-20 14:52:20 +03:00
Envy . load ( [ " config/.env.dev " ] )
end
2024-04-29 09:05:33 +03:00
if config_env ( ) == :ce_test do
2023-11-20 14:52:20 +03:00
Envy . load ( [ " config/.env.test " ] )
end
2021-06-16 12:03:01 +03:00
config_dir = System . get_env ( " CONFIG_DIR " , " /run/secrets " )
2020-11-03 12:20:11 +03:00
2023-11-13 16:53:11 +03:00
log_format =
get_var_from_path_or_env ( config_dir , " LOG_FORMAT " , " standard " )
log_level =
config_dir
2023-12-04 13:12:59 +03:00
|> get_var_from_path_or_env ( " LOG_LEVEL " , " warning " )
2023-11-13 16:53:11 +03:00
|> String . to_existing_atom ( )
config :logger ,
level : log_level ,
backends : [ :console ]
config :logger , Sentry.LoggerBackend ,
capture_log_messages : true ,
level : :error
case String . downcase ( log_format ) do
" standard " ->
config :logger , :console ,
format : " $time $metadata[$level] $message \n " ,
metadata : [ :request_id ]
" json " ->
config :logger , :console ,
format : { ExJsonLogger , :format } ,
metadata : [
:request_id
]
end
2022-01-21 23:04:29 +03:00
# Listen IP supports IPv4 and IPv6 addresses.
2022-01-21 23:23:09 +03:00
listen_ip =
(
str = get_var_from_path_or_env ( config_dir , " LISTEN_IP " ) || " 127.0.0.1 "
case :inet . parse_address ( String . to_charlist ( str ) ) do
{ :ok , ip_addr } ->
ip_addr
{ :error , reason } ->
raise " Invalid LISTEN_IP ' #{ str } ' error: #{ inspect ( reason ) } "
end
)
2022-01-21 23:04:29 +03:00
2021-06-16 12:03:01 +03:00
# System.get_env does not accept a non string default
port = get_var_from_path_or_env ( config_dir , " PORT " ) || 8000
base_url = get_var_from_path_or_env ( config_dir , " BASE_URL " )
2020-05-26 16:09:34 +03:00
2021-01-15 11:12:00 +03:00
if ! base_url do
2024-05-10 15:00:51 +03:00
raise " BASE_URL configuration option is required. See https://github.com/plausible/community-edition/tree/v2.1.0?tab=readme-ov-file # quick-start "
2021-01-15 11:12:00 +03:00
end
base_url = URI . parse ( base_url )
if base_url . scheme not in [ " http " , " https " ] do
2021-09-09 11:17:24 +03:00
raise " BASE_URL must start with `http` or `https`. Currently configured as ` #{ System . get_env ( " BASE_URL " ) } ` "
2021-01-15 11:12:00 +03:00
end
2021-06-16 12:03:01 +03:00
secret_key_base = get_var_from_path_or_env ( config_dir , " SECRET_KEY_BASE " , nil )
2021-04-13 11:19:24 +03:00
2021-06-16 12:03:01 +03:00
case secret_key_base do
nil ->
2024-05-10 15:00:51 +03:00
raise " SECRET_KEY_BASE configuration option is required. See https://github.com/plausible/community-edition/tree/v2.1.0?tab=readme-ov-file # quick-start "
2021-04-13 11:19:24 +03:00
2021-11-04 15:16:37 +03:00
key when byte_size ( key ) < 32 ->
2024-05-10 15:00:51 +03:00
raise " SECRET_KEY_BASE must be at least 32 bytes long. See https://github.com/plausible/community-edition/tree/v2.1.0?tab=readme-ov-file # quick-start "
2021-06-16 12:03:01 +03:00
_ ->
nil
end
2020-05-26 16:09:34 +03:00
2022-02-23 22:48:33 +03:00
super_admin_user_ids =
2021-06-16 15:33:37 +03:00
get_var_from_path_or_env ( config_dir , " ADMIN_USER_IDS " , " " )
2021-06-16 12:03:01 +03:00
|> String . split ( " , " )
2021-06-16 15:43:26 +03:00
|> Enum . map ( fn id -> Integer . parse ( id ) end )
|> Enum . map ( fn
{ int , " " } -> int
_ -> nil
end )
|> Enum . filter ( & &1 )
2021-06-16 12:03:01 +03:00
env = get_var_from_path_or_env ( config_dir , " ENVIRONMENT " , " prod " )
mailer_adapter = get_var_from_path_or_env ( config_dir , " MAILER_ADAPTER " , " Bamboo.SMTPAdapter " )
mailer_email = get_var_from_path_or_env ( config_dir , " MAILER_EMAIL " , " hello@plausible.local " )
2023-05-25 10:34:39 +03:00
mailer_email =
if mailer_name = get_var_from_path_or_env ( config_dir , " MAILER_NAME " ) do
{ mailer_name , mailer_email }
else
mailer_email
end
2021-06-16 12:03:01 +03:00
app_version = get_var_from_path_or_env ( config_dir , " APP_VERSION " , " 0.0.1 " )
2020-11-03 12:20:11 +03:00
ch_db_url =
2021-06-16 12:03:01 +03:00
get_var_from_path_or_env (
config_dir ,
" CLICKHOUSE_DATABASE_URL " ,
" http://plausible_events_db:8123/plausible_events_db "
)
2020-11-03 12:20:11 +03:00
2023-02-12 19:50:57 +03:00
{ ingest_pool_size , " " } =
get_var_from_path_or_env (
config_dir ,
" CLICKHOUSE_INGEST_POOL_SIZE " ,
" 5 "
)
|> Integer . parse ( )
2021-06-16 12:03:01 +03:00
{ ch_flush_interval_ms , " " } =
config_dir
|> get_var_from_path_or_env ( " CLICKHOUSE_FLUSH_INTERVAL_MS " , " 5000 " )
|> Integer . parse ( )
2023-12-14 16:21:27 +03:00
if get_var_from_path_or_env ( config_dir , " CLICKHOUSE_MAX_BUFFER_SIZE " ) do
Logger . warning (
" CLICKHOUSE_MAX_BUFFER_SIZE is deprecated, please use CLICKHOUSE_MAX_BUFFER_SIZE_BYTES instead "
)
end
2021-06-16 12:03:01 +03:00
{ ch_max_buffer_size , " " } =
config_dir
2023-12-14 16:21:27 +03:00
|> get_var_from_path_or_env ( " CLICKHOUSE_MAX_BUFFER_SIZE_BYTES " , " 100000 " )
2021-06-16 12:03:01 +03:00
|> Integer . parse ( )
2021-05-25 11:37:52 +03:00
2023-11-20 16:04:48 +03:00
# Can be generated with `Base.encode64(:crypto.strong_rand_bytes(32))` from
# iex shell or `openssl rand -base64 32` from command line.
2024-07-15 13:02:31 +03:00
totp_vault_key =
if totp_vault_key_base64 = get_var_from_path_or_env ( config_dir , " TOTP_VAULT_KEY " ) do
case Base . decode64 ( totp_vault_key_base64 ) do
{ :ok , totp_vault_key } ->
if byte_size ( totp_vault_key ) == 32 do
totp_vault_key
else
raise ArgumentError , """
TOTP_VAULT_KEY must be Base64 encoded 32 bytes , e . g . ` openssl rand - base64 32 ` .
Got Base64 encoded #{byte_size(totp_vault_key)} bytes.
More info : https :/ / github . com / plausible / community - edition / tree / v2 . 1.1 #quick-start
"""
end
:error ->
raise ArgumentError , """
TOTP_VAULT_KEY must be Base64 encoded 32 bytes , e . g . ` openssl rand - base64 32 `
More info : https :/ / github . com / plausible / community - edition / tree / v2 . 1.1 #quick-start
"""
2023-11-20 16:04:48 +03:00
end
2024-07-15 13:02:31 +03:00
else
Plug.Crypto.KeyGenerator . generate ( secret_key_base , " totp " , length : 32 , iterations : 100_000 )
end
2023-11-20 16:04:48 +03:00
2024-07-15 13:02:31 +03:00
config :plausible , Plausible.Auth.TOTP , vault_key : totp_vault_key
2020-05-26 16:09:34 +03:00
2022-12-05 18:59:16 +03:00
build_metadata_raw = get_var_from_path_or_env ( config_dir , " BUILD_METADATA " , " {} " )
2022-10-18 18:11:30 +03:00
build_metadata =
2022-12-05 18:59:16 +03:00
case Jason . decode ( build_metadata_raw ) do
{ :ok , build_metadata } ->
build_metadata
{ :error , error } ->
error = Exception . format ( :error , error )
2023-10-24 11:33:48 +03:00
Logger . warning ( """
2022-12-05 18:59:16 +03:00
failed to parse $ BUILD_METADATA : #{error}
$ BUILD_METADATA is set to #{build_metadata_raw}\
""" )
2023-10-24 11:33:48 +03:00
Logger . warning ( " falling back to empty build metadata, as if $BUILD_METADATA was set to {} " )
2022-12-05 18:59:16 +03:00
_fallback = %{ }
end
2022-10-18 18:11:30 +03:00
runtime_metadata = [
version : get_in ( build_metadata , [ " labels " , " org.opencontainers.image.version " ] ) ,
commit : get_in ( build_metadata , [ " labels " , " org.opencontainers.image.revision " ] ) ,
created : get_in ( build_metadata , [ " labels " , " org.opencontainers.image.created " ] ) ,
2023-02-03 14:51:32 +03:00
tags : get_in ( build_metadata , [ " tags " ] )
2022-10-18 18:11:30 +03:00
]
config :plausible , :runtime_metadata , runtime_metadata
2021-06-16 12:03:01 +03:00
sentry_dsn = get_var_from_path_or_env ( config_dir , " SENTRY_DSN " )
2021-11-05 15:58:57 +03:00
honeycomb_api_key = get_var_from_path_or_env ( config_dir , " HONEYCOMB_API_KEY " )
honeycomb_dataset = get_var_from_path_or_env ( config_dir , " HONEYCOMB_DATASET " )
2021-06-16 12:03:01 +03:00
paddle_auth_code = get_var_from_path_or_env ( config_dir , " PADDLE_VENDOR_AUTH_CODE " )
2021-12-09 16:49:57 +03:00
paddle_vendor_id = get_var_from_path_or_env ( config_dir , " PADDLE_VENDOR_ID " )
2021-06-16 12:03:01 +03:00
google_cid = get_var_from_path_or_env ( config_dir , " GOOGLE_CLIENT_ID " )
google_secret = get_var_from_path_or_env ( config_dir , " GOOGLE_CLIENT_SECRET " )
postmark_api_key = get_var_from_path_or_env ( config_dir , " POSTMARK_API_KEY " )
2024-07-12 13:01:59 +03:00
help_scout_app_id = get_var_from_path_or_env ( config_dir , " HELP_SCOUT_APP_ID " )
help_scout_app_secret = get_var_from_path_or_env ( config_dir , " HELP_SCOUT_APP_SECRET " )
help_scout_signature_key = get_var_from_path_or_env ( config_dir , " HELP_SCOUT_SIGNATURE_KEY " )
help_scout_vault_key = get_var_from_path_or_env ( config_dir , " HELP_SCOUT_VAULT_KEY " )
2021-06-16 12:03:01 +03:00
2023-11-13 16:57:51 +03:00
{ otel_sampler_ratio , " " } =
config_dir
|> get_var_from_path_or_env ( " OTEL_SAMPLER_RATIO " , " 0.5 " )
|> Float . parse ( )
2021-06-16 12:03:01 +03:00
cron_enabled =
config_dir
|> get_var_from_path_or_env ( " CRON_ENABLED " , " false " )
|> String . to_existing_atom ( )
2021-04-13 12:36:47 +03:00
geolite2_country_db =
2021-06-16 12:03:01 +03:00
get_var_from_path_or_env (
config_dir ,
2021-04-13 12:36:47 +03:00
" GEOLITE2_COUNTRY_DB " ,
2023-01-17 18:05:09 +03:00
Application . app_dir ( :plausible , " /priv/geodb/dbip-country.mmdb.gz " )
2021-04-13 12:36:47 +03:00
)
2021-11-23 12:39:09 +03:00
ip_geolocation_db = get_var_from_path_or_env ( config_dir , " IP_GEOLOCATION_DB " , geolite2_country_db )
2021-12-13 13:03:27 +03:00
geonames_source_file = get_var_from_path_or_env ( config_dir , " GEONAMES_SOURCE_FILE " )
2023-01-17 18:05:09 +03:00
maxmind_license_key = get_var_from_path_or_env ( config_dir , " MAXMIND_LICENSE_KEY " )
maxmind_edition = get_var_from_path_or_env ( config_dir , " MAXMIND_EDITION " , " GeoLite2-City " )
2024-05-13 11:17:56 +03:00
data_dir = get_var_from_path_or_env ( config_dir , " DATA_DIR " )
2024-03-08 17:33:55 +03:00
persistent_cache_dir = get_var_from_path_or_env ( config_dir , " PERSISTENT_CACHE_DIR " )
2021-11-23 12:39:09 +03:00
2024-05-13 11:17:56 +03:00
data_dir = data_dir || persistent_cache_dir
persistent_cache_dir = persistent_cache_dir || data_dir
2021-10-18 13:01:54 +03:00
enable_email_verification =
config_dir
|> get_var_from_path_or_env ( " ENABLE_EMAIL_VERIFICATION " , " false " )
|> String . to_existing_atom ( )
2023-06-14 12:08:52 +03:00
is_selfhost =
config_dir
|> get_var_from_path_or_env ( " SELFHOST " , " true " )
|> String . to_existing_atom ( )
# by default, registration is disabled in self-hosted setups
disable_registration_default = to_string ( is_selfhost )
2021-06-16 12:03:01 +03:00
disable_registration =
config_dir
2023-06-14 12:08:52 +03:00
|> get_var_from_path_or_env ( " DISABLE_REGISTRATION " , disable_registration_default )
2021-06-16 12:03:01 +03:00
|> String . to_existing_atom ( )
2022-05-03 10:44:17 +03:00
if disable_registration not in [ true , false , :invite_only ] do
2024-05-10 15:00:51 +03:00
raise " DISABLE_REGISTRATION must be one of `true`, `false`, or `invite_only`. See https://github.com/plausible/community-edition/tree/v2.1.0?tab=readme-ov-file # disable_registration "
2022-05-03 10:44:17 +03:00
end
2021-06-16 12:03:01 +03:00
hcaptcha_sitekey = get_var_from_path_or_env ( config_dir , " HCAPTCHA_SITEKEY " )
hcaptcha_secret = get_var_from_path_or_env ( config_dir , " HCAPTCHA_SECRET " )
2021-07-23 15:17:32 +03:00
custom_script_name =
config_dir
|> get_var_from_path_or_env ( " CUSTOM_SCRIPT_NAME " , " script " )
2021-06-16 12:03:01 +03:00
disable_cron =
config_dir
|> get_var_from_path_or_env ( " DISABLE_CRON " , " false " )
|> String . to_existing_atom ( )
2020-05-26 16:09:34 +03:00
2023-05-25 10:37:10 +03:00
log_failed_login_attempts =
config_dir
|> get_var_from_path_or_env ( " LOG_FAILED_LOGIN_ATTEMPTS " , " false " )
|> String . to_existing_atom ( )
2023-06-27 14:37:21 +03:00
websocket_url = get_var_from_path_or_env ( config_dir , " WEBSOCKET_URL " , " " )
2023-06-28 11:16:32 +03:00
if byte_size ( websocket_url ) > 0 and
not String . ends_with? ( URI . new! ( websocket_url ) . host , base_url . host ) do
raise """
Cross - domain websocket authentication is not supported for this server .
WEBSOCKET_URL = #{websocket_url} - host must be: '#{base_url.host}',
2023-06-28 13:28:26 +03:00
because BASE_URL = #{base_url}.
2023-06-28 11:16:32 +03:00
"""
end
2023-08-22 09:18:08 +03:00
secure_cookie =
config_dir
|> get_var_from_path_or_env ( " SECURE_COOKIE " , if ( is_selfhost , do : " false " , else : " true " ) )
|> String . to_existing_atom ( )
2024-02-23 14:35:22 +03:00
license_key = get_var_from_path_or_env ( config_dir , " LICENSE_KEY " , " " )
2020-05-26 16:09:34 +03:00
config :plausible ,
environment : env ,
2021-01-07 11:42:45 +03:00
mailer_email : mailer_email ,
2022-02-23 22:48:33 +03:00
super_admin_user_ids : super_admin_user_ids ,
2021-07-23 15:17:32 +03:00
is_selfhost : is_selfhost ,
2023-05-25 10:37:10 +03:00
custom_script_name : custom_script_name ,
2024-02-23 14:35:22 +03:00
log_failed_login_attempts : log_failed_login_attempts ,
2024-04-11 10:15:01 +03:00
license_key : license_key ,
2024-05-13 11:17:56 +03:00
data_dir : data_dir
2020-05-26 16:09:34 +03:00
2020-07-02 11:21:11 +03:00
config :plausible , :selfhost ,
2021-10-18 13:01:54 +03:00
enable_email_verification : enable_email_verification ,
2022-11-10 14:42:22 +03:00
disable_registration : disable_registration
2020-07-02 11:21:11 +03:00
2020-05-26 16:09:34 +03:00
config :plausible , PlausibleWeb.Endpoint ,
2021-10-18 12:16:56 +03:00
url : [ scheme : base_url . scheme , host : base_url . host , path : base_url . path , port : base_url . port ] ,
2022-10-19 15:41:05 +03:00
http : [
port : port ,
ip : listen_ip ,
transport_options : [ max_connections : :infinity ] ,
protocol_options : [ max_request_line_length : 8192 , max_header_value_length : 8192 ]
] ,
2023-06-27 14:37:21 +03:00
secret_key_base : secret_key_base ,
2023-08-22 09:18:08 +03:00
websocket_url : websocket_url ,
secure_cookie : secure_cookie
2020-05-26 16:09:34 +03:00
2024-07-15 14:05:31 +03:00
db_maybe_ipv6 =
2024-06-24 14:29:55 +03:00
if get_var_from_path_or_env ( config_dir , " ECTO_IPV6 " ) do
if config_env ( ) in [ :ce , :ce_dev , :ce_test ] do
Logger . warning (
" ECTO_IPV6 is no longer necessary as all TCP connections now try IPv6 automatically with IPv4 fallback "
)
end
[ :inet6 ]
else
[ ]
end
2022-02-17 06:26:26 +03:00
2024-07-15 14:05:31 +03:00
db_url =
get_var_from_path_or_env (
config_dir ,
" DATABASE_URL " ,
" postgres://postgres:postgres@plausible_db:5432/plausible_db "
)
if db_socket_dir = get_var_from_path_or_env ( config_dir , " DATABASE_SOCKET_DIR " ) do
Logger . warning ( """
DATABASE_SOCKET_DIR is deprecated , please use DATABASE_URL instead :
DATABASE_URL = postgresql :/ / postgres :postgres @ #{URI.encode_www_form(db_socket_dir)}/plausible_db
or
DATABASE_URL = postgresql :/ / / plausible_db? host = #{db_socket_dir}"
""" )
end
2023-08-30 15:34:19 +03:00
db_cacertfile = get_var_from_path_or_env ( config_dir , " DATABASE_CACERTFILE " , CAStore . file_path ( ) )
2024-07-15 14:05:31 +03:00
% URI { host : db_host } = db_uri = URI . parse ( db_url )
db_socket_dir? = String . starts_with? ( db_host , " %2F " ) or db_host == " "
if db_socket_dir? do
[ database ] = String . split ( db_uri . path , " / " , trim : true )
socket_dir =
if db_host == " " do
db_host = ( db_uri . query || " " ) |> URI . decode_query ( ) |> Map . get ( " host " )
db_host || raise ArgumentError , " DATABASE_URL= #{ db_url } doesn't include host info "
else
URI . decode_www_form ( db_host )
end
2023-08-30 15:34:19 +03:00
2024-07-15 14:05:31 +03:00
config :plausible , Plausible.Repo ,
socket_dir : socket_dir ,
database : database
if userinfo = db_uri . userinfo do
[ username , password ] = String . split ( userinfo , " : " )
config :plausible , Plausible.Repo ,
username : username ,
password : password
end
else
2022-02-17 06:26:26 +03:00
config :plausible , Plausible.Repo ,
url : db_url ,
2024-07-15 14:05:31 +03:00
socket_options : db_maybe_ipv6 ,
2023-08-30 15:34:19 +03:00
ssl_opts : [
cacertfile : db_cacertfile ,
verify : :verify_peer ,
customize_hostname_check : [
match_fun : :public_key . pkix_verify_hostname_match_fun ( :https )
]
]
2021-05-26 15:40:56 +03:00
end
2020-05-26 16:09:34 +03:00
2022-10-18 18:11:30 +03:00
sentry_app_version = runtime_metadata [ :version ] || app_version
2022-05-27 11:00:39 +03:00
2020-05-26 16:09:34 +03:00
config :sentry ,
dsn : sentry_dsn ,
environment_name : env ,
2022-10-18 18:11:30 +03:00
release : sentry_app_version ,
2023-02-03 14:51:32 +03:00
tags : %{ app_version : sentry_app_version } ,
2022-07-08 11:14:52 +03:00
client : Plausible.Sentry.Client ,
2022-07-14 03:03:59 +03:00
send_max_attempts : 1 ,
2024-03-18 12:10:20 +03:00
before_send : { Plausible.SentryFilter , :before_send }
2020-05-26 16:09:34 +03:00
2021-12-09 16:49:57 +03:00
config :plausible , :paddle ,
vendor_auth_code : paddle_auth_code ,
vendor_id : paddle_vendor_id
2020-05-26 16:09:34 +03:00
config :plausible , :google ,
client_id : google_cid ,
2022-08-03 12:25:50 +03:00
client_secret : google_secret ,
2022-09-08 21:02:17 +03:00
api_url : " https://www.googleapis.com " ,
2024-01-23 12:24:08 +03:00
reporting_api_url : " https://analyticsreporting.googleapis.com "
2024-07-12 13:01:59 +03:00
config :plausible , Plausible.HelpScout ,
app_id : help_scout_app_id ,
app_secret : help_scout_app_secret ,
signature_key : help_scout_signature_key ,
vault_key : help_scout_vault_key
2024-01-23 12:24:08 +03:00
config :plausible , :imported ,
max_buffer_size : get_int_from_path_or_env ( config_dir , " IMPORTED_MAX_BUFFER_SIZE " , 10_000 )
2020-05-26 16:09:34 +03:00
2023-07-11 14:57:57 +03:00
maybe_ch_ipv6 =
get_var_from_path_or_env ( config_dir , " ECTO_CH_IPV6 " , " false " )
|> String . to_existing_atom ( )
2024-06-24 14:29:55 +03:00
if maybe_ch_ipv6 && config_env ( ) in [ :ce , :ce_dev , :ce_test ] do
Logger . warning (
" ECTO_CH_IPV6 is no longer necessary as all TCP connections now try IPv6 automatically with IPv4 fallback "
)
end
2023-08-30 15:34:19 +03:00
ch_cacertfile = get_var_from_path_or_env ( config_dir , " CLICKHOUSE_CACERTFILE " )
2023-04-05 12:58:55 +03:00
ch_transport_opts = [
keepalive : true ,
2023-07-11 14:57:57 +03:00
show_econnreset : true ,
inet6 : maybe_ch_ipv6
2023-04-05 12:58:55 +03:00
]
2023-08-30 15:34:19 +03:00
ch_transport_opts =
if ch_cacertfile do
ch_transport_opts ++ [ cacertfile : ch_cacertfile ]
else
ch_transport_opts
end
2020-09-17 16:36:01 +03:00
config :plausible , Plausible.ClickhouseRepo ,
2023-02-12 19:50:57 +03:00
queue_target : 500 ,
queue_interval : 2000 ,
2023-04-05 12:58:55 +03:00
url : ch_db_url ,
2023-04-06 13:45:36 +03:00
transport_opts : ch_transport_opts ,
settings : [
2024-02-01 14:18:49 +03:00
readonly : 1 ,
2024-05-07 17:35:22 +03:00
join_algorithm : " direct,parallel_hash,hash "
2023-04-06 13:45:36 +03:00
]
2023-02-12 19:50:57 +03:00
config :plausible , Plausible.IngestRepo ,
2021-05-06 18:03:42 +03:00
queue_target : 500 ,
queue_interval : 2000 ,
2021-05-25 11:37:52 +03:00
url : ch_db_url ,
2023-04-05 12:58:55 +03:00
transport_opts : ch_transport_opts ,
2021-05-25 11:37:52 +03:00
flush_interval_ms : ch_flush_interval_ms ,
2023-02-12 19:50:57 +03:00
max_buffer_size : ch_max_buffer_size ,
2024-02-14 14:21:36 +03:00
pool_size : ingest_pool_size ,
settings : [
materialized_views_ignore_errors : 1
]
2020-05-26 16:09:34 +03:00
2023-02-23 16:34:24 +03:00
config :plausible , Plausible.AsyncInsertRepo ,
queue_target : 500 ,
queue_interval : 2000 ,
url : ch_db_url ,
2023-04-05 12:58:55 +03:00
transport_opts : ch_transport_opts ,
2023-02-23 16:34:24 +03:00
pool_size : 1 ,
2023-03-21 11:55:59 +03:00
settings : [
2023-02-23 16:34:24 +03:00
async_insert : 1 ,
2024-02-14 14:21:36 +03:00
wait_for_async_insert : 0 ,
materialized_views_ignore_errors : 1
2023-02-23 16:34:24 +03:00
]
2023-04-06 13:45:36 +03:00
config :plausible , Plausible.ImportDeletionRepo ,
queue_target : 500 ,
queue_interval : 2000 ,
url : ch_db_url ,
transport_opts : ch_transport_opts ,
pool_size : 1
2023-05-23 13:08:09 +03:00
config :ex_money ,
2023-06-12 20:29:17 +03:00
open_exchange_rates_app_id : get_var_from_path_or_env ( config_dir , " OPEN_EXCHANGE_RATES_APP_ID " ) ,
retrieve_every : :timer . hours ( 24 )
2023-05-23 13:08:09 +03:00
2020-05-26 16:09:34 +03:00
case mailer_adapter do
" Bamboo.PostmarkAdapter " ->
config :plausible , Plausible.Mailer ,
2023-02-07 14:56:47 +03:00
adapter : Bamboo.PostmarkAdapter ,
2020-12-22 16:50:08 +03:00
request_options : [ recv_timeout : 10_000 ] ,
2021-06-16 12:03:01 +03:00
api_key : get_var_from_path_or_env ( config_dir , " POSTMARK_API_KEY " )
2020-05-26 16:09:34 +03:00
2023-02-07 14:56:47 +03:00
" Bamboo.MailgunAdapter " ->
config :plausible , Plausible.Mailer ,
adapter : Bamboo.MailgunAdapter ,
hackney_opts : [ recv_timeout : :timer . seconds ( 10 ) ] ,
api_key : get_var_from_path_or_env ( config_dir , " MAILGUN_API_KEY " ) ,
domain : get_var_from_path_or_env ( config_dir , " MAILGUN_DOMAIN " )
2023-05-25 10:32:50 +03:00
if mailgun_base_uri = get_var_from_path_or_env ( config_dir , " MAILGUN_BASE_URI " ) do
config :plausible , Plausible.Mailer , base_uri : mailgun_base_uri
end
2023-02-07 14:56:47 +03:00
" Bamboo.MandrillAdapter " ->
config :plausible , Plausible.Mailer ,
adapter : Bamboo.MandrillAdapter ,
hackney_opts : [ recv_timeout : :timer . seconds ( 10 ) ] ,
api_key : get_var_from_path_or_env ( config_dir , " MANDRILL_API_KEY " )
" Bamboo.SendGridAdapter " ->
config :plausible , Plausible.Mailer ,
adapter : Bamboo.SendGridAdapter ,
hackney_opts : [ recv_timeout : :timer . seconds ( 10 ) ] ,
api_key : get_var_from_path_or_env ( config_dir , " SENDGRID_API_KEY " )
2020-05-26 16:09:34 +03:00
" Bamboo.SMTPAdapter " ->
config :plausible , Plausible.Mailer ,
2023-02-07 14:56:47 +03:00
adapter : Bamboo.SMTPAdapter ,
2021-06-16 12:03:01 +03:00
server : get_var_from_path_or_env ( config_dir , " SMTP_HOST_ADDR " , " mail " ) ,
2020-10-05 15:01:54 +03:00
hostname : base_url . host ,
2021-06-16 12:03:01 +03:00
port : get_var_from_path_or_env ( config_dir , " SMTP_HOST_PORT " , " 25 " ) ,
username : get_var_from_path_or_env ( config_dir , " SMTP_USER_NAME " ) ,
password : get_var_from_path_or_env ( config_dir , " SMTP_USER_PWD " ) ,
2020-05-26 16:09:34 +03:00
tls : :if_available ,
allowed_tls_versions : [ :tlsv1 , :" tlsv1.1 " , :" tlsv1.2 " ] ,
2021-06-29 15:26:42 +03:00
ssl : get_var_from_path_or_env ( config_dir , " SMTP_HOST_SSL_ENABLED " ) || false ,
retries : get_var_from_path_or_env ( config_dir , " SMTP_RETRIES " ) || 2 ,
2021-06-16 12:03:01 +03:00
no_mx_lookups : get_var_from_path_or_env ( config_dir , " SMTP_MX_LOOKUPS_ENABLED " ) || true
2020-05-26 16:09:34 +03:00
2024-02-27 16:18:36 +03:00
" Bamboo.Mua " ->
config :plausible , Plausible.Mailer , adapter : Bamboo.Mua
if relay = get_var_from_path_or_env ( config_dir , " SMTP_HOST_ADDR " ) do
port = get_int_from_path_or_env ( config_dir , " SMTP_HOST_PORT " , 25 )
2024-07-15 13:01:57 +03:00
config :plausible , Plausible.Mailer , relay : relay , port : port
end
username = get_var_from_path_or_env ( config_dir , " SMTP_USER_NAME " )
password = get_var_from_path_or_env ( config_dir , " SMTP_USER_PWD " )
cond do
username && password ->
config :plausible , Plausible.Mailer , auth : [ username : username , password : password ]
username || password ->
raise ArgumentError , """
Both SMTP_USER_NAME and SMTP_USER_PWD must be set for SMTP authentication .
Please provide values for both environment variables .
"""
2024-02-27 16:18:36 +03:00
2024-07-15 13:01:57 +03:00
_both_nil = true ->
nil
2024-02-27 16:18:36 +03:00
end
2021-01-13 16:41:16 +03:00
" Bamboo.LocalAdapter " ->
2021-01-13 18:04:01 +03:00
config :plausible , Plausible.Mailer , adapter : Bamboo.LocalAdapter
" Bamboo.TestAdapter " ->
config :plausible , Plausible.Mailer , adapter : Bamboo.TestAdapter
2021-01-13 16:41:16 +03:00
2020-05-26 16:09:34 +03:00
_ ->
2023-02-07 14:56:47 +03:00
raise ArgumentError , """
Unknown mailer_adapter : #{inspect(mailer_adapter)}
Please see https :/ / hexdocs . pm / bamboo / readme . html #available-adapters
for the list of available adapters that ship with Bamboo
"""
2020-05-26 16:09:34 +03:00
end
2022-04-08 11:05:21 +03:00
base_cron = [
# Daily at midnight
{ " 0 0 * * * " , Plausible.Workers.RotateSalts } ,
2023-04-04 11:55:12 +03:00
# hourly
2022-04-08 11:05:21 +03:00
{ " 0 * * * * " , Plausible.Workers.ScheduleEmailReports } ,
# hourly
{ " 0 * * * * " , Plausible.Workers.SendSiteSetupEmails } ,
# Daily at midday
{ " 0 12 * * * " , Plausible.Workers.SendCheckStatsEmails } ,
# Every 15 minutes
2024-07-11 15:55:18 +03:00
{ " */15 * * * * " , Plausible.Workers.TrafficChangeNotifier } ,
2022-04-08 11:05:21 +03:00
# Every day at 1am
2023-04-04 11:55:12 +03:00
{ " 0 1 * * * " , Plausible.Workers.CleanInvitations } ,
# Every 2 hours
{ " 0 */2 * * * " , Plausible.Workers.ExpireDomainChangeTransitions }
2022-04-08 11:05:21 +03:00
]
2022-04-11 20:20:05 +03:00
cloud_cron = [
2022-04-08 11:05:21 +03:00
# Daily at midday
{ " 0 12 * * * " , Plausible.Workers.SendTrialNotifications } ,
# Daily at 14
{ " 0 14 * * * " , Plausible.Workers.CheckUsage } ,
# Daily at 15
{ " 0 15 * * * " , Plausible.Workers.NotifyAnnualRenewal } ,
# Every midnight
2024-01-29 15:37:30 +03:00
{ " 0 0 * * * " , Plausible.Workers.LockSites } ,
# Daily at 8
2024-05-07 15:03:32 +03:00
{ " 0 8 * * * " , Plausible.Workers.AcceptTrafficUntil } ,
# First sunday of the month, 4:00 UTC
{ " 0 4 1-7 * SUN " , Plausible.Workers.ClickhouseCleanSites }
2022-04-08 11:05:21 +03:00
]
2022-04-11 20:20:05 +03:00
crontab = if ( is_selfhost , do : base_cron , else : base_cron ++ cloud_cron )
base_queues = [
2022-04-08 11:05:21 +03:00
rotate_salts : 1 ,
schedule_email_reports : 1 ,
send_email_reports : 1 ,
spike_notifications : 1 ,
check_stats_emails : 1 ,
site_setup_emails : 1 ,
clean_invitations : 1 ,
2024-01-23 12:24:08 +03:00
analytics_imports : 1 ,
2024-04-11 10:15:01 +03:00
analytics_exports : 1 ,
notify_exported_analytics : 1 ,
2024-01-29 15:37:30 +03:00
domain_change_transition : 1 ,
2024-04-11 10:15:01 +03:00
check_accept_traffic_until : 1
2022-04-11 20:20:05 +03:00
]
cloud_queues = [
2022-04-08 11:05:21 +03:00
trial_notification_emails : 1 ,
check_usage : 1 ,
notify_annual_renewal : 1 ,
2024-03-18 10:52:57 +03:00
lock_sites : 1
2022-04-08 11:05:21 +03:00
]
2022-04-11 20:20:05 +03:00
queues = if ( is_selfhost , do : base_queues , else : base_queues ++ cloud_queues )
2022-04-13 10:40:27 +03:00
cron_enabled = ! disable_cron
2022-04-11 20:20:05 +03:00
2022-07-22 12:00:00 +03:00
thirty_days_in_seconds = 60 * 60 * 24 * 30
2024-05-06 11:40:09 +03:00
if config_env ( ) in [ :prod , :ce ] do
config :plausible , Oban ,
repo : Plausible.Repo ,
plugins : [
# Keep 30 days history
{ Oban.Plugins.Pruner , max_age : thirty_days_in_seconds } ,
{ Oban.Plugins.Cron , crontab : if ( cron_enabled , do : crontab , else : [ ] ) } ,
# Rescue orphaned jobs after 2 hours
{ Oban.Plugins.Lifeline , rescue_after : :timer . minutes ( 120 ) }
] ,
queues : if ( cron_enabled , do : queues , else : [ ] ) ,
peer : if ( cron_enabled , do : Oban.Peers.Postgres , else : false )
else
config :plausible , Oban ,
repo : Plausible.Repo ,
queues : queues ,
plugins : false
2021-01-13 18:04:01 +03:00
end
2020-06-04 11:23:30 +03:00
2020-08-28 15:00:16 +03:00
config :plausible , :hcaptcha ,
sitekey : hcaptcha_sitekey ,
secret : hcaptcha_secret
2023-10-17 12:01:27 +03:00
nolt_sso_secret = get_var_from_path_or_env ( config_dir , " NOLT_SSO_SECRET " )
config :joken , default_signer : nolt_sso_secret
2022-07-08 11:14:52 +03:00
config :plausible , Plausible.Sentry.Client ,
finch_request_opts : [
pool_timeout : get_int_from_path_or_env ( config_dir , " SENTRY_FINCH_POOL_TIMEOUT " , 5000 ) ,
receive_timeout : get_int_from_path_or_env ( config_dir , " SENTRY_FINCH_RECEIVE_TIMEOUT " , 15000 )
]
2020-06-09 18:24:08 +03:00
config :ref_inspector ,
init : { Plausible.Release , :configure_ref_inspector }
config :ua_inspector ,
init : { Plausible.Release , :configure_ua_inspector }
2024-04-23 11:29:49 +03:00
if config_env ( ) in [ :dev , :staging , :prod , :test ] do
2023-11-20 14:52:20 +03:00
config :kaffy ,
otp_app : :plausible ,
ecto_repo : Plausible.Repo ,
router : PlausibleWeb.Router ,
admin_title : " Plausible Admin " ,
2024-06-17 10:21:24 +03:00
extensions : [ Plausible.CrmExtensions ] ,
2023-11-20 14:52:20 +03:00
resources : [
auth : [
resources : [
user : [ schema : Plausible.Auth.User , admin : Plausible.Auth.UserAdmin ] ,
api_key : [ schema : Plausible.Auth.ApiKey , admin : Plausible.Auth.ApiKeyAdmin ]
]
] ,
sites : [
resources : [
site : [ schema : Plausible.Site , admin : Plausible.SiteAdmin ]
]
] ,
billing : [
resources : [
enterprise_plan : [
schema : Plausible.Billing.EnterprisePlan ,
admin : Plausible.Billing.EnterprisePlanAdmin
]
2021-10-20 17:49:11 +03:00
]
]
2021-01-08 17:54:17 +03:00
]
2023-11-20 14:52:20 +03:00
end
2021-01-08 17:54:17 +03:00
2023-01-17 18:05:09 +03:00
geo_opts =
cond do
maxmind_license_key ->
[
license_key : maxmind_license_key ,
edition : maxmind_edition ,
2024-03-08 17:33:55 +03:00
cache_dir : persistent_cache_dir ,
2023-01-17 18:05:09 +03:00
async : true
]
ip_geolocation_db ->
[ path : ip_geolocation_db ]
true ->
raise """
Missing geolocation database configuration .
Please set the IP_GEOLOCATION_DB environment value to the location of
your IP geolocation . mmdb file :
IP_GEOLOCATION_DB = / etc / plausible / dbip - city . mmdb
Or authenticate with MaxMind by
configuring MAXMIND_LICENSE_KEY and ( optionally ) MAXMIND_EDITION environment
variables :
MAXMIND_LICENSE_KEY = LNpsJCCKPis6XvBP
MAXMIND_EDITION = GeoLite2 - City # this is the default edition
"""
end
config :plausible , Plausible.Geo , geo_opts
2020-06-12 09:51:45 +03:00
2021-12-13 13:03:27 +03:00
if geonames_source_file do
config :location , :geonames_source_file , geonames_source_file
end
2022-04-28 12:24:29 +03:00
if honeycomb_api_key && honeycomb_dataset do
config :opentelemetry ,
2022-10-18 18:11:30 +03:00
resource : Plausible.OpenTelemetry . resource_attributes ( runtime_metadata ) ,
2023-11-13 16:57:51 +03:00
sampler : { Plausible.OpenTelemetry.Sampler , %{ ratio : otel_sampler_ratio } } ,
2022-04-28 12:24:29 +03:00
span_processor : :batch ,
2022-10-18 18:11:30 +03:00
traces_exporter : :otlp
2022-04-28 12:24:29 +03:00
config :opentelemetry_exporter ,
otlp_protocol : :grpc ,
2022-10-18 18:11:30 +03:00
otlp_endpoint : " https://api.honeycomb.io:443 " ,
2022-04-28 12:24:29 +03:00
otlp_headers : [
{ " x-honeycomb-team " , honeycomb_api_key } ,
{ " x-honeycomb-dataset " , honeycomb_dataset }
]
else
2022-10-18 18:11:30 +03:00
config :opentelemetry ,
sampler : :always_off ,
traces_exporter : :none
2022-04-28 12:24:29 +03:00
end
2021-11-05 15:58:57 +03:00
2024-03-08 17:33:55 +03:00
config :tzdata , :data_dir , Path . join ( persistent_cache_dir || System . tmp_dir! ( ) , " tzdata_data " )
2022-07-11 15:00:04 +03:00
promex_disabled? =
config_dir
|> get_var_from_path_or_env ( " PROMEX_DISABLED " , " true " )
|> String . to_existing_atom ( )
config :plausible , Plausible.PromEx ,
disabled : promex_disabled? ,
manual_metrics_start_delay : :no_delay ,
drop_metrics_groups : [ ] ,
grafana : :disabled ,
metrics_server : :disabled
2023-04-13 14:52:54 +03:00
2024-05-23 16:00:50 +03:00
config :plausible , Plausible.Verification ,
enabled? :
get_var_from_path_or_env ( config_dir , " VERIFICATION_ENABLED " , " false " )
|> String . to_existing_atom ( )
config :plausible , Plausible.Verification.Checks.Installation ,
token : get_var_from_path_or_env ( config_dir , " BROWSERLESS_TOKEN " , " dummy_token " ) ,
endpoint : get_var_from_path_or_env ( config_dir , " BROWSERLESS_ENDPOINT " , " http://0.0.0.0:3000 " )
2023-04-13 14:52:54 +03:00
if not is_selfhost do
site_default_ingest_threshold =
case System . get_env ( " SITE_DEFAULT_INGEST_THRESHOLD " ) do
threshold when byte_size ( threshold ) > 0 ->
{ value , " " } = Integer . parse ( threshold )
value
_ ->
nil
end
config :plausible , Plausible.Site , default_ingest_threshold : site_default_ingest_threshold
end
2024-02-27 16:19:09 +03:00
s3_disabled? =
config_dir
|> get_var_from_path_or_env ( " S3_DISABLED " , " true " )
|> String . to_existing_atom ( )
unless s3_disabled? do
s3_env = [
%{
name : " S3_ACCESS_KEY_ID " ,
example : " AKIAIOSFODNN7EXAMPLE "
} ,
%{
name : " S3_SECRET_ACCESS_KEY " ,
example : " wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY "
} ,
%{
name : " S3_REGION " ,
example : " us-east-1 "
} ,
%{
name : " S3_ENDPOINT " ,
example : " https://<ACCOUNT_ID>.r2.cloudflarestorage.com "
2024-03-18 10:52:57 +03:00
} ,
%{
name : " S3_EXPORTS_BUCKET " ,
example : " my-csv-exports-bucket "
2024-03-19 14:06:47 +03:00
} ,
%{
name : " S3_IMPORTS_BUCKET " ,
example : " my-csv-imports-bucket "
2024-02-27 16:19:09 +03:00
}
]
s3_env =
Enum . map ( s3_env , fn var ->
Map . put ( var , :value , get_var_from_path_or_env ( config_dir , var . name ) )
end )
s3_missing_env = Enum . filter ( s3_env , & is_nil ( &1 . value ) )
unless s3_missing_env == [ ] do
raise ArgumentError , """
Missing S3 configuration . Please set #{s3_missing_env |> Enum.map(& &1.name) |> Enum.join(", ")} environment variable(s):
#{s3_missing_env |> Enum.map(fn %{name: name, example: example} -> "\t#{name}=#{example}" end) |> Enum.join("\n")}
"""
end
s3_env_value = fn name ->
s3_env |> Enum . find ( & ( &1 . name == name ) ) |> Map . fetch! ( :value )
end
config :ex_aws ,
http_client : Plausible.S3.Client ,
access_key_id : s3_env_value . ( " S3_ACCESS_KEY_ID " ) ,
secret_access_key : s3_env_value . ( " S3_SECRET_ACCESS_KEY " ) ,
region : s3_env_value . ( " S3_REGION " )
% URI { scheme : s3_scheme , host : s3_host , port : s3_port } = URI . parse ( s3_env_value . ( " S3_ENDPOINT " ) )
config :ex_aws , :s3 ,
scheme : s3_scheme <> " :// " ,
host : s3_host ,
port : s3_port
2024-03-18 10:52:57 +03:00
2024-03-19 14:06:47 +03:00
config :plausible , Plausible.S3 ,
exports_bucket : s3_env_value . ( " S3_EXPORTS_BUCKET " ) ,
imports_bucket : s3_env_value . ( " S3_IMPORTS_BUCKET " )
2024-02-27 16:19:09 +03:00
end