mirror of
https://github.com/plausible/analytics.git
synced 2024-12-28 12:01:39 +03:00
Merge pull request #14 from plausible-insights/configure-search-console-domain
Configure search console domain
This commit is contained in:
commit
31aab3878b
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
16
lib/plausible_web/templates/site/google_settings.html.eex
Normal file
16
lib/plausible_web/templates/site/google_settings.html.eex
Normal 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 %>
|
@ -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>
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user