From 0b7870dc4d019f79549fd2aa7aa4f9126a3a8d54 Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 10 Nov 2022 19:42:22 +0800 Subject: [PATCH] improve first launch experience for self-hosters (#2357) * first launch * dynamic children, wait for repo * remove wait_for_repo and app env manipulations * don't mention free trial in self-hosted pages * add changelog * assigns[:is_selfhost] -> @is_selfhost * better changelog wording * rm admin_user, admin_email, admin_pwd from app env * rm DISABLE_AUTH * redirect / to /login when not authenticated * remove TODO * Update lib/plausible_web/controllers/page_controller.ex Co-authored-by: Uku Taht * format Co-authored-by: Uku Taht --- CHANGELOG.md | 1 + config/.env.test | 2 - config/runtime.exs | 18 ++-- lib/plausible_release.ex | 37 ++------ .../controllers/auth_controller.ex | 87 +++++++++++-------- .../controllers/page_controller.ex | 15 ++-- lib/plausible_web/plugs/auto_auth_plug.ex | 25 ------ lib/plausible_web/plugs/first_launch_plug.ex | 26 ++++++ lib/plausible_web/router.ex | 1 + .../templates/auth/register_form.html.eex | 18 +++- .../templates/layout/_header.html.eex | 8 -- .../templates/page/index.html.eex | 3 - lib/plausible_web/views/auth_view.ex | 4 - lib/plausible_web/views/billing_view.ex | 4 - lib/plausible_web/views/email_view.ex | 4 - lib/plausible_web/views/layout_view.ex | 4 - lib/plausible_web/views/page_view.ex | 15 ---- lib/plausible_web/views/site_view.ex | 4 - lib/plausible_web/views/stats_view.ex | 4 - rel/overlays/init-admin.sh | 5 +- test/plausible/release_test.exs | 30 +++++++ .../admin_auth_controller_test.exs | 59 +++++-------- .../plugs/first_launch_plug_test.exs | 73 ++++++++++++++++ 23 files changed, 239 insertions(+), 208 deletions(-) delete mode 100644 lib/plausible_web/plugs/auto_auth_plug.ex create mode 100644 lib/plausible_web/plugs/first_launch_plug.ex delete mode 100644 lib/plausible_web/templates/page/index.html.eex delete mode 100644 lib/plausible_web/views/page_view.ex create mode 100644 test/plausible/release_test.exs create mode 100644 test/plausible_web/plugs/first_launch_plug_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index f39e07336..a7bbf55c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ All notable changes to this project will be documented in this file. - Manually lock and unlock enterprise users plausible/analytics#2197 - ARM64 support for docker images plausible/analytics#2103 - Add support for international domain names (IDNs) plausible/analytics#2034 +- Allow self-hosters to register an account on first launch ### Fixed - Plausible script does not prevent default if it's been prevented by an external script [plausible/analytics#1941](https://github.com/plausible/analytics/issues/1941) diff --git a/config/.env.test b/config/.env.test index 0f1a6f5d2..ec2510711 100644 --- a/config/.env.test +++ b/config/.env.test @@ -6,8 +6,6 @@ CRON_ENABLED=false LOG_LEVEL=warn ENVIRONMENT=test MAILER_ADAPTER=Bamboo.TestAdapter -ADMIN_USER_EMAIL=admin@email.com -ADMIN_USER_PWD=fakepassword ENABLE_EMAIL_VERIFICATION=true SELFHOST=false SITE_LIMIT=3 diff --git a/config/runtime.exs b/config/runtime.exs index 00002ac7b..08e14b159 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -58,9 +58,6 @@ db_url = db_socket_dir = get_var_from_path_or_env(config_dir, "DATABASE_SOCKET_DIR") -admin_user = get_var_from_path_or_env(config_dir, "ADMIN_USER_NAME") -admin_email = get_var_from_path_or_env(config_dir, "ADMIN_USER_EMAIL") - super_admin_user_ids = get_var_from_path_or_env(config_dir, "ADMIN_USER_IDS", "") |> String.split(",") @@ -71,7 +68,6 @@ super_admin_user_ids = end) |> Enum.filter(& &1) -admin_pwd = get_var_from_path_or_env(config_dir, "ADMIN_USER_PWD") env = get_var_from_path_or_env(config_dir, "ENVIRONMENT", "prod") mailer_adapter = get_var_from_path_or_env(config_dir, "MAILER_ADAPTER", "Bamboo.SMTPAdapter") mailer_email = get_var_from_path_or_env(config_dir, "MAILER_EMAIL", "hello@plausible.local") @@ -135,10 +131,10 @@ geolite2_country_db = ip_geolocation_db = get_var_from_path_or_env(config_dir, "IP_GEOLOCATION_DB", geolite2_country_db) geonames_source_file = get_var_from_path_or_env(config_dir, "GEONAMES_SOURCE_FILE") -disable_auth = - config_dir - |> get_var_from_path_or_env("DISABLE_AUTH", "false") - |> String.to_existing_atom() +if System.get_env("DISABLE_AUTH") do + require Logger + Logger.warn("DISABLE_AUTH env var is no longer supported") +end enable_email_verification = config_dir @@ -193,9 +189,6 @@ disable_cron = |> String.to_existing_atom() config :plausible, - admin_user: admin_user, - admin_email: admin_email, - admin_pwd: admin_pwd, environment: env, mailer_email: mailer_email, super_admin_user_ids: super_admin_user_ids, @@ -206,9 +199,8 @@ config :plausible, domain_blacklist: domain_blacklist config :plausible, :selfhost, - disable_authentication: disable_auth, enable_email_verification: enable_email_verification, - disable_registration: if(!disable_auth, do: disable_registration, else: false) + disable_registration: disable_registration config :plausible, PlausibleWeb.Endpoint, url: [scheme: base_url.scheme, host: base_url.host, path: base_url.path, port: base_url.port], diff --git a/lib/plausible_release.ex b/lib/plausible_release.ex index 69fde22d4..fe03b2941 100644 --- a/lib/plausible_release.ex +++ b/lib/plausible_release.ex @@ -1,5 +1,7 @@ defmodule Plausible.Release do use Plausible.Repo + require Logger + @app :plausible @start_apps [ :postgrex, @@ -7,24 +9,13 @@ defmodule Plausible.Release do :ecto ] - def init_admin do - prepare() + @spec selfhost? :: boolean + def selfhost? do + Application.fetch_env!(@app, :is_selfhost) + end - {admin_email, admin_user, admin_pwd} = - validate_admin( - {Application.get_env(:plausible, :admin_email), - Application.get_env(:plausible, :admin_user), - Application.get_env(:plausible, :admin_pwd)} - ) - - case Plausible.Auth.find_user_by(email: admin_email) do - nil -> - {:ok, _} = Plausible.Auth.create_user(admin_user, admin_email, admin_pwd) - IO.puts("Admin user created successful!") - - _ -> - IO.puts("Admin user already exists. I won't override, bailing") - end + def should_be_first_launch? do + selfhost?() and not (_has_users? = Repo.exists?(Plausible.Auth.User)) end def migrate do @@ -81,18 +72,6 @@ defmodule Plausible.Release do ############################## - defp validate_admin({nil, nil, nil}) do - random_user = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8) - random_pwd = :crypto.strong_rand_bytes(20) |> Base.encode64() |> binary_part(0, 20) - random_email = "#{random_user}@#{System.get_env("HOST")}" - IO.puts("generated admin user/password: #{random_email} / #{random_pwd}") - {random_email, random_user, random_pwd} - end - - defp validate_admin({admin_email, admin_user, admin_password}) do - {admin_email, admin_user, admin_password} - end - defp repos do Application.fetch_env!(@app, :ecto_repos) end diff --git a/lib/plausible_web/controllers/auth_controller.ex b/lib/plausible_web/controllers/auth_controller.ex index adde571fe..e67a858e2 100644 --- a/lib/plausible_web/controllers/auth_controller.ex +++ b/lib/plausible_web/controllers/auth_controller.ex @@ -1,7 +1,7 @@ defmodule PlausibleWeb.AuthController do use PlausibleWeb, :controller use Plausible.Repo - alias Plausible.Auth + alias Plausible.{Auth, Release} require Logger plug PlausibleWeb.RequireLoggedOutPlug @@ -24,52 +24,63 @@ defmodule PlausibleWeb.AuthController do :activate_form ] - def register_form(conn, _params) do - if Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_registration) != false do - redirect(conn, to: Routes.auth_path(conn, :login_form)) - else - changeset = Plausible.Auth.User.changeset(%Plausible.Auth.User{}) + plug :maybe_disable_registration when action in [:register_form, :register] + plug :assign_is_selfhost - render(conn, "register_form.html", - changeset: changeset, - layout: {PlausibleWeb.LayoutView, "focus.html"} - ) + defp maybe_disable_registration(conn, _opts) do + selfhost_config = Application.get_env(:plausible, :selfhost) + disable_registration = Keyword.fetch!(selfhost_config, :disable_registration) + first_launch? = Release.should_be_first_launch?() + + cond do + first_launch? -> + conn + + disable_registration in [:invite_only, true] -> + conn |> redirect(to: Routes.auth_path(conn, :login_form)) |> halt() + + true -> + conn end end + defp assign_is_selfhost(conn, _opts) do + assign(conn, :is_selfhost, Plausible.Release.selfhost?()) + end + + def register_form(conn, _params) do + changeset = Auth.User.changeset(%Auth.User{}) + + render(conn, "register_form.html", + changeset: changeset, + layout: {PlausibleWeb.LayoutView, "focus.html"} + ) + end + def register(conn, params) do - if Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_registration) != false do - redirect(conn, to: Routes.auth_path(conn, :login_form)) - else - user = Plausible.Auth.User.new(params["user"]) + conn = put_layout(conn, {PlausibleWeb.LayoutView, "focus.html"}) + user = Plausible.Auth.User.new(params["user"]) - if PlausibleWeb.Captcha.verify(params["h-captcha-response"]) do - case Repo.insert(user) do - {:ok, user} -> - conn = set_user_session(conn, user) + if PlausibleWeb.Captcha.verify(params["h-captcha-response"]) do + case Repo.insert(user) do + {:ok, user} -> + conn = set_user_session(conn, user) - case user.email_verified do - false -> - send_email_verification(user) - redirect(conn, to: Routes.auth_path(conn, :activate_form)) + if user.email_verified do + redirect(conn, to: Routes.site_path(conn, :new)) + else + send_email_verification(user) + redirect(conn, to: Routes.auth_path(conn, :activate_form)) + end - true -> - redirect(conn, to: Routes.site_path(conn, :new)) - end - - {:error, changeset} -> - render(conn, "register_form.html", - changeset: changeset, - layout: {PlausibleWeb.LayoutView, "focus.html"} - ) - end - else - render(conn, "register_form.html", - changeset: user, - captcha_error: "Please complete the captcha to register", - layout: {PlausibleWeb.LayoutView, "focus.html"} - ) + {:error, changeset} -> + render(conn, "register_form.html", changeset: changeset) end + else + render(conn, "register_form.html", + changeset: user, + captcha_error: "Please complete the captcha to register" + ) end end diff --git a/lib/plausible_web/controllers/page_controller.ex b/lib/plausible_web/controllers/page_controller.ex index 64e217d08..03ec4f2aa 100644 --- a/lib/plausible_web/controllers/page_controller.ex +++ b/lib/plausible_web/controllers/page_controller.ex @@ -1,14 +1,15 @@ defmodule PlausibleWeb.PageController do use PlausibleWeb, :controller use Plausible.Repo - plug PlausibleWeb.AutoAuthPlug + @doc """ + The root path is never accessible in Plausible.Cloud because it is handled by the upstream reverse proxy. + + This controller action is only ever triggered in self-hosted Plausible. It redirects the user to /login where `PlausibleWeb.RequireLoggedOutPlug` plug kicks in. If they are already logged in, they are redirected to /sites, otherwise they'll see the /login page. + """ def index(conn, _params) do - if conn.assigns[:current_user] do - user = conn.assigns[:current_user] |> Repo.preload(:sites) - render(conn, "sites.html", sites: user.sites) - else - render(conn, "index.html") - end + conn + |> put_session(:login_dest, conn.request_path) + |> redirect(to: "/login") end end diff --git a/lib/plausible_web/plugs/auto_auth_plug.ex b/lib/plausible_web/plugs/auto_auth_plug.ex deleted file mode 100644 index b56c4fc8f..000000000 --- a/lib/plausible_web/plugs/auto_auth_plug.ex +++ /dev/null @@ -1,25 +0,0 @@ -defmodule PlausibleWeb.AutoAuthPlug do - import Plug.Conn - alias PlausibleWeb.AuthController - - def init(options) do - options - end - - def call(conn, _opts) do - cond do - Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_authentication) -> - conn - |> AuthController.login(%{ - "email" => Application.fetch_env!(:plausible, :admin_email), - "password" => Application.fetch_env!(:plausible, :admin_pwd) - }) - |> halt - - true -> - Plug.Conn.put_session(conn, :login_dest, conn.request_path) - |> Phoenix.Controller.redirect(to: "/login") - |> halt - end - end -end diff --git a/lib/plausible_web/plugs/first_launch_plug.ex b/lib/plausible_web/plugs/first_launch_plug.ex new file mode 100644 index 000000000..6588e338b --- /dev/null +++ b/lib/plausible_web/plugs/first_launch_plug.ex @@ -0,0 +1,26 @@ +defmodule PlausibleWeb.FirstLaunchPlug do + @moduledoc """ + Redirects first-launch users to registration page. + """ + + @behaviour Plug + alias Plausible.Release + + @impl true + def init(opts) do + _path = Keyword.fetch!(opts, :redirect_to) + end + + @impl true + def call(%Plug.Conn{request_path: path} = conn, path), do: conn + + def call(conn, redirect_to) do + if Release.should_be_first_launch?() do + conn + |> Phoenix.Controller.redirect(to: redirect_to) + |> Plug.Conn.halt() + else + conn + end + end +end diff --git a/lib/plausible_web/router.ex b/lib/plausible_web/router.ex index 2667b49f3..04b750f07 100644 --- a/lib/plausible_web/router.ex +++ b/lib/plausible_web/router.ex @@ -8,6 +8,7 @@ defmodule PlausibleWeb.Router do plug :fetch_session plug :fetch_flash plug :put_secure_browser_headers + plug PlausibleWeb.FirstLaunchPlug, redirect_to: "/register" plug PlausibleWeb.SessionTimeoutPlug, timeout_after_seconds: @two_weeks_in_seconds plug PlausibleWeb.AuthPlug plug PlausibleWeb.LastSeenPlug diff --git a/lib/plausible_web/templates/auth/register_form.html.eex b/lib/plausible_web/templates/auth/register_form.html.eex index 0b63c07ba..9e5c841af 100644 --- a/lib/plausible_web/templates/auth/register_form.html.eex +++ b/lib/plausible_web/templates/auth/register_form.html.eex @@ -1,5 +1,11 @@
-

Register your 30-day unlimited-use free trial

+

+ <%= if @is_selfhost do %> + Register your Plausible Analytics account + <% else %> + Register your 30-day unlimited-use free trial + <% end %> +

Set up privacy-friendly analytics with just a few clicks
@@ -53,7 +59,15 @@ <% end %> - <%= submit "Start my free trial →", class: "button mt-4 w-full" %> + <% + submit_text = + if @is_selfhost do + "Create my account →" + else + "Start my free trial →" + end + %> + <%= submit submit_text, class: "button mt-4 w-full" %>

Already have an account? <%= link("Log in", to: "/login", class: "underline text-gray-800 dark:text-gray-50") %> instead. diff --git a/lib/plausible_web/templates/layout/_header.html.eex b/lib/plausible_web/templates/layout/_header.html.eex index f6adc3bb2..5d56d9925 100644 --- a/lib/plausible_web/templates/layout/_header.html.eex +++ b/lib/plausible_web/templates/layout/_header.html.eex @@ -46,14 +46,6 @@

  • <% end %> - <% Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_authentication) -> %> - <% Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_registration) != false -> %>
    • diff --git a/lib/plausible_web/templates/page/index.html.eex b/lib/plausible_web/templates/page/index.html.eex deleted file mode 100644 index d9551964f..000000000 --- a/lib/plausible_web/templates/page/index.html.eex +++ /dev/null @@ -1,3 +0,0 @@ -
      - You will be redirected... If it doesn't work, please click login. -
      diff --git a/lib/plausible_web/views/auth_view.ex b/lib/plausible_web/views/auth_view.ex index 32976e2c5..bc68d820e 100644 --- a/lib/plausible_web/views/auth_view.ex +++ b/lib/plausible_web/views/auth_view.ex @@ -2,10 +2,6 @@ defmodule PlausibleWeb.AuthView do use PlausibleWeb, :view alias Plausible.Billing.Plans - def admin_email do - Application.get_env(:plausible, :admin_email) - end - def base_domain do PlausibleWeb.Endpoint.host() end diff --git a/lib/plausible_web/views/billing_view.ex b/lib/plausible_web/views/billing_view.ex index 6a4abdd9e..32a088a0b 100644 --- a/lib/plausible_web/views/billing_view.ex +++ b/lib/plausible_web/views/billing_view.ex @@ -1,10 +1,6 @@ defmodule PlausibleWeb.BillingView do use PlausibleWeb, :view - def admin_email do - Application.get_env(:plausible, :admin_email) - end - def base_domain do PlausibleWeb.Endpoint.host() end diff --git a/lib/plausible_web/views/email_view.ex b/lib/plausible_web/views/email_view.ex index 5d43bd667..ac45f7472 100644 --- a/lib/plausible_web/views/email_view.ex +++ b/lib/plausible_web/views/email_view.ex @@ -1,10 +1,6 @@ defmodule PlausibleWeb.EmailView do use PlausibleWeb, :view - def admin_email do - Application.get_env(:plausible, :admin_email) - end - def plausible_url do PlausibleWeb.Endpoint.url() end diff --git a/lib/plausible_web/views/layout_view.ex b/lib/plausible_web/views/layout_view.ex index 626f9c06e..1e6e04927 100644 --- a/lib/plausible_web/views/layout_view.ex +++ b/lib/plausible_web/views/layout_view.ex @@ -1,10 +1,6 @@ defmodule PlausibleWeb.LayoutView do use PlausibleWeb, :view - def admin_email do - Application.get_env(:plausible, :admin_email) - end - def base_domain do PlausibleWeb.Endpoint.host() end diff --git a/lib/plausible_web/views/page_view.ex b/lib/plausible_web/views/page_view.ex deleted file mode 100644 index f0fd45dea..000000000 --- a/lib/plausible_web/views/page_view.ex +++ /dev/null @@ -1,15 +0,0 @@ -defmodule PlausibleWeb.PageView do - use PlausibleWeb, :view - - def admin_email do - Application.get_env(:plausible, :admin_email) - end - - def base_domain do - PlausibleWeb.Endpoint.host() - end - - def plausible_url do - PlausibleWeb.Endpoint.url() - end -end diff --git a/lib/plausible_web/views/site_view.ex b/lib/plausible_web/views/site_view.ex index 38edb8ea1..e0cc71efa 100644 --- a/lib/plausible_web/views/site_view.ex +++ b/lib/plausible_web/views/site_view.ex @@ -2,10 +2,6 @@ defmodule PlausibleWeb.SiteView do use PlausibleWeb, :view import Phoenix.Pagination.HTML - def admin_email do - Application.get_env(:plausible, :admin_email) - end - def plausible_url do PlausibleWeb.Endpoint.url() end diff --git a/lib/plausible_web/views/stats_view.ex b/lib/plausible_web/views/stats_view.ex index ebf0bdea1..faf651583 100644 --- a/lib/plausible_web/views/stats_view.ex +++ b/lib/plausible_web/views/stats_view.ex @@ -1,10 +1,6 @@ defmodule PlausibleWeb.StatsView do use PlausibleWeb, :view - def admin_email do - Application.get_env(:plausible, :admin_email) - end - def base_domain do PlausibleWeb.Endpoint.host() end diff --git a/rel/overlays/init-admin.sh b/rel/overlays/init-admin.sh index 474fc3f03..d726813af 100755 --- a/rel/overlays/init-admin.sh +++ b/rel/overlays/init-admin.sh @@ -1,6 +1,5 @@ #!/bin/sh # Create an admin user -BIN_DIR=$(dirname "$0") - -"${BIN_DIR}"/bin/plausible eval Plausible.Release.init_admin +echo "init-admin is deprecated and is no-op now" +echo "user registration on first launch happens via Web UI instead" diff --git a/test/plausible/release_test.exs b/test/plausible/release_test.exs new file mode 100644 index 000000000..d77d84b2d --- /dev/null +++ b/test/plausible/release_test.exs @@ -0,0 +1,30 @@ +defmodule Plausible.ReleaseTest do + use Plausible.DataCase + alias Plausible.{Release, Auth} + + describe "should_be_first_launch?/0" do + test "returns true when self-hosted and no users" do + patch_env(:is_selfhost, true) + refute Repo.exists?(Auth.User) + assert Release.should_be_first_launch?() + end + + test "returns false when not self-hosted and has no users" do + patch_env(:is_selfhost, false) + refute Repo.exists?(Auth.User) + refute Release.should_be_first_launch?() + end + + test "returns false when not self-hosted and has users" do + insert(:user) + patch_env(:is_selfhost, false) + refute Release.should_be_first_launch?() + end + + test "returns false when self-hosted and has users" do + insert(:user) + patch_env(:is_selfhost, true) + refute Release.should_be_first_launch?() + end + end +end diff --git a/test/plausible_web/controllers/admin_auth_controller_test.exs b/test/plausible_web/controllers/admin_auth_controller_test.exs index 282dac10b..e07fb17e5 100644 --- a/test/plausible_web/controllers/admin_auth_controller_test.exs +++ b/test/plausible_web/controllers/admin_auth_controller_test.exs @@ -1,52 +1,33 @@ defmodule PlausibleWeb.AdminAuthControllerTest do use PlausibleWeb.ConnCase + alias Plausible.Release + + setup_patch_env(:is_selfhost, true) describe "GET /" do - test "no landing page", %{conn: conn} do - set_config(disable_authentication: false) - conn = get(conn, "/") - assert redirected_to(conn) == "/login" - end - - test "logs admin user in automatically when authentication is disabled", %{conn: conn} do - set_config(disable_authentication: true) - - admin_user = - insert(:user, - email: Application.get_env(:plausible, :admin_email), - password: Application.get_env(:plausible, :admin_pwd) - ) - - # goto landing page - conn = get(conn, "/") - assert get_session(conn, :current_user_id) == admin_user.id - assert redirected_to(conn) == "/sites" - - # trying logging out - conn = get(conn, "/logout") - assert redirected_to(conn) == "/" - conn = get(conn, "/") - assert redirected_to(conn) == "/sites" - end - test "disable registration", %{conn: conn} do - set_config(disable_registration: true) + prevent_first_launch() + patch_config(disable_registration: true) conn = get(conn, "/register") assert redirected_to(conn) == "/login" end + + test "disable registration + first launch", %{conn: conn} do + patch_config(disable_registration: true) + assert Release.should_be_first_launch?() + + # "first launch" takes precedence + conn = get(conn, "/register") + assert html_response(conn, 200) =~ "Enter your details" + end end - def set_config(config) do - updated_config = - Keyword.merge( - [disable_authentication: false, disable_registration: false], - config - ) + def patch_config(config) do + updated_config = Keyword.merge([disable_registration: false], config) + patch_env(:selfhost, updated_config) + end - Application.put_env( - :plausible, - :selfhost, - updated_config - ) + defp prevent_first_launch do + insert(:user) end end diff --git a/test/plausible_web/plugs/first_launch_plug_test.exs b/test/plausible_web/plugs/first_launch_plug_test.exs new file mode 100644 index 000000000..7048a8fe9 --- /dev/null +++ b/test/plausible_web/plugs/first_launch_plug_test.exs @@ -0,0 +1,73 @@ +defmodule PlausibleWeb.FirstLaunchPlugTest do + use PlausibleWeb.ConnCase + import Plug.Test + + alias PlausibleWeb.FirstLaunchPlug + alias Plausible.Release + + describe "init/1" do + test "requires :redirect_to option" do + assert_raise KeyError, ~r"key :redirect_to not found", fn -> + FirstLaunchPlug.init(_no_opts = []) + end + + path = FirstLaunchPlug.init(redirect_to: "/register") + assert path == "/register" + end + end + + @opts FirstLaunchPlug.init(redirect_to: "/register") + + describe "call/2" do + test "no-op for paths == :redirect_to" do + conn = conn("GET", "/register") + conn = FirstLaunchPlug.call(conn, @opts) + refute conn.halted + + # even when it's the first launch + patch_env(:is_selfhost, true) + assert Release.should_be_first_launch?() + + conn = conn("GET", "/register") + conn = FirstLaunchPlug.call(conn, @opts) + refute conn.halted + end + + test "no-op when not first launch" do + refute Release.should_be_first_launch?() + conn = conn("GET", "/sites") + conn = FirstLaunchPlug.call(conn, @opts) + refute conn.halted + end + + test "redirects to :redirect_to when first launch" do + patch_env(:is_selfhost, true) + assert Release.should_be_first_launch?() + + conn = conn("GET", "/sites") + conn = FirstLaunchPlug.call(conn, @opts) + assert conn.halted + assert redirected_to(conn) == "/register" + end + end + + describe "first launch plug in :browser pipeline" do + test "redirects to /register on first launch", %{conn: conn} do + patch_env(:is_selfhost, true) + assert Release.should_be_first_launch?() + + conn = get(conn, "/") + assert redirected_to(conn) == "/register" + end + + test "no-op when not first launch", %{conn: conn} do + patch_env(:is_selfhost, false) + refute Release.should_be_first_launch?() + + # gets redirected to login by auth plugs + # "first launch" doesn't interfere + conn = get(conn, "/sites") + assert redirected_to(conn) == "/login" + end + end +end