186 lines
6.5 KiB
Elixir
186 lines
6.5 KiB
Elixir
defmodule MusicLibraryWeb.Router do
|
|
use MusicLibraryWeb, :router
|
|
use ErrorTracker.Integrations.Plug
|
|
|
|
import MusicLibraryWeb.Auth, only: [require_logged_in: 2, require_api_token: 2]
|
|
import Oban.Web.Router
|
|
|
|
# Content Security Policy: restricts resource loading to same-origin by default,
|
|
# allows inline styles and Inter font from rsms.me, album art from Last.fm CDN,
|
|
# and prevents framing by external sites.
|
|
@csp_policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://rsms.me; font-src 'self' https://rsms.me; img-src 'self' data: https://lastfm.freetls.fastly.net; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'"
|
|
|
|
pipeline :browser do
|
|
plug :accepts, ["html"]
|
|
plug :fetch_session
|
|
plug :fetch_live_flash
|
|
plug :put_root_layout, html: {MusicLibraryWeb.Layouts, :root}
|
|
plug :protect_from_forgery
|
|
|
|
plug :put_secure_browser_headers, %{
|
|
"content-security-policy" => @csp_policy
|
|
}
|
|
end
|
|
|
|
pipeline :api do
|
|
plug :accepts, ["json"]
|
|
plug :require_api_token
|
|
end
|
|
|
|
pipeline :logged_in do
|
|
plug :require_logged_in
|
|
end
|
|
|
|
scope "/", MusicLibraryWeb do
|
|
pipe_through :browser
|
|
|
|
get "/health", HealthController, :index
|
|
get "/login", SessionController, :new
|
|
post "/sessions/create", SessionController, :create
|
|
|
|
get "/auth/last_fm/callback", LastFmController, :callback
|
|
|
|
get "/public/assets/:transform_payload", AssetController, :show
|
|
|
|
scope "/" do
|
|
pipe_through :logged_in
|
|
|
|
get "/backup", ArchiveController, :backup
|
|
|
|
get "/assets/:transform_payload", AssetController, :show
|
|
|
|
live_session :default,
|
|
on_mount: [
|
|
MusicLibraryWeb.Hooks.StaticAssets,
|
|
MusicLibraryWeb.Hooks.GetTimezone,
|
|
MusicLibraryWeb.Hooks.ShowToast
|
|
] do
|
|
live "/", StatsLive.Index, :index
|
|
|
|
live "/collection", CollectionLive.Index, :index
|
|
live "/collection/import", CollectionLive.Index, :import
|
|
live "/collection/scan", CollectionLive.Index, :barcode_scan
|
|
live "/collection/:id/edit", CollectionLive.Index, :edit
|
|
|
|
live "/collection/:id", CollectionLive.Show, :show
|
|
live "/collection/:id/show/edit", CollectionLive.Show, :edit
|
|
|
|
live "/wishlist", WishlistLive.Index, :index
|
|
live "/wishlist/import", WishlistLive.Index, :import
|
|
live "/wishlist/:id/edit", WishlistLive.Index, :edit
|
|
|
|
live "/wishlist/:id", WishlistLive.Show, :show
|
|
live "/wishlist/:id/show/edit", WishlistLive.Show, :edit
|
|
|
|
live "/artists/:musicbrainz_id", ArtistLive.Show, :show
|
|
live "/artists/:musicbrainz_id/import", ArtistLive.Show, :import
|
|
live "/artists/:musicbrainz_id/edit", ArtistLive.Show, :edit
|
|
|
|
live "/record-sets", RecordSetLive.Index, :index
|
|
live "/record-sets/new", RecordSetLive.Index, :new
|
|
live "/record-sets/:id/edit", RecordSetLive.Index, :edit
|
|
live "/record-sets/:id/add-record", RecordSetLive.Index, :add_record
|
|
|
|
live "/record-sets/:id", RecordSetLive.Show, :show
|
|
live "/record-sets/:id/show/edit", RecordSetLive.Show, :edit
|
|
live "/record-sets/:id/show/add-record", RecordSetLive.Show, :add_record
|
|
|
|
live "/scrobble-rules", ScrobbleRulesLive.Index, :index
|
|
live "/scrobble-rules/new", ScrobbleRulesLive.Index, :new
|
|
live "/scrobble-rules/:id/edit", ScrobbleRulesLive.Index, :edit
|
|
|
|
live "/online-store-templates", OnlineStoreTemplateLive.Index, :index
|
|
live "/online-store-templates/new", OnlineStoreTemplateLive.Index, :new
|
|
live "/online-store-templates/:id/edit", OnlineStoreTemplateLive.Index, :edit
|
|
|
|
live "/scrobbled-tracks", ScrobbledTracksLive.Index, :index
|
|
live "/scrobbled-tracks/:scrobbled_at_uts/edit", ScrobbledTracksLive.Index, :edit
|
|
|
|
live "/scrobble", ScrobbleLive.Index, :index
|
|
live "/scrobble/:release_id", ScrobbleLive.Show, :show
|
|
end
|
|
end
|
|
end
|
|
|
|
scope "/api", MusicLibraryWeb do
|
|
pipe_through :api
|
|
|
|
get "/collection/latest", CollectionController, :latest
|
|
get "/collection/random", CollectionController, :random
|
|
get "/collection/on_this_day", CollectionController, :on_this_day
|
|
get "/collection", CollectionController, :index
|
|
get "/assets/:transform_payload", AssetController, :show
|
|
get "/backup", ArchiveController, :backup
|
|
end
|
|
|
|
if Application.compile_env(:music_library, :monitoring_routes) do
|
|
import Phoenix.LiveDashboard.Router
|
|
use ErrorTracker.Web, :router
|
|
|
|
pipeline :dev_dashboard do
|
|
plug :generate_csp_nonces
|
|
plug :put_dev_csp
|
|
end
|
|
|
|
scope "/dev" do
|
|
pipe_through [:browser, :logged_in, :dev_dashboard]
|
|
|
|
live_dashboard "/dashboard",
|
|
metrics: MusicLibraryWeb.Telemetry,
|
|
metrics_history: {MusicLibraryWeb.Telemetry.Storage, :metrics_history, []},
|
|
ecto_repos: [MusicLibrary.Repo, MusicLibrary.BackgroundRepo, MusicLibrary.TelemetryRepo],
|
|
csp_nonce_assign_key: %{
|
|
img: :img_nonce,
|
|
style: :style_nonce,
|
|
script: :script_nonce
|
|
}
|
|
|
|
oban_dashboard "/oban",
|
|
csp_nonce_assign_key: %{
|
|
img: :img_nonce,
|
|
style: :style_nonce,
|
|
script: :script_nonce
|
|
}
|
|
|
|
live "/maintenance", MusicLibraryWeb.MaintenanceLive.Index, :index
|
|
|
|
error_tracker_dashboard "/errors",
|
|
csp_nonce_assign_key: %{
|
|
img: :img_nonce,
|
|
style: :style_nonce,
|
|
script: :script_nonce
|
|
}
|
|
end
|
|
end
|
|
|
|
defp generate_csp_nonces(conn, _opts) do
|
|
nonce = Base.encode64(:crypto.strong_rand_bytes(16), padding: false)
|
|
|
|
conn
|
|
|> assign(:img_nonce, nonce)
|
|
|> assign(:style_nonce, nonce)
|
|
|> assign(:script_nonce, nonce)
|
|
end
|
|
|
|
# Replaces the default CSP with a dev-specific policy that adds nonce-based
|
|
# exceptions for scripts, styles, and images, allowing Phoenix dev tools
|
|
# (live reload, debug toolbar) to load alongside the standard policy.
|
|
defp put_dev_csp(conn, _opts) do
|
|
nonce = conn.assigns[:script_nonce]
|
|
|
|
csp =
|
|
"default-src 'self'; script-src 'self' 'nonce-#{nonce}'; style-src 'self' 'unsafe-inline' 'nonce-#{nonce}' https://rsms.me; font-src 'self' data: https://rsms.me; img-src 'self' data: 'nonce-#{nonce}' https://lastfm.freetls.fastly.net; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'"
|
|
|
|
delete_resp_header(conn, "content-security-policy")
|
|
|> put_resp_header("content-security-policy", csp)
|
|
end
|
|
|
|
if Mix.env() == :dev do
|
|
scope "/dev" do
|
|
pipe_through [:browser, :logged_in]
|
|
|
|
forward "/mailbox", Plug.Swoosh.MailboxPreview
|
|
end
|
|
end
|
|
end
|