defmodule Ngrok do
@moduledoc """
By including this supervisor in your application, an Ngrok process will be
started when your application starts and stopped when your application stops
## Options
* `port` (`integer` / `required`) - The port to tunnel.
* `protocol` (`atom` / default: `https`) - The protocol to tunnel.
(`http` / `https` / `tcp` / `tls`)
* `additional_arguments` (`[String.t()]` / default: `[]`) - Additional options
to pass to `ngrok`.
* `name` (Supervisor name / default: `Ngrok`) - Name of the supervisor in your
application.
* `api_url` (url / default: `http://localhost:4040/api/tunnels`) - The URL of
Ngrok's API (used to retrieve its settings).
* `sleep_between_attempts` (timeout (ms) / default: 200) - The amount of sleep
(in ms) to put between attempts to connect to Ngrok
## Config
config :ngrok,
# The name / path of the Ngrok executable
executable: "ngrok"
"""
use Supervisor
alias Ngrok.Executable
alias Ngrok.Settings
@type destination_port :: pos_integer()
@type protocol :: :http | :https | :tcp | :tls
@type strict_name :: atom() | {:global, atom()}
@type start_opts :: [
port: destination_port(),
additional_arguments: [String.t()],
protocol: protocol(),
api_url: String.t(),
sleep_between_attempts: timeout(),
name: strict_name()
]
@spec start_link(opts :: start_opts()) :: Supervisor.on_start()
def start_link(opts),
do:
Supervisor.start_link(
__MODULE__,
Keyword.take(opts, [
:port,
:additional_arguments,
:protocol,
:api_url,
:name,
:sleep_between_attempts
]),
name: Keyword.get(opts, :name, __MODULE__)
)
@impl Supervisor
def init(opts),
do:
Supervisor.init(
[
{Executable, executable_config(opts)},
{Settings, settings_config(opts)}
],
strategy: :rest_for_one
)
@doc """
Retrieves the public URL of the Ngrok tunnel
## Example
Ngrok.public_url # => http://(.*).ngrok.io/
"""
@spec public_url(name :: strict_name()) :: String.t()
def public_url(name \\ __MODULE__),
do: name |> extend_name(Settings) |> Settings.get("public_url")
@spec executable_config(start_opts()) :: Executable.start_opts()
defp executable_config(opts) do
[
port: Keyword.fetch!(opts, :port),
additional_arguments: Keyword.get(opts, :additional_arguments, []),
protocol: Keyword.get(opts, :protocol, :https),
name: opts |> Keyword.get(:name, __MODULE__) |> extend_name(Executable)
]
end
@spec settings_config(start_opts()) :: Settings.start_opts()
defp settings_config(opts) do
[
port: Keyword.fetch!(opts, :port),
protocol: Keyword.get(opts, :protocol, :https),
api_url: Keyword.get(opts, :api_url, "http://localhost:4040/api/tunnels"),
sleep_between_attempts: Keyword.get(opts, :sleep_between_attempts, 200),
name: opts |> Keyword.get(:name, __MODULE__) |> extend_name(Settings)
]
end
@spec extend_name(name :: strict_name(), extend :: atom()) :: strict_name()
defp extend_name(name, extend)
# credo:disable-for-lines:2 Credo.Check.Warning.UnsafeToAtom
defp extend_name({:global, name}, extend) when is_atom(name),
do: {:global, Module.concat(name, extend)}
# credo:disable-for-next-line Credo.Check.Warning.UnsafeToAtom
defp extend_name(name, extend) when is_atom(name), do: Module.concat(name, extend)
end