refactor: consolidate settings lookups and secrets loading

- Extract fetch_setting/1 in Settings (4 callsites → 1 repo lookup)
- Replace hardcoded load_stripe_config with registry-driven load_all
- Adding new secrets is now a one-line @secret_registry entry
- Mark DRY refactor plan as complete (all 8 items done)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey 2026-02-08 14:42:13 +00:00
parent 3eacd91fda
commit 3b8d5faf3b
5 changed files with 39 additions and 33 deletions

View File

@ -202,9 +202,9 @@ See: [ROADMAP.md](ROADMAP.md) for design notes
See: [docs/plans/products-context.md](docs/plans/products-context.md) for schema design See: [docs/plans/products-context.md](docs/plans/products-context.md) for schema design
### DRY Refactor ### DRY Refactor
**Status:** Planned **Status:** Complete
Codebase analysis identified ~380 lines of duplication across LiveViews, templates, and the theme editor. Top priorities: extract `ThemeHook` for shared mount logic, extract `<.shop_layout>` wrapper component, consolidate preview assigns. Also: split `shop_components.ex` (4,400 lines) into focused modules. All 8 items from the plan done. Key wins: ThemeHook eliminated mount duplication, shop_layout saved ~195 lines, shop_components split into 5 focused modules (largest file dropped from 4,487 to ~1,600 lines), Settings repo lookups consolidated via `fetch_setting/1`, secrets loading made scalable via registry pattern.
See: [docs/plans/dry-refactor.md](docs/plans/dry-refactor.md) for full analysis and plan See: [docs/plans/dry-refactor.md](docs/plans/dry-refactor.md) for full analysis and plan

View File

@ -1,6 +1,6 @@
# DRY refactor plan # DRY refactor plan
Status: Planned Status: Complete
Codebase analysis identifying repeated patterns and consolidation opportunities. Ordered by impact. Codebase analysis identifying repeated patterns and consolidation opportunities. Ordered by impact.

View File

@ -13,30 +13,27 @@ defmodule SimpleshopTheme.Secrets do
require Logger require Logger
# Registry of {settings_key, app, env_key} — add new secrets here
@secret_registry [
{"stripe_api_key", :stripity_stripe, :api_key},
{"stripe_signing_secret", :stripity_stripe, :signing_secret}
]
@doc """ @doc """
Loads all secrets from the database into Application env. Loads all secrets from the database into Application env.
Called at startup from the supervision tree, after the Repo is ready. Called at startup from the supervision tree, after the Repo is ready.
""" """
def load_all do def load_all do
load_stripe_config() for {settings_key, app, env_key} <- @secret_registry do
end case Settings.get_secret(settings_key) do
nil ->
:skip
@doc """ value ->
Loads Stripe credentials from encrypted settings into Application env. Application.put_env(app, env_key, value)
""" Logger.debug("Loaded #{settings_key} from database")
def load_stripe_config do end
api_key = Settings.get_secret("stripe_api_key")
signing_secret = Settings.get_secret("stripe_signing_secret")
if api_key do
Application.put_env(:stripity_stripe, :api_key, api_key)
Logger.debug("Stripe API key loaded from database")
end
if signing_secret do
Application.put_env(:stripity_stripe, :signing_secret, signing_secret)
Logger.debug("Stripe webhook secret loaded from database")
end end
:ok :ok

View File

@ -18,9 +18,9 @@ defmodule SimpleshopTheme.Settings do
""" """
def get_setting(key, default \\ nil) do def get_setting(key, default \\ nil) do
case Repo.get_by(Setting, key: key) do case fetch_setting(key) do
nil -> default {:ok, setting} -> decode_value(setting)
setting -> decode_value(setting) :not_found -> default
end end
end end
@ -120,9 +120,9 @@ defmodule SimpleshopTheme.Settings do
Deletes a setting by key. Deletes a setting by key.
""" """
def delete_setting(key) do def delete_setting(key) do
case Repo.get_by(Setting, key: key) do case fetch_setting(key) do
nil -> :ok {:ok, setting} -> Repo.delete(setting)
setting -> Repo.delete(setting) :not_found -> :ok
end end
end end
@ -157,8 +157,9 @@ defmodule SimpleshopTheme.Settings do
Returns the plaintext value or the default if not found. Returns the plaintext value or the default if not found.
""" """
def get_secret(key, default \\ nil) do def get_secret(key, default \\ nil) do
case Repo.get_by(Setting, key: key) do case fetch_setting(key) do
%Setting{value_type: "encrypted", encrypted_value: encrypted} when not is_nil(encrypted) -> {:ok, %Setting{value_type: "encrypted", encrypted_value: encrypted}}
when not is_nil(encrypted) ->
case Vault.decrypt(encrypted) do case Vault.decrypt(encrypted) do
{:ok, plaintext} -> plaintext {:ok, plaintext} -> plaintext
{:error, _} -> default {:error, _} -> default
@ -173,8 +174,9 @@ defmodule SimpleshopTheme.Settings do
Checks whether an encrypted secret exists in the database. Checks whether an encrypted secret exists in the database.
""" """
def has_secret?(key) do def has_secret?(key) do
case Repo.get_by(Setting, key: key) do case fetch_setting(key) do
%Setting{value_type: "encrypted", encrypted_value: encrypted} when not is_nil(encrypted) -> {:ok, %Setting{value_type: "encrypted", encrypted_value: encrypted}}
when not is_nil(encrypted) ->
true true
_ -> _ ->
@ -204,6 +206,13 @@ defmodule SimpleshopTheme.Settings do
# Private helpers # Private helpers
defp fetch_setting(key) do
case Repo.get_by(Setting, key: key) do
nil -> :not_found
setting -> {:ok, setting}
end
end
defp decode_value(%Setting{value_type: "encrypted", encrypted_value: encrypted}) defp decode_value(%Setting{value_type: "encrypted", encrypted_value: encrypted})
when not is_nil(encrypted) do when not is_nil(encrypted) do
case Vault.decrypt(encrypted) do case Vault.decrypt(encrypted) do

View File

@ -36,13 +36,13 @@ defmodule SimpleshopTheme.Stripe.Setup do
case maybe_create_webhook(api_key) do case maybe_create_webhook(api_key) do
{:ok, result} -> {:ok, result} ->
Secrets.load_stripe_config() Secrets.load_all()
{:ok, result} {:ok, result}
{:error, reason} -> {:error, reason} ->
# Key is valid and stored, but webhook creation failed. # Key is valid and stored, but webhook creation failed.
# Still load the key so checkout works (webhooks can be set up manually). # Still load the key so checkout works (webhooks can be set up manually).
Secrets.load_stripe_config() Secrets.load_all()
{:error, reason} {:error, reason}
end end
end end
@ -68,7 +68,7 @@ defmodule SimpleshopTheme.Stripe.Setup do
""" """
def save_signing_secret(signing_secret) do def save_signing_secret(signing_secret) do
Settings.put_secret("stripe_signing_secret", signing_secret) Settings.put_secret("stripe_signing_secret", signing_secret)
Secrets.load_stripe_config() Secrets.load_all()
end end
@doc """ @doc """