Merge pull request #14 from plausible-insights/configure-search-console-domain

Configure search console domain
This commit is contained in:
Uku Taht 2019-10-10 12:13:25 +08:00 committed by GitHub
commit 31aab3878b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 114 additions and 49 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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",

View File

@ -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

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content="Plausible is a simple, lightweight web analytics service. Monitor your website traffic while respecting your visitors' privacy."/>
<link rel="icon" type="image/png" sizes="32x32" href="<%= PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_favicon.png") %>">
<title><%= assigns[:title] || "Plausible · Simple web analytics" %></title>
<title><%= assigns[:title] || "Plausible · Simple, open-source web analytics" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<%= render("_tracking.html", assigns) %>
</head>

View File

@ -0,0 +1,16 @@
<%= form_for @conn, "/google", [class: "max-w-sm w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2>Connect to property</h2>
<p class="text-grey-dark mt-4">Select the Google Search Console property you would like to pull keyword data from</p>
<div class="my-6">
<%= label f, :domain, class: "block text-grey-darker text-sm font-bold mb-2" %>
<div class="inline-block relative w-full">
<%= select f, :domain, @verified_domains, class: "block appearance-none w-full bg-grey-lighter text-grey-darker cursor-pointer hover:border-grey p-2 pr-8 rounded leading-normal focus:outline-none" %>
<div class="pointer-events-none absolute pin-y pin-r flex items-center px-2 text-red">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>
</div>
</div>
</div>
<%= submit "Enable Integration →", class: "button mt-4 w-full" %>
<% end %>

View File

@ -62,34 +62,35 @@
<%= if @site.google_auth do %>
<div class="py-2"></div>
<svg class="w-6 h-6 mr-r text-green" style="transform: translateY(0.4rem);">
<use xlink:href="#feather-check-circle" />
</svg>
<span class="text-grey-darker">Connected Google account: <b><%= @site.google_auth.email %></b></span>
<%= if @google_search_console_verified do %>
<div class="py-2"></div>
<svg class="w-6 h-6 mr-r text-green" style="transform: translateY(0.4rem);">
<use xlink:href="#feather-check-circle" />
</svg>
<span class="text-grey-darker">Verified access to site <b>https://<%= @site.domain %></b></span>
<div class="mt-6">
<%= link("View google stats", to: "/#{@site.domain}/referrers/Google", class: "text-indigo") %>
</div>
<%= if @site.google_auth.property && !(@site.google_auth.property in @search_console_domains) do %>
<p class="text-grey-darker mt-6 font-bold">
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.
</p>
<% else %>
<div class="py-2"></div>
<svg class="w-6 h-6 mr-r text-red-dark" style="transform: translateY(0.4rem);">
<use xlink:href="#feather-x-circle" />
</svg>
<span class="text-grey-darker">No access to site <b>https://<%= @site.domain %></b></span>
<div class="mt-6">
<%= link("Verify your site on Google Search Console to continue", to: "https://support.google.com/webmasters/answer/34592", class: "text-indigo") %>
<p class="text-grey-darker mt-6">
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.
</p>
<% end %>
<%= form_for Plausible.Site.GoogleAuth.changeset(@site.google_auth), "/#{@site.domain}/settings/google", [class: "max-w-xs"], fn f -> %>
<div class="my-6">
<div class="inline-block relative w-full">
<%= select f, :property, @search_console_domains, prompt: "(Choose property)", class: "block appearance-none w-full bg-grey-lighter text-grey-darker cursor-pointer hover:border-grey p-2 pr-8 rounded leading-normal focus:outline-none" %>
<div class="pointer-events-none absolute pin-y pin-r flex items-center px-2 text-red">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>
</div>
</div>
</div>
<%= submit "Save", class: "button" %>
<% end %>
<% else %>
<%= button("Continue with Google", to: Plausible.Google.Api.authorize_url(@site.id), class: "button mt-4") %>
<div class="text-grey-darker mt-8">
NB: You also need to set up your site on <%= link("Google Search Console", to: "https://search.google.com/search-console/about") %> for the integration to work.
NB: You also need to set up your site on <%= link("Google Search Console", to: "https://search.google.com/search-console/about") %> for the integration to work. <%= link("Read the docs", to: "https://docs.plausible.io/google-search-console-integration", class: "text-indigo") %>
</div>
<% end %>
</div>

View File

@ -0,0 +1,19 @@
defmodule Plausible.Repo.Migrations.AddPropertyToGoogleAuth do
use Ecto.Migration
use Plausible.Repo
def change do
alter table(:google_auth) do
add :property, :text
end
flush()
for auth <- Repo.all(Plausible.Site.GoogleAuth) do
auth = Repo.preload(auth, :site)
property = "https://#{auth.site.domain}"
Plausible.Site.GoogleAuth.set_property(auth, %{property: property})
|> Repo.update!
end
end
end