defmodule OhMyExOhIrcd.Release do
@moduledoc """
Used for executing release tasks when run in production without Mix
installed.
The `mix invites.*` / `mix opers.*` tasks are dev-only (Mix is not
shipped in the release). These functions are their prod equivalents —
run them with `eval` so the Repo is started on demand and output lands
in your terminal:
bin/oh_my_ex_oh_ircd eval 'OhMyExOhIrcd.Release.create_invite(note: "for alice")'
Prefer `eval` over `rpc` here: `rpc` runs on the live node, so any
printed output goes to the *server's* console, not yours.
"""
@app :oh_my_ex_oh_ircd
def migrate do
load_app()
for repo <- repos() do
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
end
end
def rollback(repo, version) do
load_app()
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
end
## Registration invites — prod equivalents of `mix invites.*`.
@doc """
Mint a single-use registration invite and print the token + signup URL.
bin/oh_my_ex_oh_ircd eval \
'OhMyExOhIrcd.Release.create_invite(note: "for alice", expires_in_days: 7)'
Accepts `:note` and `:expires_in_days` (mirrors `mix invites.create`).
"""
def create_invite(opts \\ []) do
opts =
case Keyword.pop(opts, :expires_in_days) do
{nil, rest} -> rest
{days, rest} -> Keyword.put(rest, :expires_in_seconds, days * 24 * 3600)
end
with_started_repo(fn ->
case OhMyExOhIrcd.Accounts.create_invite(opts) do
{:ok, invite} ->
IO.puts("Token: #{invite.token}")
IO.puts("URL: #{base_url()}/users/register?invite=#{invite.token}")
if invite.expires_at, do: IO.puts("Expires: #{invite.expires_at}")
:ok
{:error, cs} ->
IO.puts("Couldn't create invite: #{inspect(cs.errors)}")
{:error, cs}
end
end)
end
@doc "List active (unconsumed) invites. Prod equivalent of `mix invites.list`."
def list_invites do
with_started_repo(fn ->
case OhMyExOhIrcd.Accounts.list_active_invites() do
[] ->
IO.puts("(no active invites)")
invites ->
Enum.each(invites, fn i ->
IO.puts(
"#{String.slice(i.token, 0, 8)}… #{i.note || ""} expires=#{i.expires_at || "never"}"
)
end)
end
end)
end
## IRC operators — prod equivalents of `mix opers.*`.
@doc "Flag an account (by email) as an IRC operator. See `mix opers.grant`."
def grant_oper(email), do: set_oper(email, true)
@doc "Clear an account's IRC-operator flag. See `mix opers.revoke`."
def revoke_oper(email), do: set_oper(email, false)
@doc "List accounts flagged as IRC operators. See `mix opers.list`."
def list_opers do
import Ecto.Query
with_started_repo(fn ->
OhMyExOhIrcd.Repo.all(
from u in OhMyExOhIrcd.Accounts.User,
where: u.is_oper == true,
order_by: [asc: u.email]
)
|> case do
[] -> IO.puts("(no IRC operators flagged)")
users -> Enum.each(users, &IO.puts(&1.email))
end
end)
end
defp set_oper(email, flag) do
with_started_repo(fn ->
case OhMyExOhIrcd.Accounts.get_user_by_email(email) do
nil ->
IO.puts("No account with email #{email}")
{:error, :not_found}
user ->
case OhMyExOhIrcd.Accounts.set_oper(user, flag) do
{:ok, updated} ->
IO.puts("#{updated.email}: is_oper=#{updated.is_oper}")
:ok
{:error, cs} ->
IO.puts("Update failed: #{inspect(cs.errors)}")
{:error, cs}
end
end
end)
end
# Build the public base URL from the Endpoint's `:url` config without
# starting the Endpoint process (we run under `eval`, where it isn't up).
defp base_url do
url = Application.get_env(@app, OhMyExOhIrcdWeb.Endpoint, []) |> Keyword.get(:url, [])
scheme = Keyword.get(url, :scheme, "http")
host = Keyword.get(url, :host, "localhost")
port = Keyword.get(url, :port, if(scheme == "https", do: 443, else: 80))
default = if scheme == "https", do: 443, else: 80
if port == default, do: "#{scheme}://#{host}", else: "#{scheme}://#{host}:#{port}"
end
# Start just the Repo (and its deps) for the duration of `fun`, then
# shut it back down — the same trick `migrate/0` uses, so these helpers
# work under `eval` on a node that isn't running the full app.
defp with_started_repo(fun) do
load_app()
{:ok, result, _apps} = Ecto.Migrator.with_repo(OhMyExOhIrcd.Repo, fn _repo -> fun.() end)
result
end
defp repos do
Application.fetch_env!(@app, :ecto_repos)
end
defp load_app do
# Many platforms require SSL when connecting to the database
Application.ensure_all_started(:ssl)
Application.ensure_loaded(@app)
end
end
neiam
/oh-my-ex-oh-ircd
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
4.9 KiB · text
History
6de4808