Packages

phoenix_kit

1.7.36
1.7.210 1.7.209 1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit install rate_limiter_config.ex
Raw

lib/phoenix_kit/install/rate_limiter_config.ex

defmodule PhoenixKit.Install.RateLimiterConfig do
@moduledoc """
Handles Hammer rate limiter configuration for PhoenixKit installation.
This module provides functionality to:
- Configure Hammer backend (ETS by default)
- Add rate limiting configuration for PhoenixKit endpoints
- Ensure configuration exists during updates
"""
use PhoenixKit.Install.IgniterCompat
@doc """
Adds or verifies Hammer rate limiter configuration.
This function ensures that both:
1. Hammer backend configuration exists (required for Hammer to start)
2. PhoenixKit rate limiter settings are configured
## Parameters
- `igniter` - The igniter context
## Returns
Updated igniter with rate limiter configuration and notices.
"""
def add_rate_limiter_configuration(igniter) do
igniter
|> add_hammer_backend_config()
|> add_phoenix_kit_rate_limiter_config()
|> add_rate_limiter_notice()
end
@doc """
Checks if Hammer configuration exists in config.exs.
## Parameters
- `_igniter` - The igniter context (unused but required for API consistency)
## Returns
Boolean indicating if configuration exists.
"""
def hammer_config_exists?(_igniter) do
config_path = "config/config.exs"
if File.exists?(config_path) do
content = File.read!(config_path)
lines = String.split(content, "\n")
# Check for active (non-commented) Hammer configuration
has_hammer_config =
Enum.any?(lines, fn line ->
trimmed = String.trim(line)
# Not a comment and contains config :hammer
!String.starts_with?(trimmed, "#") and String.starts_with?(trimmed, "config :hammer")
end)
has_expiry_ms =
Enum.any?(lines, fn line ->
trimmed = String.trim(line)
# Not a comment and contains expiry_ms
!String.starts_with?(trimmed, "#") and String.contains?(line, "expiry_ms")
end)
has_hammer_config and has_expiry_ms
else
false
end
rescue
_ -> false
end
# Add Hammer backend configuration to config.exs
defp add_hammer_backend_config(igniter) do
hammer_config = """
# Configure rate limiting with Hammer
config :hammer,
backend:
{Hammer.Backend.ETS,
[
# Cleanup expired rate limit buckets every 60 seconds
expiry_ms: 60_000,
# Cleanup interval (1 minute)
cleanup_interval_ms: 60_000
]}
"""
try do
Igniter.update_file(igniter, "config/config.exs", fn source ->
content = Rewrite.Source.get(source, :content)
# Check if Hammer config already exists
if String.contains?(content, "config :hammer") do
source
else
# Find insertion point before import_config statements
insertion_point = find_import_config_location(content)
updated_content =
case insertion_point do
{:before_import, before_content, after_content} ->
# Insert before import_config
before_content <> hammer_config <> "\n" <> after_content
:append_to_end ->
# No import_config found, append to end
content <> hammer_config
end
Rewrite.Source.update(source, :content, updated_content)
end
end)
rescue
e ->
IO.warn("Failed to add Hammer configuration: #{inspect(e)}")
add_manual_config_notice(igniter, :hammer)
end
end
# Add PhoenixKit rate limiter configuration to config.exs
defp add_phoenix_kit_rate_limiter_config(igniter) do
rate_limiter_config = """
# Configure rate limits for authentication endpoints
config :phoenix_kit, PhoenixKit.Users.RateLimiter,
# Login: 5 attempts per minute per email
login_limit: 5,
login_window_ms: 60_000,
# Magic link: 3 requests per 5 minutes per email
magic_link_limit: 3,
magic_link_window_ms: 300_000,
# Password reset: 3 requests per 5 minutes per email
password_reset_limit: 3,
password_reset_window_ms: 300_000,
# Registration: 3 attempts per hour per email
registration_limit: 3,
registration_window_ms: 3_600_000,
# Registration IP: 10 attempts per hour per IP
registration_ip_limit: 10,
registration_ip_window_ms: 3_600_000
"""
try do
Igniter.update_file(igniter, "config/config.exs", fn source ->
content = Rewrite.Source.get(source, :content)
# Check if PhoenixKit rate limiter config already exists
if String.contains?(content, "config :phoenix_kit, PhoenixKit.Users.RateLimiter") do
source
else
# Find insertion point before import_config statements
insertion_point = find_import_config_location(content)
updated_content =
case insertion_point do
{:before_import, before_content, after_content} ->
# Insert before import_config
before_content <> rate_limiter_config <> "\n" <> after_content
:append_to_end ->
# No import_config found, append to end
content <> rate_limiter_config
end
Rewrite.Source.update(source, :content, updated_content)
end
end)
rescue
e ->
IO.warn("Failed to add PhoenixKit rate limiter configuration: #{inspect(e)}")
add_manual_config_notice(igniter, :rate_limiter)
end
end
# Find the location to insert config before import_config statements
defp find_import_config_location(content) do
lines = String.split(content, "\n")
# Look for import_config pattern
import_index =
Enum.find_index(lines, fn line ->
trimmed = String.trim(line)
String.starts_with?(trimmed, "import_config") or String.contains?(line, "import_config")
end)
case import_index do
nil ->
# No import_config found, append to end
:append_to_end
index ->
# Find the start of the import_config block
start_index = find_import_block_start(lines, index)
# Split content at the start of import block
before_lines = Enum.take(lines, start_index)
after_lines = Enum.drop(lines, start_index)
before_content = Enum.join(before_lines, "\n")
after_content = Enum.join(after_lines, "\n")
{:before_import, before_content, after_content}
end
end
# Find the start of the import_config block (including preceding comments)
defp find_import_block_start(lines, import_index) do
lines
|> Enum.take(import_index)
|> Enum.reverse()
|> Enum.reduce_while(import_index, fn line, current_index ->
trimmed = String.trim(line)
cond do
# Comment line related to import
String.starts_with?(trimmed, "#") and
(String.contains?(line, "import") or String.contains?(line, "Import") or
String.contains?(line, "bottom") or String.contains?(line, "BOTTOM") or
String.contains?(line, "environment")) ->
{:cont, current_index - 1}
# Blank line
trimmed == "" ->
{:cont, current_index - 1}
# config_env or similar
String.contains?(line, "config_env()") or String.contains?(line, "env_config") ->
{:cont, current_index - 1}
# Stop at any other code
true ->
{:halt, current_index}
end
end)
end
# Add notice about rate limiter configuration
defp add_rate_limiter_notice(igniter) do
if hammer_config_exists?(igniter) do
Igniter.add_notice(
igniter,
"🛡️ Rate limiting configured (Hammer + PhoenixKit.Users.RateLimiter)"
)
else
Igniter.add_notice(
igniter,
"⚠️ Rate limiting configuration added - restart your server if running"
)
end
end
# Add notice when manual configuration is required
defp add_manual_config_notice(igniter, :hammer) do
notice = """
⚠️ Manual Configuration Required: Hammer
PhoenixKit couldn't automatically configure Hammer rate limiting.
Please add the following to config/config.exs:
config :hammer,
backend:
{Hammer.Backend.ETS,
[
expiry_ms: 60_000,
cleanup_interval_ms: 60_000
]}
Without this configuration, your application will fail to start.
"""
Igniter.add_notice(igniter, notice)
end
defp add_manual_config_notice(igniter, :rate_limiter) do
notice = """
⚠️ Manual Configuration Required: PhoenixKit Rate Limiter
PhoenixKit couldn't automatically configure rate limits.
Please add the following to config/config.exs:
config :phoenix_kit, PhoenixKit.Users.RateLimiter,
login_limit: 5,
login_window_ms: 60_000,
magic_link_limit: 3,
magic_link_window_ms: 300_000,
password_reset_limit: 3,
password_reset_window_ms: 300_000,
registration_limit: 3,
registration_window_ms: 3_600_000,
registration_ip_limit: 10,
registration_ip_window_ms: 3_600_000
"""
Igniter.add_notice(igniter, notice)
end
end