283 lines
11 KiB
Erlang
283 lines
11 KiB
Erlang
%% Copyright (C) 2026 Fluxer Contributors
|
|
%%
|
|
%% This file is part of Fluxer.
|
|
%%
|
|
%% Fluxer is free software: you can redistribute it and/or modify
|
|
%% it under the terms of the GNU Affero General Public License as published by
|
|
%% the Free Software Foundation, either version 3 of the License, or
|
|
%% (at your option) any later version.
|
|
%%
|
|
%% Fluxer is distributed in the hope that it will be useful,
|
|
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
%% GNU Affero General Public License for more details.
|
|
%%
|
|
%% You should have received a copy of the GNU Affero General Public License
|
|
%% along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
-module(fluxer_gateway_config).
|
|
|
|
-export([load/0, load_from/1]).
|
|
|
|
-type config() :: map().
|
|
-type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
|
|
|
|
-spec load() -> config().
|
|
load() ->
|
|
case os:getenv("FLUXER_CONFIG") of
|
|
false -> erlang:error({missing_env, "FLUXER_CONFIG"});
|
|
"" -> erlang:error({missing_env, "FLUXER_CONFIG"});
|
|
Path -> load_from(Path)
|
|
end.
|
|
|
|
-spec load_from(string()) -> config().
|
|
load_from(Path) when is_list(Path) ->
|
|
case file:read_file(Path) of
|
|
{ok, Content} ->
|
|
Json = json:decode(Content),
|
|
build_config(Json);
|
|
{error, Reason} ->
|
|
erlang:error({json_read_failed, Path, Reason})
|
|
end.
|
|
|
|
-spec build_config(map()) -> config().
|
|
build_config(Json) ->
|
|
Service = get_map(Json, [<<"services">>, <<"gateway">>]),
|
|
Nats = get_map(Json, [<<"services">>, <<"nats">>]),
|
|
Telemetry = get_map(Json, [<<"telemetry">>]),
|
|
Sentry = get_map(Json, [<<"sentry">>]),
|
|
Vapid = get_map(Json, [<<"auth">>, <<"vapid">>]),
|
|
#{
|
|
port => get_int(Service, <<"port">>, 8080),
|
|
admin_reload_secret => get_optional_binary(Service, <<"admin_reload_secret">>),
|
|
nats_core_url => get_string(Nats, <<"core_url">>, "nats://127.0.0.1:4222"),
|
|
nats_auth_token => get_string(Nats, <<"auth_token">>, ""),
|
|
identify_rate_limit_enabled => get_bool(Service, <<"identify_rate_limit_enabled">>, false),
|
|
push_enabled => get_bool(Service, <<"push_enabled">>, true),
|
|
push_user_guild_settings_cache_mb => get_int(
|
|
Service,
|
|
<<"push_user_guild_settings_cache_mb">>,
|
|
1024
|
|
),
|
|
push_subscriptions_cache_mb => get_int(Service, <<"push_subscriptions_cache_mb">>, 1024),
|
|
push_blocked_ids_cache_mb => get_int(Service, <<"push_blocked_ids_cache_mb">>, 1024),
|
|
presence_cache_shards => get_optional_int(Service, <<"presence_cache_shards">>),
|
|
presence_bus_shards => get_optional_int(Service, <<"presence_bus_shards">>),
|
|
presence_shards => get_optional_int(Service, <<"presence_shards">>),
|
|
guild_shards => get_optional_int(Service, <<"guild_shards">>),
|
|
session_shards => get_optional_int(Service, <<"session_shards">>),
|
|
push_badge_counts_cache_mb => get_int(Service, <<"push_badge_counts_cache_mb">>, 256),
|
|
push_badge_counts_cache_ttl_seconds =>
|
|
get_int(Service, <<"push_badge_counts_cache_ttl_seconds">>, 60),
|
|
push_dispatcher_max_inflight => get_int(Service, <<"push_dispatcher_max_inflight">>, 16),
|
|
push_dispatcher_max_queue => get_int(Service, <<"push_dispatcher_max_queue">>, 2048),
|
|
gateway_http_push_connect_timeout_ms =>
|
|
get_int(Service, <<"gateway_http_push_connect_timeout_ms">>, 3000),
|
|
gateway_http_push_recv_timeout_ms =>
|
|
get_int(Service, <<"gateway_http_push_recv_timeout_ms">>, 5000),
|
|
gateway_http_rpc_max_concurrency =>
|
|
get_int(Service, <<"gateway_http_rpc_max_concurrency">>, 512),
|
|
gateway_http_push_max_concurrency =>
|
|
get_int(Service, <<"gateway_http_push_max_concurrency">>, 256),
|
|
gateway_http_failure_threshold =>
|
|
get_int(Service, <<"gateway_http_failure_threshold">>, 6),
|
|
gateway_http_recovery_timeout_ms =>
|
|
get_int(Service, <<"gateway_http_recovery_timeout_ms">>, 15000),
|
|
gateway_http_cleanup_interval_ms =>
|
|
get_int(Service, <<"gateway_http_cleanup_interval_ms">>, 30000),
|
|
gateway_http_cleanup_max_age_ms =>
|
|
get_int(Service, <<"gateway_http_cleanup_max_age_ms">>, 300000),
|
|
media_proxy_endpoint => get_optional_binary(Service, <<"media_proxy_endpoint">>),
|
|
vapid_email => get_binary(Vapid, <<"email">>, <<>>),
|
|
vapid_public_key => get_optional_binary(Vapid, <<"public_key">>),
|
|
vapid_private_key => get_optional_binary(Vapid, <<"private_key">>),
|
|
gateway_metrics_enabled => get_optional_bool(Service, <<"gateway_metrics_enabled">>),
|
|
gateway_metrics_report_interval_ms =>
|
|
get_optional_int(Service, <<"gateway_metrics_report_interval_ms">>),
|
|
release_node => get_string(Service, <<"release_node">>, "fluxer_gateway@127.0.0.1"),
|
|
logger_level => get_log_level(Service, <<"logger_level">>, info),
|
|
telemetry => #{
|
|
enabled => get_bool(Telemetry, <<"enabled">>, true),
|
|
otlp_endpoint => get_string(Telemetry, <<"otlp_endpoint">>, ""),
|
|
api_key => get_string(Telemetry, <<"api_key">>, ""),
|
|
service_name => get_string(Telemetry, <<"service_name">>, "fluxer-gateway"),
|
|
environment => get_string(Telemetry, <<"environment">>, "development"),
|
|
trace_sampling_ratio => get_float(Telemetry, <<"trace_sampling_ratio">>, 1.0)
|
|
},
|
|
sentry => #{
|
|
build_sha => get_string(Sentry, <<"build_sha">>, ""),
|
|
release_channel => get_string(Sentry, <<"release_channel">>, "")
|
|
}
|
|
}.
|
|
|
|
-spec get_map(map(), [binary()]) -> map().
|
|
get_map(Map, Keys) ->
|
|
case get_in(Map, Keys) of
|
|
Value when is_map(Value) -> Value;
|
|
_ -> #{}
|
|
end.
|
|
|
|
-spec get_int(map(), binary(), integer()) -> integer().
|
|
get_int(Map, Key, Default) when is_integer(Default) ->
|
|
to_int(get_value(Map, Key), Default).
|
|
|
|
-spec get_optional_int(map(), binary()) -> integer() | undefined.
|
|
get_optional_int(Map, Key) ->
|
|
to_optional_int(get_value(Map, Key)).
|
|
|
|
-spec get_bool(map(), binary(), boolean()) -> boolean().
|
|
get_bool(Map, Key, Default) when is_boolean(Default) ->
|
|
to_bool(get_value(Map, Key), Default).
|
|
|
|
-spec get_optional_bool(map(), binary()) -> boolean() | undefined.
|
|
get_optional_bool(Map, Key) ->
|
|
case get_value(Map, Key) of
|
|
undefined -> undefined;
|
|
Value -> to_bool(Value, undefined)
|
|
end.
|
|
|
|
-spec get_string(map(), binary(), string()) -> string().
|
|
get_string(Map, Key, Default) when is_list(Default) ->
|
|
to_string(get_value(Map, Key), Default).
|
|
|
|
-spec get_binary(map(), binary(), binary() | undefined) -> binary() | undefined.
|
|
get_binary(Map, Key, Default) ->
|
|
to_binary(get_value(Map, Key), Default).
|
|
|
|
-spec get_optional_binary(map(), binary()) -> binary() | undefined.
|
|
get_optional_binary(Map, Key) ->
|
|
case get_value(Map, Key) of
|
|
undefined -> undefined;
|
|
Value -> to_binary(Value, undefined)
|
|
end.
|
|
|
|
-spec get_log_level(map(), binary(), log_level()) -> log_level().
|
|
get_log_level(Map, Key, Default) when is_atom(Default) ->
|
|
Value = get_value(Map, Key),
|
|
case normalize_log_level(Value) of
|
|
undefined -> Default;
|
|
Level -> Level
|
|
end.
|
|
|
|
-spec get_float(map(), binary(), number()) -> float().
|
|
get_float(Map, Key, Default) when is_number(Default) ->
|
|
to_float(get_value(Map, Key), Default).
|
|
|
|
-spec get_in(term(), [binary()]) -> term().
|
|
get_in(Map, [Key | Rest]) when is_map(Map) ->
|
|
case get_value(Map, Key) of
|
|
undefined -> undefined;
|
|
Value when Rest =:= [] -> Value;
|
|
Value -> get_in(Value, Rest)
|
|
end;
|
|
get_in(_, _) ->
|
|
undefined.
|
|
|
|
-spec get_value(term(), binary()) -> term().
|
|
get_value(Map, Key) when is_map(Map) ->
|
|
case maps:get(Key, Map, undefined) of
|
|
undefined when is_binary(Key) ->
|
|
maps:get(binary_to_list(Key), Map, undefined);
|
|
Value ->
|
|
Value
|
|
end.
|
|
|
|
-spec to_int(term(), integer() | undefined) -> integer() | undefined.
|
|
to_int(Value, _Default) when is_integer(Value) ->
|
|
Value;
|
|
to_int(Value, _Default) when is_float(Value) ->
|
|
trunc(Value);
|
|
to_int(Value, Default) ->
|
|
case to_string(Value, "") of
|
|
"" ->
|
|
Default;
|
|
Str ->
|
|
case string:to_integer(Str) of
|
|
{Int, _} when is_integer(Int) -> Int;
|
|
{error, _} -> Default
|
|
end
|
|
end.
|
|
|
|
-spec to_optional_int(term()) -> integer() | undefined.
|
|
to_optional_int(Value) ->
|
|
case to_int(Value, undefined) of
|
|
undefined -> undefined;
|
|
Int -> Int
|
|
end.
|
|
|
|
-spec to_bool(term(), boolean() | undefined) -> boolean() | undefined.
|
|
to_bool(Value, _Default) when is_boolean(Value) ->
|
|
Value;
|
|
to_bool(Value, Default) when is_atom(Value) ->
|
|
case Value of
|
|
true -> true;
|
|
false -> false;
|
|
_ -> Default
|
|
end;
|
|
to_bool(Value, Default) ->
|
|
case string:lowercase(to_string(Value, "")) of
|
|
"true" -> true;
|
|
"1" -> true;
|
|
"false" -> false;
|
|
"0" -> false;
|
|
_ -> Default
|
|
end.
|
|
|
|
-spec to_string(term(), string()) -> string().
|
|
to_string(Value, Default) when is_list(Default) ->
|
|
case Value of
|
|
undefined -> Default;
|
|
Bin when is_binary(Bin) -> binary_to_list(Bin);
|
|
Str when is_list(Str) -> Str;
|
|
Atom when is_atom(Atom) -> atom_to_list(Atom);
|
|
_ -> Default
|
|
end.
|
|
|
|
-spec to_binary(term(), binary() | undefined) -> binary() | undefined.
|
|
to_binary(Value, Default) ->
|
|
case Value of
|
|
undefined -> Default;
|
|
Bin when is_binary(Bin) -> Bin;
|
|
Str when is_list(Str) -> list_to_binary(Str);
|
|
Atom when is_atom(Atom) -> list_to_binary(atom_to_list(Atom));
|
|
_ -> Default
|
|
end.
|
|
|
|
-spec to_float(term(), float()) -> float().
|
|
to_float(Value, _Default) when is_float(Value) ->
|
|
Value;
|
|
to_float(Value, _Default) when is_integer(Value) ->
|
|
float(Value);
|
|
to_float(Value, Default) ->
|
|
case to_string(Value, "") of
|
|
"" ->
|
|
Default;
|
|
Str ->
|
|
case string:to_float(Str) of
|
|
{Float, _} when is_float(Float) -> Float;
|
|
{error, _} -> Default
|
|
end
|
|
end.
|
|
|
|
-spec normalize_log_level(term()) -> log_level() | undefined.
|
|
normalize_log_level(undefined) ->
|
|
undefined;
|
|
normalize_log_level(Level) when is_atom(Level) ->
|
|
normalize_log_level(atom_to_list(Level));
|
|
normalize_log_level(Level) when is_binary(Level) ->
|
|
normalize_log_level(binary_to_list(Level));
|
|
normalize_log_level(Level) when is_list(Level) ->
|
|
case string:lowercase(string:trim(Level)) of
|
|
"debug" -> debug;
|
|
"info" -> info;
|
|
"notice" -> notice;
|
|
"warning" -> warning;
|
|
"error" -> error;
|
|
"critical" -> critical;
|
|
"alert" -> alert;
|
|
"emergency" -> emergency;
|
|
_ -> undefined
|
|
end;
|
|
normalize_log_level(_) ->
|
|
undefined.
|