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
|
|
|
|
2023-11-29 13:04:54 +03:00
|
|
|
if config_env() == :small_dev do
|
2023-11-20 14:52:20 +03:00
|
|
|
Envy.load(["config/.env.dev"])
|
|
|
|
end
|
|
|
|
|
|
|
|
if config_env() == :small_test do
|
|
|
|
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
|
|
|
|
raise "BASE_URL configuration option is required. See https://plausible.io/docs/self-hosting-configuration#server"
|
|
|
|
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 ->
|
|
|
|
raise "SECRET_KEY_BASE configuration option is required. See https://plausible.io/docs/self-hosting-configuration#server"
|
2021-04-13 11:19:24 +03:00
|
|
|
|
2021-11-04 15:16:37 +03:00
|
|
|
key when byte_size(key) < 32 ->
|
|
|
|
raise "SECRET_KEY_BASE must be at least 32 bytes long. See https://plausible.io/docs/self-hosting-configuration#server"
|
2021-06-16 12:03:01 +03:00
|
|
|
|
|
|
|
_ ->
|
|
|
|
nil
|
|
|
|
end
|
2020-05-26 16:09:34 +03:00
|
|
|
|
|
|
|
db_url =
|
2021-06-16 12:03:01 +03:00
|
|
|
get_var_from_path_or_env(
|
|
|
|
config_dir,
|
2020-05-26 16:09:34 +03:00
|
|
|
"DATABASE_URL",
|
2020-10-05 15:01:54 +03:00
|
|
|
"postgres://postgres:postgres@plausible_db:5432/plausible_db"
|
2020-05-26 16:09:34 +03:00
|
|
|
)
|
|
|
|
|
2021-06-16 12:03:01 +03:00
|
|
|
db_socket_dir = get_var_from_path_or_env(config_dir, "DATABASE_SOCKET_DIR")
|
|
|
|
|
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.
|
|
|
|
totp_vault_key = get_var_from_path_or_env(config_dir, "TOTP_VAULT_KEY", nil)
|
|
|
|
|
|
|
|
case totp_vault_key do
|
|
|
|
nil ->
|
|
|
|
raise "TOTP_VAULT_KEY configuration option is required. See https://plausible.io/docs/self-hosting-configuration#server"
|
|
|
|
|
|
|
|
key ->
|
|
|
|
if byte_size(Base.decode64!(key)) != 32 do
|
|
|
|
raise "TOTP_VAULT_KEY must exactly 32 bytes long. See https://plausible.io/docs/self-hosting-configuration#server"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-26 16:09:34 +03:00
|
|
|
### Mandatory params End
|
|
|
|
|
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
|
|
|
|
|
2023-11-20 16:04:48 +03:00
|
|
|
config :plausible, Plausible.Auth.TOTP, vault_key: totp_vault_key
|
|
|
|
|
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")
|
|
|
|
|
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")
|
2023-10-24 11:33:48 +03:00
|
|
|
maxmind_cache_dir = get_var_from_path_or_env(config_dir, "PERSISTENT_CACHE_DIR")
|
2021-11-23 12:39:09 +03:00
|
|
|
|
2022-11-10 14:42:22 +03:00
|
|
|
if System.get_env("DISABLE_AUTH") do
|
2023-10-24 11:33:48 +03:00
|
|
|
Logger.warning("DISABLE_AUTH env var is no longer supported")
|
2022-11-10 14:42:22 +03:00
|
|
|
end
|
2021-06-16 12:03:01 +03:00
|
|
|
|
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
|
|
|
|
raise "DISABLE_REGISTRATION must be one of `true`, `false`, or `invite_only`. See https://plausible.io/docs/self-hosting-configuration#server"
|
|
|
|
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()
|
|
|
|
|
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,
|
2023-12-11 18:31:03 +03:00
|
|
|
log_failed_login_attempts: log_failed_login_attempts
|
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
|
|
|
|
2022-02-17 06:26:26 +03:00
|
|
|
maybe_ipv6 = if System.get_env("ECTO_IPV6"), do: [:inet6], else: []
|
|
|
|
|
2023-08-30 15:34:19 +03:00
|
|
|
db_cacertfile = get_var_from_path_or_env(config_dir, "DATABASE_CACERTFILE", CAStore.file_path())
|
|
|
|
|
2021-06-16 12:03:01 +03:00
|
|
|
if is_nil(db_socket_dir) do
|
2022-02-17 06:26:26 +03:00
|
|
|
config :plausible, Plausible.Repo,
|
|
|
|
url: db_url,
|
2023-08-30 15:34:19 +03:00
|
|
|
socket_options: maybe_ipv6,
|
|
|
|
ssl_opts: [
|
|
|
|
cacertfile: db_cacertfile,
|
|
|
|
verify: :verify_peer,
|
|
|
|
customize_hostname_check: [
|
|
|
|
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
|
|
|
|
]
|
|
|
|
]
|
2021-06-16 12:03:01 +03:00
|
|
|
else
|
|
|
|
config :plausible, Plausible.Repo,
|
|
|
|
socket_dir: db_socket_dir,
|
|
|
|
database: get_var_from_path_or_env(config_dir, "DATABASE_NAME", "plausible")
|
2021-05-26 15:40:56 +03:00
|
|
|
end
|
2020-05-26 16:09:34 +03:00
|
|
|
|
2022-07-08 11:14:52 +03:00
|
|
|
included_environments = if sentry_dsn, do: ["prod", "staging", "dev"], else: []
|
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-05-27 11:00:39 +03:00
|
|
|
included_environments: included_environments,
|
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},
|
2021-04-23 11:56:41 +03:00
|
|
|
enable_source_code_context: true,
|
2022-07-07 10:15:13 +03:00
|
|
|
root_source_code_path: [File.cwd!()],
|
2022-07-08 11:14:52 +03:00
|
|
|
client: Plausible.Sentry.Client,
|
2022-07-14 03:03:59 +03:00
|
|
|
send_max_attempts: 1,
|
2022-09-22 18:24:54 +03:00
|
|
|
filter: Plausible.SentryFilter,
|
|
|
|
before_send_event: {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"
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
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,
|
|
|
|
join_algorithm: "direct,parallel_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,
|
|
|
|
pool_size: ingest_pool_size
|
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,
|
|
|
|
wait_for_async_insert: 0
|
|
|
|
]
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
{"*/15 * * * *", Plausible.Workers.SpikeNotifier},
|
|
|
|
# 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
|
|
|
|
{"0 8 * * *", Plausible.Workers.AcceptTrafficUntil}
|
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
|
|
|
# NOTE: to be removed once #3700 is released
|
2023-04-04 11:55:12 +03:00
|
|
|
google_analytics_imports: 1,
|
2024-01-23 12:24:08 +03:00
|
|
|
analytics_imports: 1,
|
2024-01-29 15:37:30 +03:00
|
|
|
domain_change_transition: 1,
|
|
|
|
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,
|
|
|
|
lock_sites: 1
|
|
|
|
]
|
|
|
|
|
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
|
|
|
|
|
2022-04-08 11:05:21 +03:00
|
|
|
cond do
|
2022-04-11 20:20:05 +03:00
|
|
|
config_env() == :prod ->
|
2022-04-08 11:05:21 +03:00
|
|
|
config :plausible, Oban,
|
|
|
|
repo: Plausible.Repo,
|
|
|
|
plugins: [
|
|
|
|
# Keep 30 days history
|
2022-07-22 12:00:00 +03:00
|
|
|
{Oban.Plugins.Pruner, max_age: thirty_days_in_seconds},
|
2022-04-11 20:20:05 +03:00
|
|
|
{Oban.Plugins.Cron, crontab: if(cron_enabled, do: crontab, else: [])},
|
2022-04-08 11:05:21 +03:00
|
|
|
# Rescue orphaned jobs after 2 hours
|
2024-01-11 10:48:04 +03:00
|
|
|
{Oban.Plugins.Lifeline, rescue_after: :timer.minutes(120)}
|
2022-04-08 11:05:21 +03:00
|
|
|
],
|
2022-04-28 16:57:56 +03:00
|
|
|
queues: if(cron_enabled, do: queues, else: []),
|
|
|
|
peer: if(cron_enabled, do: Oban.Peers.Postgres, else: false)
|
2022-04-08 11:05:21 +03:00
|
|
|
|
|
|
|
true ->
|
|
|
|
config :plausible, Oban,
|
|
|
|
repo: Plausible.Repo,
|
|
|
|
queues: queues,
|
2022-04-11 20:20:05 +03:00
|
|
|
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}
|
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
if config_env() in [:dev, :staging, :prod] do
|
|
|
|
config :kaffy,
|
|
|
|
otp_app: :plausible,
|
|
|
|
ecto_repo: Plausible.Repo,
|
|
|
|
router: PlausibleWeb.Router,
|
|
|
|
admin_title: "Plausible Admin",
|
|
|
|
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,
|
2023-10-24 11:33:48 +03:00
|
|
|
cache_dir: maxmind_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
|
|
|
|
2021-06-16 12:03:01 +03:00
|
|
|
config :tzdata,
|
|
|
|
:data_dir,
|
|
|
|
get_var_from_path_or_env(config_dir, "STORAGE_DIR", Application.app_dir(:tzdata, "priv"))
|
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
|
|
|
|
|
|
|
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
|