diff --git a/lib/mv_web/components/layouts/root.html.heex b/lib/mv_web/components/layouts/root.html.heex index f6c3014..3ba099d 100644 --- a/lib/mv_web/components/layouts/root.html.heex +++ b/lib/mv_web/components/layouts/root.html.heex @@ -33,7 +33,44 @@ - <.flash_group flash={@flash} /> +
+ <.flash id="flash-success-root" kind={:success} flash={@flash} /> + <.flash id="flash-warning-root" kind={:warning} flash={@flash} /> + <.flash id="flash-info-root" kind={:info} flash={@flash} /> + <.flash id="flash-error-root" kind={:error} flash={@flash} /> + + <.flash + id="client-error-root" + kind={:error} + title={gettext("We can't find the internet")} + phx-disconnected={ + show(".phx-client-error #client-error-root") |> JS.remove_attribute("hidden") + } + phx-connected={hide("#client-error-root") |> JS.set_attribute({"hidden", ""})} + hidden + > + {gettext("Attempting to reconnect")} + <.icon name="hero-arrow-path" class="ml-1 size-3 motion-safe:animate-spin" /> + + + <.flash + id="server-error-root" + kind={:error} + title={gettext("Something went wrong!")} + phx-disconnected={ + show(".phx-server-error #server-error-root") |> JS.remove_attribute("hidden") + } + phx-connected={hide("#server-error-root") |> JS.set_attribute({"hidden", ""})} + hidden + > + {gettext("Attempting to reconnect")} + <.icon name="hero-arrow-path" class="ml-1 size-3 motion-safe:animate-spin" /> + +
{@inner_content} diff --git a/lib/mv_web/controllers/auth_controller.ex b/lib/mv_web/controllers/auth_controller.ex index 8249a10..24d1078 100644 --- a/lib/mv_web/controllers/auth_controller.ex +++ b/lib/mv_web/controllers/auth_controller.ex @@ -88,7 +88,10 @@ defmodule MvWeb.AuthController do Logger.warning("OIDC authentication server unreachable", safe_assent_meta(err)) conn - |> put_flash(:error, gettext("The authentication server is currently unavailable. Please try again later.")) + |> put_flash( + :error, + gettext("The authentication server is currently unavailable. Please try again later.") + ) |> redirect(to: ~p"/sign-in") end @@ -98,7 +101,10 @@ defmodule MvWeb.AuthController do Logger.warning("OIDC authentication invalid response", safe_assent_meta(err)) conn - |> put_flash(:error, gettext("Authentication configuration error. Please contact the administrator.")) + |> put_flash( + :error, + gettext("Authentication configuration error. Please contact the administrator.") + ) |> redirect(to: ~p"/sign-in") end @@ -224,10 +230,11 @@ defmodule MvWeb.AuthController do |> Enum.filter(fn {_key, value} -> not is_nil(value) end) end - defp safe_assent_meta(%{response: %{status_code: status_code}} = err) do + # Handle InvalidResponseError which has :response field (HTTPResponse struct) + defp safe_assent_meta(%{response: %{status: status} = response} = err) do [ - status_code: status_code, - http_adapter: Map.get(err, :http_adapter) + status: status, + http_adapter: Map.get(response, :http_adapter) || Map.get(err, :http_adapter) ] |> Enum.filter(fn {_key, value} -> not is_nil(value) end) end diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index 527a849..9968f16 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -27,6 +27,7 @@ msgid "Are you sure?" msgstr "Bist du sicher?" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "Attempting to reconnect" msgstr "Verbindung wird wiederhergestellt" @@ -115,11 +116,13 @@ msgid "Show" msgstr "Anzeigen" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "Something went wrong!" msgstr "Etwas ist schiefgelaufen!" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "We can't find the internet" msgstr "Keine Internetverbindung gefunden" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 8247f31..a4bf53a 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -28,6 +28,7 @@ msgid "Are you sure?" msgstr "" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "Attempting to reconnect" msgstr "" @@ -116,11 +117,13 @@ msgid "Show" msgstr "" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "Something went wrong!" msgstr "" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "We can't find the internet" msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index bff719d..5ed6eb1 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -28,6 +28,7 @@ msgid "Are you sure?" msgstr "" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "Attempting to reconnect" msgstr "" @@ -116,11 +117,13 @@ msgid "Show" msgstr "" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "Something went wrong!" msgstr "" #: lib/mv_web/components/layouts.ex +#: lib/mv_web/components/layouts/root.html.heex #, elixir-autogen, elixir-format msgid "We can't find the internet" msgstr "" diff --git a/test/mv_web/controllers/auth_controller_test.exs b/test/mv_web/controllers/auth_controller_test.exs index c177c96..5f53e0f 100644 --- a/test/mv_web/controllers/auth_controller_test.exs +++ b/test/mv_web/controllers/auth_controller_test.exs @@ -6,7 +6,10 @@ defmodule MvWeb.AuthControllerTest do # Helper to create an unauthenticated conn (preserves sandbox metadata) defp build_unauthenticated_conn(authenticated_conn) do # Create new conn but preserve sandbox metadata for database access - new_conn = build_conn() + new_conn = + build_conn() + |> init_test_session(%{}) + |> fetch_flash() # Copy sandbox metadata from authenticated conn if authenticated_conn.private[:ecto_sandbox] do @@ -255,29 +258,41 @@ defmodule MvWeb.AuthControllerTest do conn: authenticated_conn } do conn = build_unauthenticated_conn(authenticated_conn) - # Create a mock Assent.ServerUnreachableError struct - error = %Assent.ServerUnreachableError{request_url: "https://auth.example.com/callback?token=secret123"} + # Create a mock Assent.ServerUnreachableError struct with required fields + error = %Assent.ServerUnreachableError{ + http_adapter: Assent.HTTPAdapter.Finch, + request_url: "https://auth.example.com/callback?token=secret123", + reason: %Mint.TransportError{reason: :econnrefused} + } conn = MvWeb.AuthController.failure(conn, {:rauthy, :callback}, error) assert redirected_to(conn) == ~p"/sign-in" - assert get_flash(conn, :error) == "The authentication server is currently unavailable. Please try again later." + + assert get_flash(conn, :error) == + "The authentication server is currently unavailable. Please try again later." end test "Assent.InvalidResponseError redirects to sign-in with error flash", %{ conn: authenticated_conn } do conn = build_unauthenticated_conn(authenticated_conn) - # Create a mock Assent.InvalidResponseError struct + # Create a mock Assent.InvalidResponseError struct with required field + # InvalidResponseError only has :response field (HTTPResponse struct) error = %Assent.InvalidResponseError{ - response: %{status_code: 400, body: "invalid_request"}, - request_url: "https://auth.example.com/callback" + response: %Assent.HTTPAdapter.HTTPResponse{ + status: 400, + headers: [], + body: "invalid_request" + } } conn = MvWeb.AuthController.failure(conn, {:rauthy, :callback}, error) assert redirected_to(conn) == ~p"/sign-in" - assert get_flash(conn, :error) == "Authentication configuration error. Please contact the administrator." + + assert get_flash(conn, :error) == + "Authentication configuration error. Please contact the administrator." end test "unknown reason triggers catch-all and redirects to sign-in with error flash", %{ @@ -289,7 +304,9 @@ defmodule MvWeb.AuthControllerTest do conn = MvWeb.AuthController.failure(conn, {:rauthy, :callback}, unknown_reason) assert redirected_to(conn) == ~p"/sign-in" - assert get_flash(conn, :error) == "Unable to authenticate with OIDC. Please try again." + + assert get_flash(conn, :error) == + "Unable to authenticate with OIDC. Please try again." end end end