I’m developing an application that’s going to be (for the forseeable future) installed and managed in a pretty boring, standard way — not using bespoke containerization, clustering, or anything like that.
I’ve currently got this code to manage SECRET_KEY_BASE
, and it seems to work, but it also seems like a really messy hack:
# /config/runtime.exs
# …
if config_env() == :prod do
# …
secret_key_base =
System.get_env("SECRET_KEY_BASE")
|| Foo.Application.Util.app_secret_key_auto()
# …
end
# /lib/foo/util.ex
defmodule Foo.Application.Util do
# …
def app_secret_key_auto do
p = Path.expand("secret_key_base.txt", :filename.basedir(:user_config, "foo-app"))
# 1. Generate key if it doesn't exist
:ok = mkdir_p(Path.expand("..", p))
:ok = case File.open(p, [:write, :exclusive]) do # FIXME surely there must be some built-in tooling for this???
{:error, :eexist} -> :ok # happy path
{:ok, h} ->
case File.chmod(p, 0o600) do
:ok ->
# https://github.com/phoenixframework/phoenix/blob/v1.7.17/lib/mix/tasks/phx.gen.secret.ex#L17
data_ascii = (&:crypto.strong_rand_bytes(&1) |> Base.encode64(padding: false) |> binary_part(0, &1)).(64)
result = IO.write(h, data_ascii)
:ok = File.close(h)
result
{:error, e} ->
:ok = File.close(h)
{:error, e}
end
{:error, e} -> {:error, e}
end
# 2. load key
{:ok, data_ascii} = File.open(p, [:read], &Enum.fetch!(IO.stream(&1, :line), 0))
data_ascii
end
end
Is there any existing utility function or simple pattern that I should be using instead of this pile of spaghetti, or is Phoenix really just not meant to be used outside of cloud containers?
The documentation didn’t say much about it.