Hey everyone!
It's been quite a while since I last wrote here. I've been busy developing games with my free-time company balconygames. We've made good progress, though the market remains challenging due to the high costs of user acquisition.
Today, I'd like to share an example of a proxy application for a Phoenix umbrella project. This might be useful for those facing similar issues with code reloading and WebSocket support. The example has four apps inside the apps/
directory: proxy
, web
(Phoenix), admin
(Phoenix), and db
(shared Ecto project).
Without further ado, here's the implementation:
defmodule YourApp.Proxy.Application do@moduledoc falseuse Applicationalias Phoenix.LiveReloader.Socket, as: LiveReloadSocketrequire Loggerdef start(_type, _args) doimport Supervisor.Spec, warn: falserequire_code_reloading?(:your_app_web, YourAppWeb.Endpoint)opts = [strategy: :one_for_one, name: YourApp.Proxy.Supervisor]Supervisor.start_link(children(Mix.env()), opts)enddef children(:test), do: []def children(_) doproxy_children = [{Plug.Cowboy,scheme: :http,plug: YourApp.Proxy.Builder,port: port(),dispatch: [{:_,[# your_app_webweb_phoenix_live_reload(),web_phoenix_live_view(),web_websocket(),# your_app_adminadmin_phoenix_live_reload(),admin_phoenix_live_view(),admin_websocket(),# proxymaster_proxy()]}]}]# - proxy endpoints# - /api endpoints# - /dev/admin pages# - web watchers for webpacker and code reloading# - admin watchers for webpacker and code reloadingchildren =proxy_children ++watcher_children(:your_app_web,YourAppWeb.Endpoint) ++watcher_children(:your_app_admin_web,YourAppAdminWeb.Endpoint)childrenenddefp watcher_children(otp_app, mod) doconf = Application.get_env(otp_app, mod)unless Mix.env() == :test doEnum.map(conf[:watchers], fn {cmd, args} ->{Phoenix.Endpoint.Watcher, watcher_args(cmd, args)}end)else[]endenddefp watcher_args(cmd, cmd_args) do{args, opts} = Enum.split_while(cmd_args, &is_binary(&1)){cmd, args, opts}enddef port do(System.get_env("PORT") || "4000") |> String.to_integer()enddef web_phoenix_live_reload dowebsocket_handler("/phoenix/live_reload/socket/websocket",YourAppWeb.Endpoint,{LiveReloadSocket, :websocket})enddef web_phoenix_live_view dowebsocket_handler("/live",YourAppWeb.Endpoint,{Phoenix.LiveView.Socket, :websocket})enddef web_websocket dowebsocket_handler("/cable/websocket",YourAppWeb.Endpoint,{YourAppWeb.UserSocket, websocket: true})enddef admin_phoenix_live_reload dowebsocket_handler("/dev/admin/phoenix/live_reload/socket/websocket",YourAppAdminWeb.Endpoint,{LiveReloadSocket, :websocket})enddef admin_phoenix_live_view dowebsocket_handler("/dev/admin/live",YourAppAdminWeb.Endpoint,{Phoenix.LiveView.Socket, :websocket})enddef admin_websocket dowebsocket_handler("/dev/admin/websocket",YourAppAdminWeb.Endpoint,{YourAppAdminWeb.UserSocket, websocket: true})enddef master_proxy do{:_, Plug.Cowboy.Handler, {YourApp.Proxy.Plug, []}}enddefp websocket_handler(path, endpoint, options) do{path, Phoenix.Endpoint.Cowboy2Handler, {endpoint, options}}enddefp require_code_reloading?(otp_app, mod) doconf = Application.get_env(otp_app, mod)if conf[:code_reloader] doPhoenix.CodeReloader.Server.check_symlinks()endendend
That's it! This proxy application handles routing for both your web and admin Phoenix applications, while properly supporting WebSockets, LiveView, and code reloading.