towerops/lib/towerops/settings/application_setting.ex

92 lines
2.5 KiB
Elixir

defmodule Towerops.Settings.ApplicationSetting do
@moduledoc """
Application-wide settings schema.
Stores global configuration values that affect the entire application,
regardless of organization. Only accessible by superadmins.
"""
use Ecto.Schema
import Ecto.Changeset
require Logger
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "application_settings" do
field :key, :string
field :value, :string
field :value_type, :string, default: "string"
field :description, :string
timestamps(type: :utc_datetime)
end
@type t :: %__MODULE__{
id: Ecto.UUID.t(),
key: String.t(),
value: String.t() | nil,
value_type: String.t(),
description: String.t() | nil,
inserted_at: DateTime.t(),
updated_at: DateTime.t()
}
@doc false
def changeset(setting, attrs) do
setting
|> cast(attrs, [:key, :value, :value_type, :description])
|> validate_required([:key, :value_type])
|> validate_inclusion(:value_type, ["string", "integer", "uuid", "boolean", "json", "decimal"])
|> unique_constraint(:key)
end
@doc """
Parses the value field according to value_type.
## Examples
iex> parse_value(%ApplicationSetting{value: "abc-123", value_type: "uuid"})
"abc-123"
iex> parse_value(%ApplicationSetting{value: nil, value_type: "uuid"})
nil
"""
def parse_value(%__MODULE__{value: nil}), do: nil
def parse_value(%__MODULE__{value: value, value_type: "string"}), do: value
def parse_value(%__MODULE__{value: value, value_type: "uuid"}), do: value
def parse_value(%__MODULE__{value: value, value_type: "integer"}) do
case Integer.parse(value) do
{int, _} -> int
:error -> nil
end
end
def parse_value(%__MODULE__{value: value, value_type: "boolean"}) do
value in ["true", "1", "yes"]
end
def parse_value(%__MODULE__{key: key, value: value, value_type: "json"}) do
case Jason.decode(value) do
{:ok, decoded} ->
decoded
{:error, reason} ->
Logger.warning("Failed to parse JSON value for setting '#{key}': #{inspect(reason)}")
nil
end
end
def parse_value(%__MODULE__{key: key, value: value, value_type: "decimal"}) do
case Decimal.parse(value) do
{decimal, _remainder} ->
decimal
:error ->
Logger.warning("Failed to parse decimal value for setting '#{key}': #{inspect(value)}")
nil
end
end
end