Packages
phoenix_kit
1.7.93
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
Current section
Files
lib/phoenix_kit_web/plugs/integration.ex
defmodule PhoenixKitWeb.Plugs.Integration do
@moduledoc """
Central integration plug for PhoenixKit.
This plug serves as a single entry point for all PhoenixKit plugs that need to run
in the parent application's browser pipeline. It coordinates multiple sub-plugs and
ensures they run in the correct order.
## Current Features
- **Maintenance Mode**: Intercepts requests when maintenance mode is enabled
- **WebSocket Transport Fix**: Clears cached LongPoll fallback preferences to ensure
WebSocket is always tried first, providing much better LiveView performance
## Future Extensions
This plug can be extended to include additional PhoenixKit features such as:
- Rate limiting
- Security headers
- Analytics tracking
- Custom middleware
## Usage
This plug is automatically added to your `:browser` pipeline during installation:
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :put_root_layout, html: {MyAppWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
plug PhoenixKitWeb.Plugs.Integration # ← Added automatically
end
## Performance
Each sub-plug is optimized to be a no-op when its feature is disabled, ensuring
zero performance impact when features are not in use.
"""
import Plug.Conn
alias PhoenixKitWeb.Plugs.MaintenanceMode
# Inline script to clear Phoenix LiveView transport cache.
# This ensures WebSocket is always tried first instead of using cached LongPoll fallback.
# Must run BEFORE app.js/LiveSocket initializes.
# IMPORTANT: Excludes 'phx:' prefixed keys (PhoenixKit features like phx:theme)
@websocket_fix_script """
<script>try{Object.keys(localStorage).filter(k=>k.includes('phx')&&!k.startsWith('phx:')).forEach(k=>localStorage.removeItem(k));Object.keys(sessionStorage).filter(k=>k.includes('phx')&&!k.startsWith('phx:')).forEach(k=>sessionStorage.removeItem(k))}catch(e){}</script>
"""
@doc """
Initializes the plug with options.
"""
def init(opts), do: opts
@doc """
Runs all PhoenixKit integration plugs in sequence.
"""
def call(conn, _opts) do
conn
|> run_maintenance_mode_check()
|> inject_websocket_fix()
# Future plugs can be added here:
# |> run_rate_limiter()
# |> run_security_headers()
end
# Run maintenance mode check
defp run_maintenance_mode_check(conn) do
MaintenanceMode.call(conn, [])
end
# Inject WebSocket transport fix script into HTML responses.
# This clears any cached LongPoll fallback preferences before LiveSocket initializes.
defp inject_websocket_fix(conn) do
register_before_send(conn, fn conn ->
# Only inject for HTML responses
content_type = get_resp_header(conn, "content-type") |> List.first() || ""
if String.contains?(content_type, "text/html") and conn.resp_body do
inject_script_into_head(conn)
else
conn
end
end)
end
# Inject the script right after <head> tag so it runs before any other scripts
# Uses simple string replacement instead of regex for better performance
defp inject_script_into_head(conn) do
body =
try do
IO.iodata_to_binary(conn.resp_body)
rescue
_ -> nil
end
# Skip if body couldn't be converted or isn't valid UTF-8
if is_nil(body) or not String.valid?(body) do
conn
else
# Try simple replacements first (faster than regex)
cond do
String.contains?(body, "<head>") ->
new_body =
String.replace(body, "<head>", "<head>" <> @websocket_fix_script, global: false)
%{conn | resp_body: new_body}
String.contains?(body, "<HEAD>") ->
new_body =
String.replace(body, "<HEAD>", "<HEAD>" <> @websocket_fix_script, global: false)
%{conn | resp_body: new_body}
true ->
# No simple <head> tag found, try regex for <head ...> with attributes
case Regex.run(~r/<head[^>]*>/i, body, return: :index) do
[{start, length}] ->
insert_pos = start + length
{before, after_head} = String.split_at(body, insert_pos)
new_body = before <> @websocket_fix_script <> after_head
%{conn | resp_body: new_body}
_ ->
conn
end
end
end
end
end