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
### 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

View File

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

View File

@ -13,30 +13,27 @@ defmodule SimpleshopTheme.Secrets do
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 """
Loads all secrets from the database into Application env.
Called at startup from the supervision tree, after the Repo is ready.
"""
def load_all do
load_stripe_config()
for {settings_key, app, env_key} <- @secret_registry do
case Settings.get_secret(settings_key) do
nil ->
:skip
value ->
Application.put_env(app, env_key, value)
Logger.debug("Loaded #{settings_key} from database")
end
@doc """
Loads Stripe credentials from encrypted settings into Application env.
"""
def load_stripe_config do
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
:ok

View File

@ -18,9 +18,9 @@ defmodule SimpleshopTheme.Settings do
"""
def get_setting(key, default \\ nil) do
case Repo.get_by(Setting, key: key) do
nil -> default
setting -> decode_value(setting)
case fetch_setting(key) do
{:ok, setting} -> decode_value(setting)
:not_found -> default
end
end
@ -120,9 +120,9 @@ defmodule SimpleshopTheme.Settings do
Deletes a setting by key.
"""
def delete_setting(key) do
case Repo.get_by(Setting, key: key) do
nil -> :ok
setting -> Repo.delete(setting)
case fetch_setting(key) do
{:ok, setting} -> Repo.delete(setting)
:not_found -> :ok
end
end
@ -157,8 +157,9 @@ defmodule SimpleshopTheme.Settings do
Returns the plaintext value or the default if not found.
"""
def get_secret(key, default \\ nil) do
case Repo.get_by(Setting, key: key) do
%Setting{value_type: "encrypted", encrypted_value: encrypted} when not is_nil(encrypted) ->
case fetch_setting(key) do
{:ok, %Setting{value_type: "encrypted", encrypted_value: encrypted}}
when not is_nil(encrypted) ->
case Vault.decrypt(encrypted) do
{:ok, plaintext} -> plaintext
{:error, _} -> default
@ -173,8 +174,9 @@ defmodule SimpleshopTheme.Settings do
Checks whether an encrypted secret exists in the database.
"""
def has_secret?(key) do
case Repo.get_by(Setting, key: key) do
%Setting{value_type: "encrypted", encrypted_value: encrypted} when not is_nil(encrypted) ->
case fetch_setting(key) do
{:ok, %Setting{value_type: "encrypted", encrypted_value: encrypted}}
when not is_nil(encrypted) ->
true
_ ->
@ -204,6 +206,13 @@ defmodule SimpleshopTheme.Settings do
# 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})
when not is_nil(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
{:ok, result} ->
Secrets.load_stripe_config()
Secrets.load_all()
{:ok, result}
{:error, reason} ->
# Key is valid and stored, but webhook creation failed.
# Still load the key so checkout works (webhooks can be set up manually).
Secrets.load_stripe_config()
Secrets.load_all()
{:error, reason}
end
end
@ -68,7 +68,7 @@ defmodule SimpleshopTheme.Stripe.Setup do
"""
def save_signing_secret(signing_secret) do
Settings.put_secret("stripe_signing_secret", signing_secret)
Secrets.load_stripe_config()
Secrets.load_all()
end
@doc """