diff --git a/extra/lib/plausible_web/controllers/api/external_sites_controller.ex b/extra/lib/plausible_web/controllers/api/external_sites_controller.ex index 78da9e0dd8ca..10de3192d700 100644 --- a/extra/lib/plausible_web/controllers/api/external_sites_controller.ex +++ b/extra/lib/plausible_web/controllers/api/external_sites_controller.ex @@ -6,6 +6,50 @@ defmodule PlausibleWeb.Api.ExternalSitesController do alias Plausible.Goals alias PlausibleWeb.Api.Helpers, as: H + def index(conn, params) do + user = conn.assigns.current_user + + page = Sites.list(user, params) + + json(conn, %{ + sites: page.entries, + meta: %{ + page_number: page.page_number, + page_size: page.page_size, + total_entries: page.total_entries, + total_pages: page.total_pages + } + }) + end + + def goals_index(conn, params) do + user = conn.assigns.current_user + + with {:ok, site_id} <- expect_param_key(params, "site_id"), + {:ok, site} <- get_site(user, site_id, [:owner, :admin]) do + page = + site + |> Plausible.Goals.for_site_query() + |> Repo.paginate(params) + + json(conn, %{ + goals: page.entries, + meta: %{ + page_number: page.page_number, + page_size: page.page_size, + total_entries: page.total_entries, + total_pages: page.total_pages + } + }) + else + {:missing, "site_id"} -> + H.bad_request(conn, "Parameter `site_id` is required to create a shared link") + + {:error, :site_not_found} -> + H.not_found(conn, "Site could not be found") + end + end + def create_site(conn, params) do user = conn.assigns[:current_user] @@ -29,57 +73,50 @@ defmodule PlausibleWeb.Api.ExternalSitesController do end def get_site(conn, %{"site_id" => site_id}) do - site = Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]) + case get_site(conn.assigns.current_user, site_id, [:owner, :admin]) do + {:ok, site} -> + json(conn, %{ + domain: site.domain, + timezone: site.timezone, + allowed_custom_props: site.allowed_event_props || [] + }) - if site do - json(conn, site) - else - H.not_found(conn, "Site could not be found") + {:error, :site_not_found} -> + H.not_found(conn, "Site could not be found") end end def delete_site(conn, %{"site_id" => site_id}) do - site = Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner]) + case get_site(conn.assigns.current_user, site_id, [:owner]) do + {:ok, site} -> + {:ok, _} = Plausible.Site.Removal.run(site.domain) + json(conn, %{"deleted" => true}) - if site do - {:ok, _} = Plausible.Site.Removal.run(site.domain) - json(conn, %{"deleted" => true}) - else - H.not_found(conn, "Site could not be found") + {:error, :site_not_found} -> + H.not_found(conn, "Site could not be found") end end def update_site(conn, %{"site_id" => site_id} = params) do # for now this only allows to change the domain - site = Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]) - - if site do - case Plausible.Site.Domain.change(site, params["domain"]) do - {:ok, site} -> - json(conn, site) - - {:error, changeset} -> - conn - |> put_status(400) - |> json(serialize_errors(changeset)) - end + with {:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]), + {:ok, site} <- Plausible.Site.Domain.change(site, params["domain"]) do + json(conn, site) else - H.not_found(conn, "Site could not be found") - end - end + {:error, :site_not_found} -> + H.not_found(conn, "Site could not be found") - defp expect_param_key(params, key) do - case Map.fetch(params, key) do - :error -> {:missing, key} - res -> res + {:error, %Ecto.Changeset{} = changeset} -> + conn + |> put_status(400) + |> json(serialize_errors(changeset)) end end def find_or_create_shared_link(conn, params) do with {:ok, site_id} <- expect_param_key(params, "site_id"), {:ok, link_name} <- expect_param_key(params, "name"), - site when not is_nil(site) <- - Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]) do + {:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]) do shared_link = Repo.get_by(Plausible.Site.SharedLink, site_id: site.id, name: link_name) shared_link = @@ -96,7 +133,7 @@ defmodule PlausibleWeb.Api.ExternalSitesController do }) end else - nil -> + {:error, :site_not_found} -> H.not_found(conn, "Site could not be found") {:missing, "site_id"} -> @@ -113,12 +150,11 @@ defmodule PlausibleWeb.Api.ExternalSitesController do def find_or_create_goal(conn, params) do with {:ok, site_id} <- expect_param_key(params, "site_id"), {:ok, _} <- expect_param_key(params, "goal_type"), - site when not is_nil(site) <- - Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]), + {:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]), {:ok, goal} <- Goals.find_or_create(site, params) do json(conn, goal) else - nil -> + {:error, :site_not_found} -> H.not_found(conn, "Site could not be found") {:missing, param} -> @@ -132,19 +168,16 @@ defmodule PlausibleWeb.Api.ExternalSitesController do def delete_goal(conn, params) do with {:ok, site_id} <- expect_param_key(params, "site_id"), {:ok, goal_id} <- expect_param_key(params, "goal_id"), - site when not is_nil(site) <- - Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]) do - case Goals.delete(goal_id, site) do - :ok -> - json(conn, %{"deleted" => true}) - - {:error, :not_found} -> - H.not_found(conn, "Goal could not be found") - end + {:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]), + :ok <- Goals.delete(goal_id, site) do + json(conn, %{"deleted" => true}) else - nil -> + {:error, :site_not_found} -> H.not_found(conn, "Site could not be found") + {:error, :not_found} -> + H.not_found(conn, "Goal could not be found") + {:missing, "site_id"} -> H.bad_request(conn, "Parameter `site_id` is required to delete a goal") @@ -156,9 +189,23 @@ defmodule PlausibleWeb.Api.ExternalSitesController do end end + defp get_site(user, site_id, roles) do + case Sites.get_for_user(user.id, site_id, roles) do + nil -> {:error, :site_not_found} + site -> {:ok, site} + end + end + defp serialize_errors(changeset) do {field, {msg, _opts}} = List.first(changeset.errors) error_msg = Atom.to_string(field) <> ": " <> msg %{"error" => error_msg} end + + defp expect_param_key(params, key) do + case Map.fetch(params, key) do + :error -> {:missing, key} + res -> res + end + end end diff --git a/lib/plausible_web/router.ex b/lib/plausible_web/router.ex index 7c281e06c54a..f4802db34c52 100644 --- a/lib/plausible_web/router.ex +++ b/lib/plausible_web/router.ex @@ -188,7 +188,9 @@ defmodule PlausibleWeb.Router do scope assigns: %{api_scope: "sites:read:*"} do pipe_through PlausibleWeb.Plugs.AuthorizePublicAPI + get "/", ExternalSitesController, :index get "/:site_id", ExternalSitesController, :get_site + get "/goals", ExternalSitesController, :goals_index end scope assigns: %{api_scope: "sites:provision:*"} do diff --git a/test/plausible_web/controllers/api/external_sites_controller_test.exs b/test/plausible_web/controllers/api/external_sites_controller_test.exs index 0a079648ac8b..f2769cc4a69c 100644 --- a/test/plausible_web/controllers/api/external_sites_controller_test.exs +++ b/test/plausible_web/controllers/api/external_sites_controller_test.exs @@ -495,7 +495,11 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do test "get a site by its domain", %{conn: conn, site: site} do conn = get(conn, "/api/v1/sites/" <> site.domain) - assert json_response(conn, 200) == %{"domain" => site.domain, "timezone" => site.timezone} + assert json_response(conn, 200) == %{ + "domain" => site.domain, + "timezone" => site.timezone, + "allowed_custom_props" => [] + } end test "get a site by old site_id after domain change", %{conn: conn, site: site} do @@ -506,7 +510,11 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do conn = get(conn, "/api/v1/sites/" <> old_domain) - assert json_response(conn, 200) == %{"domain" => new_domain, "timezone" => site.timezone} + assert json_response(conn, 200) == %{ + "domain" => new_domain, + "timezone" => site.timezone, + "allowed_custom_props" => [] + } end test "get a site with basic scope config", %{conn: conn, user: user, site: site} do @@ -517,7 +525,11 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do |> Plug.Conn.put_req_header("authorization", "Bearer #{api_key.key}") |> get("/api/v1/sites/" <> site.domain) - assert json_response(conn, 200) == %{"domain" => site.domain, "timezone" => site.timezone} + assert json_response(conn, 200) == %{ + "domain" => site.domain, + "timezone" => site.timezone, + "allowed_custom_props" => [] + } end test "is 404 when site cannot be found", %{conn: conn} do