defmodule BDS.AI.SettingsStore do @moduledoc false import Ecto.Query alias BDS.AI.CatalogMeta alias BDS.AI.SecretBackend alias BDS.Persistence alias BDS.Repo alias BDS.Settings.Setting @spec get_setting(String.t()) :: String.t() | nil def get_setting(key) do case Repo.get(Setting, key) do nil -> nil setting -> setting.value end end @spec put_setting(String.t(), String.t()) :: :ok def put_setting(key, value) when is_binary(key) and is_binary(value) do now = Persistence.now_ms() (%Setting{} |> Setting.changeset(%{key: key, value: value, updated_at: now})) |> Repo.insert( on_conflict: [set: [value: value, updated_at: now]], conflict_target: [:key] ) :ok end @spec delete_setting(String.t()) :: :ok def delete_setting(key) do Repo.delete_all(from(setting in Setting, where: setting.key == ^key)) :ok end @spec put_secret(String.t(), String.t() | nil, module()) :: :ok | {:error, term()} def put_secret(_key, nil, _backend), do: :ok def put_secret(key, value, backend) do with {:ok, encrypted_value} <- backend.encrypt(value) do put_setting(encrypted_key(key), encrypted_value) end end @spec get_secret(String.t() | nil, module()) :: {:ok, String.t() | nil} | {:error, term()} def get_secret(nil, _backend), do: {:ok, nil} def get_secret(value, backend), do: backend.decrypt(value) @spec encrypted_key(String.t()) :: String.t() def encrypted_key(key), do: "__encrypted_#{key}" @spec secret_backend() :: module() def secret_backend, do: SecretBackend @spec get_catalog_meta_value(String.t()) :: String.t() | nil def get_catalog_meta_value(key) do case Repo.get(CatalogMeta, key) do nil -> get_setting("ai.catalog.#{key}") meta -> meta.value end end @spec put_catalog_meta(String.t(), String.t()) :: :ok def put_catalog_meta(key, value) do %CatalogMeta{} |> CatalogMeta.changeset(%{key: key, value: value}) |> Repo.insert( on_conflict: [set: [value: value]], conflict_target: [:key] ) :ok end end