Current section
Files
Jump to
Current section
Files
lib/nex/env.ex
defmodule Nex.Env do
@moduledoc """
Environment variable management.
Loads from .env files and system environment.
"""
@doc "Initialize environment from .env files"
def init do
env = Mix.env() |> to_string()
# Get project root directory (parent of _build)
project_root =
case Mix.Project.app_path() do
nil -> File.cwd!()
app_path ->
# app_path is like "_build/dev/lib/chatbot"
# We need to go up to project root
app_path
|> Path.dirname() # _build/dev/lib
|> Path.dirname() # _build/dev
|> Path.dirname() # _build
|> Path.dirname() # project root
end
files =
[".env", ".env.#{env}"]
|> Enum.map(&Path.join(project_root, &1))
|> Enum.filter(&File.exists?/1)
if files != [] do
IO.puts("[Nex.Env] Loading environment from: #{Enum.join(files, ", ")}")
case Dotenvy.source(files) do
{:ok, vars} ->
# Set all loaded variables to system environment
Enum.each(vars, fn {key, value} ->
System.put_env(key, value)
end)
IO.puts("[Nex.Env] ✓ Environment loaded successfully (#{map_size(vars)} variables)")
{:error, reason} ->
IO.puts("[Nex.Env] ✗ Failed to load environment: #{inspect(reason)}")
end
else
IO.puts("[Nex.Env] ⚠ No .env files found in: #{project_root}")
end
:ok
end
@doc "Get environment variable with optional default"
def get(key, default \\ nil) when is_atom(key) do
key_str = key |> to_string() |> String.upcase()
System.get_env(key_str) || default
end
@doc "Get required environment variable (raises if missing)"
def get!(key) when is_atom(key) do
case get(key) do
nil -> raise "Missing required environment variable: #{key}"
value -> value
end
end
@doc "Get environment variable as integer"
def get_integer(key, default) when is_atom(key) do
case get(key) do
nil -> default
value -> String.to_integer(value)
end
end
@doc "Get environment variable as boolean"
def get_boolean(key, default) when is_atom(key) do
case get(key) do
nil -> default
"true" -> true
"1" -> true
_ -> false
end
end
end