Skip to content

Instantly share code, notes, and snippets.

@parndt
Last active July 22, 2024 07:56
Show Gist options
  • Save parndt/5e1dd05afff91f2fe0b8d2a08e432c82 to your computer and use it in GitHub Desktop.
Save parndt/5e1dd05afff91f2fe0b8d2a08e432c82 to your computer and use it in GitHub Desktop.
Proxy a Ruby app from an Elixir app
defmodule App.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
@impl true
def start(_type, _args) do
children =
ecto_repos(App.config_env()) ++
[
# Start the PubSub system
{Phoenix.PubSub, name: App.PubSub}
# Start a worker by calling: App.Worker.start_link(arg)
# {App.Worker, arg}
] ++
extra_children(
App.config_env(),
(System.get_env("API_PROD_ENDPOINT") || "") |> String.split(".") |> List.last()
)
Supervisor.start_link(children, strategy: :one_for_one, name: App.Supervisor)
end
def ecto_repos(:test), do: [App.Repo]
def ecto_repos(_), do: [App.Repo, App.Repo.Replica]
defp extra_children(:dev, _), do: all_extra_children()
defp extra_children(:prod, "localhost"), do: all_extra_children()
defp extra_children(_, _), do: []
defp all_extra_children do
[
Supervisor.child_spec(
{App.Ports.Commander,
[
command: "../bin/monitor.sh ./node_modules/.bin/ts-node ./scripts/build development",
args: [
{:cd, "web"},
{:env, [{'TS_NODE_PROJECT', 'tsconfig.webpack.json'}, {'PORT', '8080'}]}
]
]},
id: :build_assets
),
Supervisor.child_spec(
{App.Ports.Commander,
[
command: "../bin/monitor.sh ./node_modules/.bin/serve -C ./tmp",
args: [
{:cd, "web"},
{:env, [{'PORT', '8080'}]}
]
]},
id: :assets
),
Supervisor.child_spec(
{App.Ports.Commander,
[
command: "bin/monitor.sh caddy run --watch"
]},
id: :caddy
),
Supervisor.child_spec(
{App.Ports.Commander,
[
args: [{:cd, "web"}],
command: "../bin/monitor.sh bin/puma"
]},
id: :puma
)
]
end
end
app.localhost {
reverse_proxy http://127.0.0.1:9393
tls ./app.localhost+5.pem ./app.localhost+5-key.pem
}
admin.app.localhost {
reverse_proxy http://127.0.0.1:9292
}
api.app.localhost {
reverse_proxy http://127.0.0.1:3456
}
assets.app.localhost {
reverse_proxy http://127.0.0.1:8080
}
# lib/app/ports/commander.ex
defmodule App.Ports.Commander do
use GenServer
require Logger
# GenServer API
def start_link(args \\ [], opts \\ []) do
GenServer.start_link(__MODULE__, args, opts)
end
def init(args \\ []) do
Process.flag(:trap_exit, true)
port = Port.open({:spawn, args[:command]}, [:binary, :exit_status] ++ (args[:args] || []))
Port.monitor(port)
{:ok, %{port: port, latest_output: nil, exit_status: nil}}
end
# This callback handles data incoming from the command's STDOUT
def handle_info({_port, {:data, text_line}}, state) do
Logger.info("Latest output: #{inspect(text_line)}")
{:noreply, %{state | latest_output: String.trim(text_line)}}
end
# This callback tells us when the process exits
def handle_info({_port, {:exit_status, status}}, state) do
Logger.info("External exit: :exit_status: #{status}")
{:noreply, %{state | exit_status: status}}
end
def handle_info({:DOWN, _ref, :port, port, :normal}, state) do
Logger.info("Handled :DOWN message from port: #{inspect(port)}")
{:noreply, state}
end
# no-op catch-all callback for unhandled messages
def handle_info(msg, state) do
Logger.info("Unhandled message: #{inspect(msg)}")
{:noreply, state}
end
def terminate(reason, %{port: port} = state) do
Logger.info(
"** TERMINATE: #{inspect(reason)}. This is the last chance to clean up after this process."
)
Logger.info("Final state: #{inspect(state)}")
port_info = Port.info(port)
os_pid = port_info[:os_pid]
Logger.warn("Orphaned OS process: #{os_pid}")
:normal
end
end
# bin/monitor.sh
#!/usr/bin/env bash
# Start the program in the background
exec "$@" &
pid1=$!
# Silence warnings from here on
exec >/dev/null 2>&1
# Read from stdin in the background and
# kill running program when stdin closes
exec 0<&0 $(
while read; do :; done
kill -KILL $pid1
) &
pid2=$!
# Clean up
wait $pid1
ret=$?
kill -KILL $pid2
exit $ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment