berrypod/lib/simpleshop_theme_web/router.ex
jamey 5c2f70ce44 add shipping costs with live exchange rates and country detection
Shipping rates fetched from Printify during product sync, converted to
GBP at sync time using frankfurter.app ECB exchange rates with 5%
buffer. Cached in shipping_rates table per blueprint/provider/country.

Cart page shows shipping estimate with country selector (detected from
Accept-Language header, persisted in cookie). Stripe Checkout includes
shipping_options for UK domestic and international delivery. Order
shipping_cost extracted from Stripe on payment.

ScheduledSyncWorker runs every 6 hours via Oban cron to keep rates
and exchange rates fresh. REST_OF_THE_WORLD fallback covers unlisted
countries. 780 tests, 0 failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:48:00 +00:00

186 lines
5.7 KiB
Elixir

defmodule SimpleshopThemeWeb.Router do
use SimpleshopThemeWeb, :router
import SimpleshopThemeWeb.UserAuth
import Phoenix.LiveDashboard.Router
import ErrorTracker.Web.Router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, html: {SimpleshopThemeWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :fetch_current_scope_for_user
plug SimpleshopThemeWeb.Plugs.CountryDetect
end
pipeline :api do
plug :accepts, ["json"]
end
pipeline :printify_webhook do
plug SimpleshopThemeWeb.Plugs.VerifyPrintifyWebhook
end
pipeline :shop do
plug :put_root_layout, html: {SimpleshopThemeWeb.Layouts, :shop_root}
plug SimpleshopThemeWeb.Plugs.LoadTheme
end
pipeline :admin do
plug :put_root_layout, html: {SimpleshopThemeWeb.Layouts, :admin_root}
end
# Public storefront (root level)
scope "/", SimpleshopThemeWeb do
pipe_through [:browser, :shop]
live_session :coming_soon,
layout: {SimpleshopThemeWeb.Layouts, :shop},
on_mount: [
{SimpleshopThemeWeb.ThemeHook, :mount_theme}
] do
live "/coming-soon", Shop.ComingSoon, :index
end
live_session :public_shop,
layout: {SimpleshopThemeWeb.Layouts, :shop},
on_mount: [
{SimpleshopThemeWeb.UserAuth, :mount_current_scope},
{SimpleshopThemeWeb.ThemeHook, :mount_theme},
{SimpleshopThemeWeb.ThemeHook, :require_site_live},
{SimpleshopThemeWeb.CartHook, :mount_cart},
{SimpleshopThemeWeb.SearchHook, :mount_search}
] do
live "/", Shop.Home, :index
live "/about", Shop.Content, :about
live "/delivery", Shop.Content, :delivery
live "/privacy", Shop.Content, :privacy
live "/terms", Shop.Content, :terms
live "/contact", Shop.Contact, :index
live "/collections/:slug", Shop.Collection, :show
live "/products/:id", Shop.ProductShow, :show
live "/cart", Shop.Cart, :index
live "/checkout/success", Shop.CheckoutSuccess, :show
end
# Checkout (POST — creates Stripe session and redirects)
post "/checkout", CheckoutController, :create
end
# Health check (no auth, no theme loading — for load balancers and uptime monitors)
scope "/", SimpleshopThemeWeb do
pipe_through [:api]
get "/health", HealthController, :show
end
# Cart API (session persistence for LiveView)
scope "/api", SimpleshopThemeWeb do
pipe_through [:browser]
post "/cart", CartController, :update
end
# Image serving routes (public, no auth required)
scope "/images", SimpleshopThemeWeb do
pipe_through :browser
get "/:id", ImageController, :show
get "/:id/thumbnail", ImageController, :thumbnail
get "/:id/variant/:width", ImageController, :variant
get "/:id/recolored/:color", ImageController, :recolored_svg
end
# Webhook endpoints (no CSRF, signature verified)
scope "/webhooks", SimpleshopThemeWeb do
pipe_through [:api, :printify_webhook]
post "/printify", WebhookController, :printify
end
scope "/webhooks", SimpleshopThemeWeb do
pipe_through [:api]
post "/stripe", StripeWebhookController, :handle
end
# LiveDashboard and ErrorTracker behind admin auth (available in all environments)
scope "/admin" do
pipe_through [:browser, :require_authenticated_user]
live_dashboard "/dashboard", metrics: SimpleshopThemeWeb.Telemetry
error_tracker_dashboard("/errors")
end
# Dev-only routes (mailbox preview, error previews)
if Application.compile_env(:simpleshop_theme, :dev_routes) do
scope "/dev" do
pipe_through :browser
forward "/mailbox", Plug.Swoosh.MailboxPreview
# Preview error pages
get "/errors/404", SimpleshopThemeWeb.ErrorPreviewController, :not_found
get "/errors/500", SimpleshopThemeWeb.ErrorPreviewController, :server_error
end
end
## Authentication routes
# Admin pages with sidebar layout
scope "/admin", SimpleshopThemeWeb do
pipe_through [:browser, :require_authenticated_user, :admin]
live_session :admin,
layout: {SimpleshopThemeWeb.Layouts, :admin},
on_mount: [
{SimpleshopThemeWeb.UserAuth, :require_authenticated},
{SimpleshopThemeWeb.AdminLayoutHook, :assign_current_path}
] do
live "/", Admin.Dashboard, :index
live "/orders", Admin.Orders, :index
live "/orders/:id", Admin.OrderShow, :show
live "/providers", Admin.Providers.Index, :index
live "/providers/new", Admin.Providers.Form, :new
live "/providers/:id/edit", Admin.Providers.Form, :edit
live "/settings", Admin.Settings, :index
end
# Theme editor: admin root layout but full-screen (no sidebar)
live_session :admin_theme,
on_mount: [{SimpleshopThemeWeb.UserAuth, :require_authenticated}] do
live "/theme", Admin.Theme.Index, :index
end
end
# User account settings
scope "/", SimpleshopThemeWeb do
pipe_through [:browser, :require_authenticated_user]
live_session :user_settings,
on_mount: [{SimpleshopThemeWeb.UserAuth, :require_authenticated}] do
live "/users/settings", Auth.Settings, :edit
live "/users/settings/confirm-email/:token", Auth.Settings, :confirm_email
end
post "/users/update-password", UserSessionController, :update_password
end
scope "/", SimpleshopThemeWeb do
pipe_through [:browser]
live_session :current_user,
on_mount: [{SimpleshopThemeWeb.UserAuth, :mount_current_scope}] do
live "/users/register", Auth.Registration, :new
live "/users/log-in", Auth.Login, :new
live "/users/log-in/:token", Auth.Confirmation, :new
end
post "/users/log-in", UserSessionController, :create
delete "/users/log-out", UserSessionController, :delete
end
end