improve setup UX: password field, setup hook, checklist banners, theme tweaks
All checks were successful
deploy / deploy (push) Successful in 1m31s
All checks were successful
deploy / deploy (push) Successful in 1m31s
- add password field and required shop name to setup wizard - extract SetupHook for DRY redirect to /setup when no admin exists - add ?from=checklist param to checklist hrefs with contextual banner on email settings and theme pages for easy return to dashboard - remove email warning banner from admin layout (checklist covers it) - make email a required checklist item (no longer optional) - add DevReset module for wiping dev data without restart - rename "Theme Studio" to "Theme", drop subtitle - lower theme editor side-by-side breakpoint from 64em to 48em - clean up login/registration pages (remove dead registration_open code) - fix settings.put_secret to invalidate cache after write Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,7 @@ defmodule Berrypod.Accounts do
|
||||
else
|
||||
%User{}
|
||||
|> User.email_changeset(attrs)
|
||||
|> User.password_changeset(attrs)
|
||||
|> Ecto.Changeset.put_change(:confirmed_at, DateTime.utc_now(:second))
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
78
lib/berrypod/dev_reset.ex
Normal file
78
lib/berrypod/dev_reset.ex
Normal file
@@ -0,0 +1,78 @@
|
||||
if Mix.env() == :dev do
|
||||
defmodule Berrypod.DevReset do
|
||||
@moduledoc """
|
||||
Dev-only helper to wipe all data and flush caches without restarting.
|
||||
|
||||
Usage from IEx or Tidewave eval:
|
||||
|
||||
Berrypod.DevReset.run()
|
||||
"""
|
||||
|
||||
alias Berrypod.Repo
|
||||
|
||||
# Tables in deletion order (children before parents)
|
||||
@tables ~w(
|
||||
order_items
|
||||
orders
|
||||
abandoned_carts
|
||||
product_images
|
||||
product_variants
|
||||
products
|
||||
provider_connections
|
||||
favicon_variants
|
||||
images
|
||||
users_tokens
|
||||
users
|
||||
pages
|
||||
analytics_events
|
||||
newsletter_subscribers
|
||||
newsletter_campaigns
|
||||
redirects
|
||||
broken_urls
|
||||
dead_links
|
||||
activity_log
|
||||
email_suppressions
|
||||
settings
|
||||
)
|
||||
|
||||
def run do
|
||||
# Reconnect the Repo in case mix ecto.reset replaced the DB file
|
||||
# while the server was running (old connections would point to a ghost file)
|
||||
Supervisor.terminate_child(Berrypod.Supervisor, Repo)
|
||||
Supervisor.restart_child(Berrypod.Supervisor, Repo)
|
||||
Process.sleep(100)
|
||||
|
||||
IO.puts("Wiping all data...")
|
||||
|
||||
Repo.query!("PRAGMA foreign_keys = OFF")
|
||||
|
||||
for table <- @tables do
|
||||
Repo.query!("DELETE FROM \"#{table}\"")
|
||||
end
|
||||
|
||||
# Clear Oban jobs too
|
||||
Repo.query!("DELETE FROM oban_jobs")
|
||||
|
||||
Repo.query!("PRAGMA foreign_keys = ON")
|
||||
|
||||
IO.puts("Flushing caches and runtime config...")
|
||||
|
||||
# ETS caches (invalidate_all clears computed/cached values too)
|
||||
Berrypod.Settings.SettingsCache.invalidate_all()
|
||||
Berrypod.Theme.CSSCache.invalidate()
|
||||
Berrypod.Pages.PageCache.invalidate_all()
|
||||
|
||||
# Redirects ETS table
|
||||
if :ets.whereis(:redirects_cache) != :undefined do
|
||||
:ets.delete_all_objects(:redirects_cache)
|
||||
end
|
||||
|
||||
# Runtime Application env
|
||||
Application.put_env(:berrypod, Berrypod.Mailer, adapter: Swoosh.Adapters.Local)
|
||||
Application.put_env(:swoosh, :api_client, false)
|
||||
|
||||
IO.puts("Done — visit /setup to start fresh")
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -202,23 +202,29 @@ defmodule Berrypod.Settings do
|
||||
The plaintext is encrypted via Vault before storage.
|
||||
"""
|
||||
def put_secret(key, plaintext) when is_binary(plaintext) do
|
||||
case Vault.encrypt(plaintext) do
|
||||
{:ok, encrypted} ->
|
||||
%Setting{key: key}
|
||||
|> Setting.changeset(%{
|
||||
key: key,
|
||||
value: "[encrypted]",
|
||||
value_type: "encrypted",
|
||||
encrypted_value: encrypted
|
||||
})
|
||||
|> Repo.insert(
|
||||
on_conflict: {:replace, [:value, :encrypted_value, :value_type, :updated_at]},
|
||||
conflict_target: :key
|
||||
)
|
||||
result =
|
||||
case Vault.encrypt(plaintext) do
|
||||
{:ok, encrypted} ->
|
||||
%Setting{key: key}
|
||||
|> Setting.changeset(%{
|
||||
key: key,
|
||||
value: "[encrypted]",
|
||||
value_type: "encrypted",
|
||||
encrypted_value: encrypted
|
||||
})
|
||||
|> Repo.insert(
|
||||
on_conflict: {:replace, [:value, :encrypted_value, :value_type, :updated_at]},
|
||||
conflict_target: :key
|
||||
)
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
|
||||
SettingsCache.invalidate()
|
||||
SettingsCache.warm()
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
Reference in New Issue
Block a user