diff --git a/lib/plausible/google/api.ex b/lib/plausible/google/api.ex index 14df65ae3..6a6f2f7d1 100644 --- a/lib/plausible/google/api.ex +++ b/lib/plausible/google/api.ex @@ -1,5 +1,6 @@ defmodule Plausible.Google.Api do @scope URI.encode_www_form("https://www.googleapis.com/auth/webmasters.readonly email") + @verified_permission_levels ["siteOwner", "siteFullUser", "siteRestrictedUser"] def authorize_url(site_id) do if Mix.env() == :test do @@ -14,32 +15,20 @@ defmodule Plausible.Google.Api do Jason.decode!(res.body) end - def fetch_site(domain, auth) do - auth = if Timex.before?(auth.expires, Timex.now() |> Timex.shift(seconds: 5)) do - refresh_token(auth) - else - auth - end - with_https = URI.encode_www_form("https://#{domain}") - - res = HTTPoison.get!("https://www.googleapis.com/webmasters/v3/sites/#{with_https}",["Content-Type": "application/json", "Authorization": "Bearer #{auth.access_token}"]) - + def fetch_verified_properties(auth) do + auth = refresh_if_needed(auth) + res = HTTPoison.get!("https://www.googleapis.com/webmasters/v3/sites",["Content-Type": "application/json", "Authorization": "Bearer #{auth.access_token}"]) Jason.decode!(res.body) + |> Map.get("siteEntry") + |> Enum.filter(fn site -> site["permissionLevel"] in @verified_permission_levels end) + |> Enum.map(fn site -> site["siteUrl"] end) end - def fetch_stats(site, auth, query) do - if Timex.before?(auth.expires, Timex.now() |> Timex.shift(seconds: 5)) do - auth = refresh_token(auth) - fetch_queries(site.domain, auth, query) - else - fetch_queries(site.domain, auth, query) - end - end + def fetch_stats(auth, query) do + auth = refresh_if_needed(auth) + property = URI.encode_www_form(auth.property) - defp fetch_queries(domain, auth, query) do - with_https = URI.encode_www_form("https://#{domain}") - - res = HTTPoison.post!("https://www.googleapis.com/webmasters/v3/sites/#{with_https}/searchAnalytics/query", Jason.encode!(%{ + res = HTTPoison.post!("https://www.googleapis.com/webmasters/v3/sites/#{property}/searchAnalytics/query", Jason.encode!(%{ startDate: Date.to_iso8601(query.date_range.first), endDate: Date.to_iso8601(query.date_range.last), dimensions: ["query"], @@ -66,6 +55,14 @@ defmodule Plausible.Google.Api do end end + defp refresh_if_needed(auth) do + if Timex.before?(auth.expires, Timex.now() |> Timex.shift(seconds: 30)) do + refresh_token(auth) + else + auth + end + end + defp refresh_token(auth) do res = HTTPoison.post!("https://www.googleapis.com/oauth2/v4/token", "client_id=#{client_id()}&client_secret=#{client_secret()}&refresh_token=#{auth.refresh_token}&grant_type=refresh_token&redirect_uri=#{redirect_uri()}", ["Content-Type": "application/x-www-form-urlencoded"]) body = Jason.decode!(res.body) diff --git a/lib/plausible/site/google_auth.ex b/lib/plausible/site/google_auth.ex index c391c7173..d19b9d5a9 100644 --- a/lib/plausible/site/google_auth.ex +++ b/lib/plausible/site/google_auth.ex @@ -4,6 +4,7 @@ defmodule Plausible.Site.GoogleAuth do schema "google_auth" do field :email, :string + field :property, :string field :refresh_token, :string field :access_token, :string field :expires, :naive_datetime @@ -20,4 +21,9 @@ defmodule Plausible.Site.GoogleAuth do |> validate_required([:refresh_token, :access_token, :expires, :email, :user_id, :site_id]) |> unique_constraint(:site) end + + def set_property(auth, attrs \\ %{}) do + auth + |> cast(attrs, [:property]) + end end diff --git a/lib/plausible_web/controllers/site_controller.ex b/lib/plausible_web/controllers/site_controller.ex index 04eb6feae..282802234 100644 --- a/lib/plausible_web/controllers/site_controller.ex +++ b/lib/plausible_web/controllers/site_controller.ex @@ -37,9 +37,8 @@ defmodule PlausibleWeb.SiteController do site = Sites.get_for_user!(conn.assigns[:current_user].id, website) |> Repo.preload(:google_auth) - google_search_console_verified = if site.google_auth do - google_site = Plausible.Google.Api.fetch_site(site.domain, site.google_auth) - !google_site["error"] + search_console_domains = if site.google_auth do + Plausible.Google.Api.fetch_verified_properties(site.google_auth) end weekly_report = Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id) @@ -53,11 +52,23 @@ defmodule PlausibleWeb.SiteController do site: site, weekly_report_changeset: weekly_report_changeset, monthly_report_changeset: monthly_report_changeset, - google_search_console_verified: google_search_console_verified, + search_console_domains: search_console_domains, changeset: Plausible.Site.changeset(site, %{}) ) end + def update_google_auth(conn, %{"website" => website, "google_auth" => attrs}) do + site = Sites.get_for_user!(conn.assigns[:current_user].id, website) + |> Repo.preload(:google_auth) + + Plausible.Site.GoogleAuth.set_property(site.google_auth, attrs) + |> Repo.update! + + conn + |> put_flash(:success, "Google integration saved succesfully") + |> redirect(to: "/#{site.domain}/settings") + end + def update_settings(conn, %{"website" => website, "site" => site_params}) do site = Sites.get_for_user!(conn.assigns[:current_user].id, website) changeset = site |> Plausible.Site.changeset(site_params) @@ -179,6 +190,20 @@ defmodule PlausibleWeb.SiteController do |> redirect(to: "/#{site.domain}/settings") end + def google_settings(conn, %{"website" => website}) do + site = Sites.get_for_user!(conn.assigns[:current_user].id, website) + |> Repo.preload(:google_auth) + + verified_domains = Plausible.Google.Api.fetch_verified_properties(site.google_auth) + + render(conn, + "google_settings.html", + site: site, + verified_domains: verified_domains, + layout: {PlausibleWeb.LayoutView, "focus.html"} + ) + end + defp insert_site(user_id, params) do site_changeset = Plausible.Site.changeset(%Plausible.Site{}, params) diff --git a/lib/plausible_web/controllers/stats_controller.ex b/lib/plausible_web/controllers/stats_controller.ex index e6ac5b163..139e398cc 100644 --- a/lib/plausible_web/controllers/stats_controller.ex +++ b/lib/plausible_web/controllers/stats_controller.ex @@ -213,8 +213,8 @@ defmodule PlausibleWeb.StatsController do {conn, params} = fetch_period(conn, site) query = Stats.Query.from(site.timezone, params) total_visitors = Stats.visitors_from_referrer(site, query, "Google") - search_terms = if site.google_auth do - Plausible.Google.Api.fetch_stats(site, site.google_auth, query) + search_terms = if site.google_auth && site.google_auth.property do + Plausible.Google.Api.fetch_stats(site.google_auth, query) end render(conn, "google_referrer.html", diff --git a/lib/plausible_web/router.ex b/lib/plausible_web/router.ex index 9195f029a..40ed821f4 100644 --- a/lib/plausible_web/router.ex +++ b/lib/plausible_web/router.ex @@ -94,6 +94,7 @@ defmodule PlausibleWeb.Router do get "/:website/snippet", SiteController, :add_snippet get "/:website/settings", SiteController, :settings put "/:website/settings", SiteController, :update_settings + put "/:website/settings/google", SiteController, :update_google_auth delete "/:website", SiteController, :delete_site get "/stats/:domain/referrers", StatsController, :referrers_preview diff --git a/lib/plausible_web/templates/layout/app.html.eex b/lib/plausible_web/templates/layout/app.html.eex index 915a483d4..e39a8321c 100644 --- a/lib/plausible_web/templates/layout/app.html.eex +++ b/lib/plausible_web/templates/layout/app.html.eex @@ -6,7 +6,7 @@ "> -
Select the Google Search Console property you would like to pull keyword data from
+ ++ NB: Your Google account does not have access to your currently configured property, <%= @site.google_auth.property %>. Please select a verified property from the list below. +
<% else %> - - - No access to site https://<%= @site.domain %> -+ Select the Google Search Console property you would like to pull keyword data from. If you don't see your domain, <%= link("set it up and verify", to: "https://docs.plausible.io/google-search-console-integration#1-add-your-site-on-the-search-console", class: "text-indigo") %> on Search Console first. +
+ <% end %> + + <%= form_for Plausible.Site.GoogleAuth.changeset(@site.google_auth), "/#{@site.domain}/settings/google", [class: "max-w-xs"], fn f -> %> +