Configurable site id (#30)

* Use site id instead of hostname for events

* Use site id in domain status check

* Revert change to tracking module

* Catch more places where link generation needed updating

* Rename site_id to domain

* Drop hostname index from events
This commit is contained in:
Uku Taht 2020-02-04 15:44:13 +02:00 committed by GitHub
parent ccbc04f6bd
commit 7dbbc8ba22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 316 additions and 261 deletions

View File

@ -22,7 +22,7 @@ export default class Browsers extends React.Component {
}
fetchBrowsers() {
api.get(`/api/stats/${this.props.site.domain}/browsers`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/browsers`, this.props.query)
.then((res) => this.setState({loading: false, browsers: res}))
}

View File

@ -24,7 +24,7 @@ export default class Conversions extends React.Component {
}
fetchConversions() {
api.get(`/api/stats/${this.props.site.domain}/conversions`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/conversions`, this.props.query)
.then((res) => this.setState({loading: false, goals: res}))
}

View File

@ -24,7 +24,7 @@ export default class Countries extends React.Component {
}
fetchCountries() {
api.get(`/api/stats/${this.props.site.domain}/countries`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/countries`, this.props.query)
.then((res) => this.setState({loading: false, countries: res}))
}

View File

@ -19,7 +19,7 @@ export default class CurrentVisitors extends React.Component {
}
updateCount() {
return fetch(`/api/stats/${this.props.site.domain}/current-visitors`)
return fetch(`/api/stats/${encodeURIComponent(this.props.site.domain)}/current-visitors`)
.then(res => res.json())
.then((res) => this.setState({currentVisitors: res}))
}

View File

@ -16,7 +16,7 @@ class BrowsersModal extends React.Component {
componentDidMount() {
const query = parseQuery(this.props.location.search, this.props.site)
api.get(`/api/stats/${this.props.site.domain}/browsers`, query, {limit: 100})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/browsers`, query, {limit: 100})
.then((res) => this.setState({loading: false, browsers: res}))
}

View File

@ -16,7 +16,7 @@ class CountriesModal extends React.Component {
componentDidMount() {
const query = parseQuery(this.props.location.search, this.props.site)
api.get(`/api/stats/${this.props.site.domain}/countries`, query, {limit: 100})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/countries`, query, {limit: 100})
.then((res) => this.setState({loading: false, countries: res}))
}

View File

@ -18,7 +18,7 @@ class GoogleKeywordsModal extends React.Component {
}
componentDidMount() {
api.get(`/api/stats/${this.props.site.domain}/referrers/Google`, this.state.query, {limit: 100})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers/Google`, this.state.query, {limit: 100})
.then((res) => this.setState({
loading: false,
searchTerms: res.search_terms,
@ -56,7 +56,7 @@ class GoogleKeywordsModal extends React.Component {
<RocketIcon />
<div className="text-lg">The site is not connected to Google Search Keywords</div>
<div className="text-lg">Configure the integration to view search terms</div>
<a href={`/${this.props.site.domain}/settings#google-auth`} className="button mt-4">Connect with Google</a>
<a href={`/${encodeURIComponent(this.props.site.domain)}/settings#google-auth`} className="button mt-4">Connect with Google</a>
</div>
)
} else {
@ -93,7 +93,7 @@ class GoogleKeywordsModal extends React.Component {
return (
<React.Fragment>
<header className="modal__header">
<Link to={`/${this.props.site.domain}/referrers${window.location.search}`} className="font-bold text-grey-darker hover:underline"> All referrers</Link>
<Link to={`/${encodeURIComponent(this.props.site.domain)}/referrers${window.location.search}`} className="font-bold text-grey-darker hover:underline"> All referrers</Link>
</header>
<div className="my-4 border-b border-grey-light"></div>

View File

@ -39,7 +39,7 @@ class Modal extends React.Component {
}
close() {
this.props.history.push(`/${this.props.site.domain}${this.props.location.search}`)
this.props.history.push(`/${encodeURIComponent(this.props.site.domain)}${this.props.location.search}`)
}
render() {

View File

@ -16,7 +16,7 @@ class OperatingSystemsModal extends React.Component {
componentDidMount() {
const query = parseQuery(this.props.location.search, this.props.site)
api.get(`/api/stats/${this.props.site.domain}/operating-systems`, query, {limit: 100})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/operating-systems`, query, {limit: 100})
.then((res) => this.setState({loading: false, systems: res}))
}

View File

@ -18,7 +18,7 @@ class PagesModal extends React.Component {
componentDidMount() {
const include = this.showBounceRate() ? 'bounce_rate' : null
api.get(`/api/stats/${this.props.site.domain}/pages`, this.state.query, {limit: 100, include: include})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/pages`, this.state.query, {limit: 100, include: include})
.then((res) => this.setState({loading: false, pages: res}))
}

View File

@ -17,12 +17,12 @@ class ReferrerDrilldownModal extends React.Component {
componentDidMount() {
if (this.state.query.filters.goal) {
api.get(`/api/stats/${this.props.site.domain}/goal/referrers/${this.props.match.params.referrer}`, this.state.query, {limit: 100})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/goal/referrers/${this.props.match.params.referrer}`, this.state.query, {limit: 100})
.then((res) => this.setState({loading: false, referrers: res.referrers, totalVisitors: res.total_visitors}))
} else {
const include = this.showBounceRate() ? 'bounce_rate' : null
api.get(`/api/stats/${this.props.site.domain}/referrers/${this.props.match.params.referrer}`, this.state.query, {limit: 100, include: include})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers/${this.props.match.params.referrer}`, this.state.query, {limit: 100, include: include})
.then((res) => this.setState({loading: false, referrers: res.referrers, totalVisitors: res.total_visitors}))
}
}
@ -119,7 +119,7 @@ class ReferrerDrilldownModal extends React.Component {
return (
<React.Fragment>
<header className="modal__header">
<Link to={`/${this.props.site.domain}/referrers${window.location.search}`} className="font-bold text-grey-darker hover:underline"> All referrers</Link>
<Link to={`/${encodeURIComponent(this.props.site.domain)}/referrers${window.location.search}`} className="font-bold text-grey-darker hover:underline"> All referrers</Link>
</header>
<div className="my-4 border-b border-grey-light"></div>

View File

@ -17,12 +17,12 @@ class ReferrersModal extends React.Component {
componentDidMount() {
if (this.state.query.filters.goal) {
api.get(`/api/stats/${this.props.site.domain}/goal/referrers`, this.state.query, {limit: 100})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/goal/referrers`, this.state.query, {limit: 100})
.then((res) => this.setState({loading: false, referrers: res}))
} else {
const include = this.showBounceRate() ? 'bounce_rate' : null
api.get(`/api/stats/${this.props.site.domain}/referrers`, this.state.query, {limit: 100, include: include})
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers`, this.state.query, {limit: 100, include: include})
.then((res) => this.setState({loading: false, referrers: res}))
}
}
@ -43,7 +43,7 @@ class ReferrersModal extends React.Component {
return (
<tr className="text-sm" key={referrer.name}>
<td className="p-2">
<Link className="hover:underline truncate" style={{maxWidth: '80%'}} to={`/${this.props.site.domain}/referrers/${referrer.name}${window.location.search}`}>{ referrer.name }</Link>
<Link className="hover:underline truncate" style={{maxWidth: '80%'}} to={`/${encodeURIComponent(this.props.site.domain)}/referrers/${referrer.name}${window.location.search}`}>{ referrer.name }</Link>
</td>
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(referrer.count)}</td>
{this.showBounceRate() && <td className="p-2 w-32 font-medium" align="right">{this.formatBounceRate(referrer)}</td> }

View File

@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'
export default function MoreLink({site, endpoint}) {
return (
<div className="text-center w-full absolute pin-b pin-l p-4">
<Link to={`/${site.domain}/${endpoint}${window.location.search}`}className="font-bold text-sm text-grey-dark hover:text-red transition tracking-wide">
<Link to={`/${encodeURIComponent(site.domain)}/${endpoint}${window.location.search}`}className="font-bold text-sm text-grey-dark hover:text-red transition tracking-wide">
<svg className="feather mr-1"><use xlinkHref="#feather-maximize" /></svg>
MORE
</Link>

View File

@ -22,7 +22,7 @@ export default class OperatingSystems extends React.Component {
}
fetchOperatingSystems() {
api.get(`/api/stats/${this.props.site.domain}/operating-systems`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/operating-systems`, this.props.query)
.then((res) => this.setState({loading: false, systems: res}))
}

View File

@ -26,7 +26,7 @@ export default class Pages extends React.Component {
}
fetchPages() {
api.get(`/api/stats/${this.props.site.domain}/pages`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/pages`, this.props.query)
.then((res) => this.setState({loading: false, pages: res}))
}

View File

@ -25,10 +25,10 @@ export default class Referrers extends React.Component {
fetchReferrers() {
if (this.props.query.filters.goal) {
api.get(`/api/stats/${this.props.site.domain}/goal/referrers`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/goal/referrers`, this.props.query)
.then((res) => this.setState({loading: false, referrers: res}))
} else {
api.get(`/api/stats/${this.props.site.domain}/referrers`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers`, this.props.query)
.then((res) => this.setState({loading: false, referrers: res}))
}
}
@ -37,7 +37,7 @@ export default class Referrers extends React.Component {
return (
<React.Fragment key={referrer.name}>
<div className="flex items-center justify-between my-2">
<Link className="hover:underline truncate" style={{maxWidth: '80%'}} to={`/${this.props.site.domain}/referrers/${referrer.name}${window.location.search}`}>{ referrer.name }</Link>
<Link className="hover:underline truncate" style={{maxWidth: '80%'}} to={`/${encodeURIComponent(this.props.site.domain)}/referrers/${referrer.name}${window.location.search}`}>{ referrer.name }</Link>
<span>{numberFormatter(referrer.count)}</span>
</div>
<Bar count={referrer.count} all={this.state.referrers} color="blue" />

View File

@ -48,7 +48,7 @@ export default class ScreenSizes extends React.Component {
}
fetchScreenSizes() {
api.get(`/api/stats/${this.props.site.domain}/screen-sizes`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/screen-sizes`, this.props.query)
.then((res) => this.setState({loading: false, sizes: res}))
}

View File

@ -289,7 +289,7 @@ class LineGraph extends React.Component {
}
downloadLink() {
const endpoint = `/${this.props.site.domain}/visitors.csv${api.serializeQuery(this.props.query)}`
const endpoint = `/${encodeURIComponent(this.props.site.domain)}/visitors.csv${api.serializeQuery(this.props.query)}`
return (
<a href={endpoint} download>
@ -337,7 +337,7 @@ export default class VisitorGraph extends React.Component {
}
fetchGraphData() {
api.get(`/api/stats/${this.props.site.domain}/main-graph`, this.props.query)
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/main-graph`, this.props.query)
.then((res) => {
this.setState({loading: false, graphData: res})
return res

View File

@ -2,6 +2,10 @@
'use strict';
try {
const CONFIG = {
domain: window.location.hostname
}
function setCookie(name,value) {
var date = new Date();
date.setTime(date.getTime() + (3*365*24*60*60*1000)); // 3 YEARS
@ -76,6 +80,7 @@
var payload = getUserData()
payload.name = eventName
payload.url = getUrl()
payload.domain = CONFIG['domain']
var request = new XMLHttpRequest();
request.open('POST', plausibleHost + '/api/event', true);
@ -113,10 +118,15 @@
}
}
function configure(key, val) {
CONFIG[key] = val
}
const functions = {
page: page,
trigger: trigger,
trackPushState: trackPushState
trackPushState: trackPushState,
configure: configure
}
const queue = window.plausible.q || []

View File

@ -33,8 +33,8 @@ defmodule Mix.Tasks.SendEmailReports do
query = Stats.Query.from(site.timezone, %{"period" => "7d"})
for email <- site.weekly_report.recipients do
Logger.info("Sending weekly report for #{site.domain} to #{email}")
unsubscribe_link = PlausibleWeb.Endpoint.url() <> "/sites/#{site.domain}/weekly-report/unsubscribe?email=#{email}"
Logger.info("Sending weekly report for #{URI.encode_www_form(site.domain)} to #{email}")
unsubscribe_link = PlausibleWeb.Endpoint.url() <> "/sites/#{URI.encode_www_form(site.domain)}/weekly-report/unsubscribe?email=#{email}"
send_report(email, site, "Weekly", unsubscribe_link, query)
end
@ -59,7 +59,7 @@ defmodule Mix.Tasks.SendEmailReports do
for email <- site.monthly_report.recipients do
Logger.info("Sending monthly report for #{site.domain} to #{email}")
unsubscribe_link = PlausibleWeb.Endpoint.url() <> "/sites/#{site.domain}/monthly-report/unsubscribe?email=#{email}"
unsubscribe_link = PlausibleWeb.Endpoint.url() <> "/sites/#{URI.encode_www_form(site.domain)}/monthly-report/unsubscribe?email=#{email}"
send_report(email, site, Timex.format!(last_month, "{Mfull}"), unsubscribe_link, query)
end
@ -115,11 +115,11 @@ defmodule Mix.Tasks.SendEmailReports do
end
defp view_link(site, %Plausible.Stats.Query{period: "7d"}) do
PlausibleWeb.Endpoint.url() <> "/#{site.domain}?period=7d"
PlausibleWeb.Endpoint.url() <> "/#{URI.encode_www_form(site.domain)}?period=7d"
end
defp view_link(site, %Plausible.Stats.Query{period: "month", date_range: range}) do
month = Timex.format!(range.first, "{ISOdate}")
PlausibleWeb.Endpoint.url() <> "/#{site.domain}?period=month&date=#{month}"
PlausibleWeb.Endpoint.url() <> "/#{URI.encode_www_form(site.domain)}?period=month&date=#{month}"
end
end

View File

@ -44,42 +44,6 @@ defmodule Mix.Tasks.SendTrialNotifications do
nil
end
end
#two_weeks_left = from(
# u in base_query,
# where: type(u.inserted_at, :date) == fragment("now()::date - '14 days'::interval")
#)
#tomorrow = from(
# u in base_query,
# where: type(u.inserted_at, :date) == fragment("now()::date - '29 days'::interval")
#)
#today = from(
# u in base_query,
# where: type(u.inserted_at, :date) == fragment("now()::date - '30 days'::interval")
#)
#yesterday = from(
# u in base_query,
# where: type(u.inserted_at, :date) == fragment("now()::date - '31 days'::interval")
#)
#for user <- Repo.all(two_weeks_left) do
# if Plausible.Auth.user_completed_setup?(user), do: send_two_week_reminder(args, user)
#end
#for user <- Repo.all(tomorrow) do
# if Plausible.Auth.user_completed_setup?(user), do: send_tomorrow_reminder(args, user)
#end
#for user <- Repo.all(today) do
# if Plausible.Auth.user_completed_setup?(user), do: send_today_reminder(args, user)
#end
#for user <- Repo.all(yesterday) do
# if Plausible.Auth.user_completed_setup?(user), do: send_over_reminder(args, user)
#end
end
defp send_two_week_reminder(["--dry-run"], user) do

View File

@ -17,7 +17,7 @@ defmodule Plausible.Auth do
from(
e in Plausible.Event,
join: s in Plausible.Site,
on: s.domain == e.hostname,
on: s.domain == e.domain,
join: sm in Plausible.Site.Membership,
on: sm.site_id == s.id,
join: u in Plausible.Auth.User,

View File

@ -101,7 +101,7 @@ defmodule Plausible.Billing do
defp site_usage(site) do
Repo.aggregate(from(
e in Plausible.Event,
where: e.hostname == ^site.domain,
where: e.domain == ^site.domain,
where: e.timestamp >= fragment("now() - '30 days'::interval")
), :count, :id)
end

View File

@ -4,6 +4,7 @@ defmodule Plausible.Event do
schema "events" do
field :name, :string
field :domain, :string
field :hostname, :string
field :pathname, :string
field :new_visitor, :boolean
@ -21,7 +22,7 @@ defmodule Plausible.Event do
def changeset(pageview, attrs) do
pageview
|> cast(attrs, [:name, :hostname, :pathname, :referrer, :new_visitor, :user_id, :operating_system, :browser, :referrer_source, :country_code, :screen_size])
|> validate_required([:name, :hostname, :pathname, :new_visitor, :user_id])
|> cast(attrs, [:name, :domain, :hostname, :pathname, :referrer, :new_visitor, :user_id, :operating_system, :browser, :referrer_source, :country_code, :screen_size])
|> validate_required([:name, :domain, :hostname, :pathname, :new_visitor, :user_id])
end
end

View File

@ -48,6 +48,7 @@ defmodule Plausible.Ingest.Session do
Plausible.Session.changeset(%Plausible.Session{}, %{
hostname: first_event.hostname,
domain: first_event.domain,
user_id: first_event.user_id,
new_visitor: first_event.new_visitor,
entry_page: first_event.pathname,

View File

@ -4,6 +4,7 @@ defmodule Plausible.Session do
schema "sessions" do
field :hostname, :string
field :domain, :string
field :new_visitor, :boolean
field :user_id, :binary_id
@ -25,7 +26,7 @@ defmodule Plausible.Session do
def changeset(session, attrs) do
session
|> cast(attrs, [:hostname, :entry_page, :exit_page, :referrer, :new_visitor, :user_id, :start, :length, :is_bounce, :operating_system, :browser, :referrer_source, :country_code, :screen_size])
|> validate_required([:hostname, :new_visitor, :user_id, :is_bounce, :start])
|> cast(attrs, [:hostname, :domain, :entry_page, :exit_page, :referrer, :new_visitor, :user_id, :start, :length, :is_bounce, :operating_system, :browser, :referrer_source, :country_code, :screen_size])
|> validate_required([:hostname, :domain, :new_visitor, :user_id, :is_bounce, :start])
end
end

View File

@ -14,7 +14,7 @@ defmodule Plausible.Sites do
def has_pageviews?(site) do
Repo.exists?(
from e in Plausible.Event,
where: e.hostname == ^site.domain
where: e.domain == ^site.domain
)
end

View File

@ -126,7 +126,7 @@ defmodule Plausible.Stats do
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
sessions_query = from(s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.new_visitor,
where: s.start >= ^first_datetime and s.start < ^last_datetime
)
@ -143,7 +143,7 @@ defmodule Plausible.Stats do
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
Repo.one(from s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.start >= ^first_datetime and s.start < ^last_datetime,
select: coalesce(avg(s.length), 0)
) |> Decimal.round |> Decimal.to_integer
@ -199,7 +199,7 @@ defmodule Plausible.Stats do
total_sessions_by_referrer = Repo.all(
from s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.new_visitor,
where: s.start >= ^first_datetime and s.start < ^last_datetime,
where: s.referrer_source in ^referrers,
@ -209,7 +209,7 @@ defmodule Plausible.Stats do
bounced_sessions_by_referrer = Repo.all(
from s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.new_visitor,
where: s.start >= ^first_datetime and s.start < ^last_datetime,
where: s.is_bounce,
@ -300,7 +300,7 @@ defmodule Plausible.Stats do
total_sessions_by_url = Repo.all(
from s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.new_visitor,
where: s.start >= ^first_datetime and s.start < ^last_datetime,
where: s.referrer in ^referring_urls,
@ -310,7 +310,7 @@ defmodule Plausible.Stats do
bounced_sessions_by_url = Repo.all(
from s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.new_visitor,
where: s.start >= ^first_datetime and s.start < ^last_datetime,
where: s.is_bounce,
@ -355,7 +355,7 @@ defmodule Plausible.Stats do
total_sessions_by_url = Repo.all(
from s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.new_visitor,
where: s.start >= ^first_datetime and s.start < ^last_datetime,
where: s.entry_page in ^page_urls,
@ -365,7 +365,7 @@ defmodule Plausible.Stats do
bounced_sessions_by_url = Repo.all(
from s in Plausible.Session,
where: s.hostname == ^site.domain,
where: s.domain == ^site.domain,
where: s.new_visitor,
where: s.start >= ^first_datetime and s.start < ^last_datetime,
where: s.is_bounce,
@ -453,7 +453,7 @@ defmodule Plausible.Stats do
Repo.one(
from e in Plausible.Event,
where: e.timestamp >= fragment("(now() at time zone 'utc') - '5 minutes'::interval"),
where: e.hostname == ^site.domain,
where: e.domain == ^site.domain,
select: count(e.user_id, :distinct)
)
end
@ -513,7 +513,7 @@ defmodule Plausible.Stats do
{goal_event, path} = event_name_for_goal(query)
q = from(e in Plausible.Event,
where: e.hostname == ^site.domain,
where: e.domain == ^site.domain,
where: e.timestamp >= ^first_datetime and e.timestamp < ^last_datetime
)

View File

@ -50,6 +50,7 @@ defmodule PlausibleWeb.Api.ExternalController do
event_attrs = %{
name: params["name"],
hostname: strip_www(uri.host),
domain: params["domain"] || strip_www(uri.host),
pathname: uri.path,
new_visitor: params["new_visitor"],
country_code: country_code,

View File

@ -5,7 +5,7 @@ defmodule PlausibleWeb.Api.InternalController do
def domain_status(conn, %{"domain" => domain}) do
has_pageviews = Repo.exists?(
from e in Plausible.Event,
where: e.hostname == ^domain
where: e.domain == ^domain
)
if has_pageviews do

View File

@ -208,6 +208,6 @@ defmodule PlausibleWeb.AuthController do
site = Repo.get(Plausible.Site, site_id)
redirect(conn, to: "/#{site.domain}/settings#google-auth")
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings#google-auth")
end
end

View File

@ -19,7 +19,7 @@ defmodule PlausibleWeb.SiteController do
Plausible.Slack.notify("#{user.name} created #{site.domain}")
conn
|> put_session(site.domain <> "_offer_email_report", true)
|> redirect(to: "/#{site.domain}/snippet")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/snippet")
{:error, :site, changeset, _} ->
render(conn, "new.html", changeset: changeset)
end
@ -52,7 +52,7 @@ defmodule PlausibleWeb.SiteController do
{:ok, _} ->
conn
|> put_flash(:success, "Goal created succesfully")
|> redirect(to: "/#{website}/settings")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings")
{:error, :goal, changeset, _} ->
conn
|> assign(:skip_plausible_tracking, true)
@ -69,7 +69,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Goal deleted succesfully")
|> redirect(to: "/#{website}/settings")
|> redirect(to: "/#{URI.encode_www_form(website)}/settings")
end
def settings(conn, %{"website" => website}) do
@ -107,7 +107,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Google integration saved succesfully")
|> redirect(to: "/#{site.domain}/settings")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#google-auth")
end
def update_settings(conn, %{"website" => website, "site" => site_params}) do
@ -122,7 +122,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_session(site_session_key, nil)
|> put_flash(:success, "Site settings saved succesfully")
|> redirect(to: "/#{site.domain}/settings")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings")
{:error, changeset} ->
render("settings.html", site: site, changeset: changeset)
end
@ -134,7 +134,7 @@ defmodule PlausibleWeb.SiteController do
|> Repo.preload(:google_auth)
Repo.delete_all(from sm in "site_memberships", where: sm.site_id == ^site.id)
Repo.delete_all(from e in "events", where: e.hostname == ^site.domain)
Repo.delete_all(from e in "events", where: e.domain == ^site.domain)
if site.google_auth do
Repo.delete!(site.google_auth)
@ -153,7 +153,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Congrats! Stats for #{site.domain} are now public.")
|> redirect(to: "/" <> site.domain <> "/settings")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings")
end
def make_private(conn, %{"website" => website}) do
@ -163,7 +163,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Stats for #{site.domain} are now private.")
|> redirect(to: "/" <> site.domain <> "/settings")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings")
end
def enable_weekly_report(conn, %{"website" => website}) do
@ -177,7 +177,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Success! You will receive an email report every Monday going forward")
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def disable_weekly_report(conn, %{"website" => website}) do
@ -186,7 +186,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Success! You will not receive weekly email reports going forward")
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def add_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -198,7 +198,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Succesfully added #{recipient} as a recipient for the weekly report")
|> redirect(to: "/#{site.domain}/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def remove_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -210,7 +210,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Succesfully removed #{recipient} as a recipient for the weekly report")
|> redirect(to: "/#{site.domain}/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def enable_monthly_report(conn, %{"website" => website}) do
@ -224,7 +224,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Success! You will receive an email report every month going forward")
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def disable_monthly_report(conn, %{"website" => website}) do
@ -233,7 +233,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Success! You will not receive monthly email reports going forward")
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def add_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -245,7 +245,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Succesfully added #{recipient} as a recipient for the monthly report")
|> redirect(to: "/#{site.domain}/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def remove_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -257,7 +257,7 @@ defmodule PlausibleWeb.SiteController do
conn
|> put_flash(:success, "Succesfully removed #{recipient} as a recipient for the monthly report")
|> redirect(to: "/#{site.domain}/settings#email-reports")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports")
end
def new_shared_link(conn, %{"website" => website}) do
@ -276,17 +276,18 @@ defmodule PlausibleWeb.SiteController do
case Repo.insert(changes) do
{:ok, _created} ->
redirect(conn, to: "/#{site.domain}/settings#visibility")
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings#visibility")
{:error, changeset} ->
render(conn, "new_shared_link.html", site: site, changeset: changeset, layout: {PlausibleWeb.LayoutView, "focus.html"})
end
end
def delete_shared_link(conn, %{"website" => website, "slug" => slug}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
Repo.get_by(Plausible.Site.SharedLink, slug: slug)
|> Repo.delete!
redirect(conn, to: "/#{website}/settings#visibility")
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings#visibility")
end
defp insert_site(user_id, params) do

View File

@ -7,10 +7,10 @@
<div class="overflow-hidden bg-white rounded max-w-md w-full shadow-md leading-normal">
<%= for site <- @sites do %>
<div class="w-full relative">
<a href="/<%= site.domain %>" class="block hover:bg-grey-lighter group p-4 border-b no-underline flex justify-between transition">
<a href="/<%= URI.encode_www_form(site.domain) %>" class="block hover:bg-grey-lighter group p-4 border-b no-underline flex justify-between transition">
<p class="w-full font-bold text-lg mb-1 text-grey-darkest"><%= site.domain %></p>
</a>
<%= link(to: "/#{site.domain}/settings", class: "flex absolute hover:bg-grey-lighter transition rounded py-3 px-5", style: "top: 6px; right: 6px;") do %>
<%= link(to: "/#{URI.encode_www_form(site.domain)}/settings", class: "flex absolute hover:bg-grey-lighter transition rounded py-3 px-5", style: "top: 6px; right: 6px;") do %>
<div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-settings"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
</div>

View File

@ -1,4 +1,4 @@
<%= form_for @changeset, "/#{@site.domain}/goals", [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 -> %>
<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/goals", [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>Add goal for <%= @site.domain %></h2>
<div class="mt-6 text-sm font-bold">Goal trigger</div>
<div class="my-3 w-full flex rounded border border-grey-light">

View File

@ -1,4 +1,4 @@
<%= form_for @changeset, "/sites/#{@site.domain}/shared-links", [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 -> %>
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/shared-links", [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>New shared link</h2>
<div class="my-6">
Add a password or leave it blank so anyone with the link can see the stats.

View File

@ -1,6 +1,6 @@
<div class="pt-24"></div>
<div class="max-w-md mx-auto flex justify-between">
<a href="/<%= @site.domain %>"><h1>Settings for <%= @site.domain %></h1></a>
<a href="/<%= URI.encode_www_form(@site.domain) %>"><h1>Settings for <%= @site.domain %></h1></a>
</div>
<div class="max-w-md mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-lightest px-8 pt-6 pb-8 mt-10">
<div class="flex items-center justify-between">
@ -9,7 +9,7 @@
<div class="my-4 border-b border-grey-light"></div>
<%= form_for @changeset, "/#{@site.domain}/settings", [class: "max-w-xs"], fn f -> %>
<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/settings", [class: "max-w-xs"], fn f -> %>
<div class="my-4">
<%= label f, :domain, class: "block text-grey-darker text-sm font-bold mb-2" %>
<%= text_input f, :domain, class: "transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:border-grey-light ", disabled: "disabled" %>
@ -37,18 +37,18 @@
<%= if @site.public do %>
Stats for <%= @site.domain %> are currently <b>public</b>. Anyone with the following link can view the stats:
<div class="relative text-sm mt-4">
<input type="text" id="public-link" value="https://plausible.io/<%= @site.domain %>" class="transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 pr-16 text-grey-darker appearance-none focus:outline-none focus:border-grey-light" />
<input type="text" id="public-link" value="https://plausible.io/<%= URI.encode_www_form(@site.domain) %>" class="transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 pr-16 text-grey-darker appearance-none focus:outline-none focus:border-grey-light" />
<a onclick="var input = document.getElementById('public-link'); input.focus(); input.select(); document.execCommand('copy');" href="javascript:void(0)" class="absolute pin-r text-indigo-darker font-bold p-2">
<svg class="feather-sm"><use xlink:href="#feather-copy" /></svg>
</a>
</div>
<%= button("Make stats private", to: "/sites/#{@site.domain}/make-private", method: "POST", class: "button mt-8") %>
<%= button("Make stats private", to: "/sites/#{URI.encode_www_form(@site.domain)}/make-private", method: "POST", class: "button mt-8") %>
<% else %>
<div class="text-grey-darker">
Stats for <%= @site.domain %> are currently <b>private</b>. You are the only person who can see them.
If you choose to make your stats public, anyone with the link will be able to view them.
</div>
<%= button("Make stats public", to: "/sites/#{@site.domain}/make-public", method: "POST", class: "button mt-8") %>
<%= button("Make stats public", to: "/sites/#{URI.encode_www_form(@site.domain)}/make-public", method: "POST", class: "button mt-8") %>
<% end %>
<div class="mt-16">
@ -67,14 +67,14 @@
<button onclick="var input = document.getElementById('<%= link.slug %>'); input.focus(); input.select(); document.execCommand('copy');" href="javascript:void(0)" class="py-2 px-4 bg-grey-lighter text-indigo-darker rounded-none border-r border-grey-light">
<svg class="feather-sm"><use xlink:href="#feather-copy" /></svg>
</button>
<%= button(to: "/sites/#{@site.domain}/shared-links/#{link.slug}", method: :delete, class: "py-2 px-4 bg-grey-lighter text-red-dark rounded-l-none", data: [confirm: "Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."]) do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/#{link.slug}", method: :delete, class: "py-2 px-4 bg-grey-lighter text-red-dark rounded-l-none", data: [confirm: "Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."]) do %>
<svg class="feather-sm"><use xlink:href="#feather-trash-2" /></svg>
<% end %>
</div>
<% end %>
</div>
<%= link("+ New link", to: "/sites/#{@site.domain}/shared-links/new", class: "button mt-4") %>
<%= link("+ New link", to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/new", class: "button mt-4") %>
</div>
<div class="max-w-md mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-lightest px-8 pt-6 pb-8 mt-10">
@ -85,7 +85,7 @@
<%= for goal <- @goals do %>
<div class="border-b border-grey-light py-3 flex justify-between">
<small class="font-bold"><%= goal_name(goal) %></small>
<%= button("", to: "/#{@site.domain}/goals/#{goal.id}", method: :delete, class: "text-sm", data: [confirm: "Are you sure you want to remove goal #{goal_name(goal)}? This will just affect the UI, all of your analytics data will stay intact."]) %>
<%= button("", to: "/#{URI.encode_www_form(@site.domain)}/goals/#{goal.id}", method: :delete, class: "text-sm", data: [confirm: "Are you sure you want to remove goal #{goal_name(goal)}? This will just affect the UI, all of your analytics data will stay intact."]) %>
</div>
<% end %>
<% else %>
@ -93,7 +93,7 @@
<% end %>
</div>
<%= link("+ Add goal", to: "/#{@site.domain}/goals/new", class: "button mt-6") %>
<%= link("+ Add goal", to: "/#{URI.encode_www_form(@site.domain)}/goals/new", class: "button mt-6") %>
</div>
<div class="max-w-md mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-lightest px-8 pt-6 pb-8 mt-10" id="google-auth">
@ -120,7 +120,7 @@
</p>
<% end %>
<%= form_for Plausible.Site.GoogleAuth.changeset(@site.google_auth), "/#{@site.domain}/settings/google", [class: "max-w-xs"], fn f -> %>
<%= form_for Plausible.Site.GoogleAuth.changeset(@site.google_auth), "/#{URI.encode_www_form(@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" %>
@ -148,11 +148,11 @@
<div class="my-8 flex items-center">
<%= if @weekly_report do %>
<%= button(to: "/sites/#{@site.domain}/weekly-report/disable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 bg-green justify-end") do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/disable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 bg-green justify-end") do %>
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
<% end %>
<% else %>
<%= button(to: "/sites/#{@site.domain}/weekly-report/enable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 justify-start") do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/enable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 justify-start") do %>
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
<% end %>
<% end %>
@ -164,10 +164,10 @@
<%= for recipient <- @weekly_report.recipients do %>
<div class="p-3 flex justify-between bg-grey-lighter rounded my-2 max-w-sm">
<span><svg class="feather mr-1" style="transform: translateY(0.05em)"><use xlink:href="#feather-mail" /></svg><%= recipient %></span>
<%= button("", to: "/sites/#{@site.domain}/weekly-report/recipients/#{recipient}", method: :delete) %>
<%= button("", to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/recipients/#{recipient}", method: :delete) %>
</div>
<% end %>
<%= form_for @conn, "/sites/#{@site.domain}/weekly-report/recipients", fn f -> %>
<%= form_for @conn, "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/recipients", fn f -> %>
<div class="flex justify-between my-2 max-w-sm">
<%= email_input f, :recipient, class: "transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:border-grey-light", style: "flex-grow: 2", placeholder: "recipient@example.com" %>
<%= submit "Add recipient", class: "button rounded-l-none whitespace-no-wrap" %>
@ -178,11 +178,11 @@
<div class="my-8 border-b border-grey-light"></div>
<div class="my-8 flex items-center">
<%= if @monthly_report do %>
<%= button(to: "/sites/#{@site.domain}/monthly-report/disable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 bg-green justify-end") do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/disable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 bg-green justify-end") do %>
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
<% end %>
<% else %>
<%= button(to: "/sites/#{@site.domain}/monthly-report/enable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 justify-start") do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/enable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 justify-start") do %>
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
<% end %>
<% end %>
@ -194,10 +194,10 @@
<%= for recipient <- @monthly_report.recipients do %>
<div class="p-3 flex justify-between bg-grey-lighter rounded my-2 max-w-sm">
<span><svg class="feather mr-1" style="transform: translateY(0.05em)"><use xlink:href="#feather-mail" /></svg><%= recipient %></span>
<%= button("", to: "/sites/#{@site.domain}/monthly-report/recipients/#{recipient}", method: :delete) %>
<%= button("", to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients/#{recipient}", method: :delete) %>
</div>
<% end %>
<%= form_for @conn, "/sites/#{@site.domain}/monthly-report/recipients", fn f -> %>
<%= form_for @conn, "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients", fn f -> %>
<div class="flex justify-between my-2 max-w-sm">
<%= email_input f, :recipient, class: "transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:border-grey-light", style: "flex-grow: 2", placeholder: "recipient@example.com" %>
<%= submit "Add recipient", class: "button rounded-l-none whitespace-no-wrap" %>
@ -234,5 +234,5 @@
<div class="my-4 border-b border-grey-light"></div>
<p class="text-lg">Deleting the site removes all stats you've collected</p>
<%= link "Delete #{@site.domain}", to: "/#{@site.domain}", method: :delete, class: "button bg-red-dark mt-4", data: [confirm: "Deleting the site data cannot be reversed. Are you sure?"] %>
<%= link "Delete #{@site.domain}", to: "/#{URI.encode_www_form(@site.domain)}", method: :delete, class: "button bg-red-dark mt-4", data: [confirm: "Deleting the site data cannot be reversed. Are you sure?"] %>
</div>

View File

@ -15,5 +15,5 @@
<%= link("Read the docs", class: "text-indigo hover:underline", to: "https://docs.plausible.io/single-page-application-support", target: "_blank") %>
</div>
</div>
<%= link("Start collecting data →", class: "button mt-4 w-full", to: "/#{@site.domain}") %>
<%= link("Start collecting data →", class: "button mt-4 w-full", to: "/#{URI.encode_www_form(@site.domain)}") %>
<% end %>

View File

@ -1,7 +1,7 @@
<div class="container pb-32" data-site-domain="<%= @site.domain %>">
<%= if @offer_email_report do %>
<div class="text-center bg-blue-lighter text-blue-darkest text-sm font-bold px-4 w-full rounded transition" style="top: 91px" role="alert">
<%= link("Click here to enable weekly email reports →", to: "/#{@site.domain}/settings#email-reports", class: "py-2 block") %>
<%= link("Click here to enable weekly email reports →", to: "/#{URI.encode_www_form(@site.domain)}/settings#email-reports", class: "py-2 block") %>
</div>
<% end %>
<div class="pt-12"></div>

View File

@ -1,7 +1,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/3.0.0/fetch.min.js" integrity="sha256-E1M+0f/hvoNVoV8K5RSn1gwe4EFwlvORnOrFzghX0wM=" crossorigin="anonymous"></script>
<script>
function updateStatus() {
fetch("/api/<%= @site.domain %>/status")
fetch("/api/<%= URI.encode_www_form(@site.domain) %>/status")
.then(function(res) { return res.json() })
.then(function(status) {
if (status === "READY") {
@ -20,7 +20,7 @@
<div class="my-4">
<div class="pulsating-circle"></div>
<p class="text-grey-dark text-sm absolute pin-l pin-b mb-6 w-full text-center leading-normal">
Need to see the snippet again? <%= link("Click here", to: "/#{@site.domain}/snippet")%><br />
Need to see the snippet again? <%= link("Click here", to: "/#{URI.encode_www_form(@site.domain)}/snippet")%><br />
Not working? Contact <a href="mailto:uku@plausible.io">uku@plausible.io</a> to get set up
</p>
</div>

View File

@ -0,0 +1,37 @@
defmodule Plausible.Repo.Migrations.AddSiteIdToEvents do
use Ecto.Migration
def up do
alter table(:events) do
add :site_id, :text
end
alter table(:sessions) do
add :site_id, :text
end
execute "UPDATE events set site_id=hostname"
execute "UPDATE sessions set site_id=hostname"
alter table(:events) do
modify :site_id, :text, null: false
end
alter table(:sessions) do
modify :site_id, :text, null: false
end
create index(:events, :site_id)
create index(:sessions, :site_id)
end
def down do
alter table(:events) do
remove :site_id
end
alter table(:sessions) do
remove :site_id
end
end
end

View File

@ -0,0 +1,8 @@
defmodule Plausible.Repo.Migrations.RenameSiteIdToDomain do
use Ecto.Migration
def change do
rename table(:events), :site_id, to: :domain
rename table(:sessions), :site_id, to: :domain
end
end

View File

@ -0,0 +1,7 @@
defmodule Plausible.Repo.Migrations.DropEventsHostnameIndex do
use Ecto.Migration
def change do
drop index("pageviews", :hostname)
end
end

View File

@ -17,7 +17,7 @@ defmodule Mix.Tasks.SendFeedbackEmailsTest do
test "sends an email if the user is more than 30 days old and logged on in the last week" do
user = insert(:user, inserted_at: days_ago(31), last_seen: days_ago(1))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
SendFeedbackEmails.execute()
@ -27,7 +27,7 @@ defmodule Mix.Tasks.SendFeedbackEmailsTest do
test "sends the email only once" do
user = insert(:user, inserted_at: days_ago(31), last_seen: days_ago(1))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
SendFeedbackEmails.execute()
assert_email_delivered_with(subject: "Plausible feedback")
@ -39,7 +39,7 @@ defmodule Mix.Tasks.SendFeedbackEmailsTest do
test "does not send if user has not logged in recently" do
user = insert(:user, inserted_at: days_ago(31), last_seen: days_ago(15))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
SendFeedbackEmails.execute()
@ -49,7 +49,7 @@ defmodule Mix.Tasks.SendFeedbackEmailsTest do
test "does not send if user is less than a month old" do
user = insert(:user, inserted_at: days_ago(15), last_seen: days_ago(1))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
SendFeedbackEmails.execute()

View File

@ -66,7 +66,7 @@ defmodule Mix.Tasks.SendIntroEmailsTest do
test "sends a welcome email 6 hours after signup if the user has created a site and has received a pageview" do
user = insert(:user, inserted_at: hours_ago(6))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
Mix.Tasks.SendIntroEmails.execute()
@ -79,7 +79,7 @@ defmodule Mix.Tasks.SendIntroEmailsTest do
test "sends a welcome email 23 hours after signup" do
user = insert(:user, inserted_at: hours_ago(23))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
Mix.Tasks.SendIntroEmails.execute()
@ -92,7 +92,7 @@ defmodule Mix.Tasks.SendIntroEmailsTest do
test "does not send a welcome email 24 hours after signup" do
user = insert(:user, inserted_at: hours_ago(24))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
Mix.Tasks.SendIntroEmails.execute()
@ -106,7 +106,7 @@ defmodule Mix.Tasks.SendIntroEmailsTest do
Mix.Tasks.SendIntroEmails.execute()
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
Mix.Tasks.SendIntroEmails.execute()

View File

@ -17,7 +17,7 @@ defmodule Mix.Tasks.SendTrialNotificationsTest do
test "sends a reminder 14 days before trial ends (16 days after user signed up)" do
user = insert(:user, inserted_at: Timex.now |> Timex.shift(days: -16))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
Mix.Tasks.SendTrialNotifications.execute()
@ -27,7 +27,7 @@ defmodule Mix.Tasks.SendTrialNotificationsTest do
test "sends an upgrade email the day before the trial ends" do
user = insert(:user, inserted_at: Timex.now |> Timex.shift(days: -29))
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
Mix.Tasks.SendTrialNotifications.execute()
@ -37,10 +37,10 @@ defmodule Mix.Tasks.SendTrialNotificationsTest do
test "does not send a notification if user has a subscription" do
user1 = insert(:user, inserted_at: Timex.now |> Timex.shift(days: -14))
site1 = insert(:site, members: [user1])
insert(:pageview, hostname: site1.domain)
insert(:pageview, domain: site1.domain)
user2 = insert(:user, inserted_at: Timex.now |> Timex.shift(days: -29))
site2 = insert(:site, members: [user2])
insert(:pageview, hostname: site2.domain)
insert(:pageview, domain: site2.domain)
insert(:subscription, user: user1)
insert(:subscription, user: user2)

View File

@ -13,7 +13,7 @@ defmodule Plausible.AuthTest do
test "is true if user does have events" do
user = insert(:user)
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
assert Auth.user_completed_setup?(user)
end

View File

@ -12,8 +12,8 @@ defmodule Plausible.BillingTest do
test "counts the total number of events" do
user = insert(:user)
site = insert(:site, members: [user])
insert(:pageview, hostname: site.domain)
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
insert(:pageview, domain: site.domain)
assert Billing.usage(user) == 2
end

View File

@ -5,7 +5,7 @@ defmodule Plausible.SitesTest do
describe "has_pageviews?" do
test "is true if site has pageviews" do
site = insert(:site)
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
assert Sites.has_pageviews?(site)
end

View File

@ -34,11 +34,33 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
assert response(conn, 202) == ""
assert pageview.hostname == "gigride.live"
assert pageview.domain == "gigride.live"
assert pageview.pathname == "/"
assert pageview.new_visitor == true
assert pageview.country_code == @country_code
end
test "can specify the domain", %{conn: conn} do
params = %{
name: "custom event",
url: "http://gigride.live/",
domain: "some_site.com",
new_visitor: false,
uid: UUID.uuid4()
}
conn = conn
|> put_req_header("content-type", "text/plain")
|> put_req_header("user-agent", @user_agent)
|> post("/api/event", Jason.encode!(params))
event = Repo.one(Plausible.Event)
finalize_session(event.user_id)
assert response(conn, 202) == ""
assert event.domain == "some_site.com"
end
test "www. is stripped from hostname", %{conn: conn} do
params = %{
name: "pageview",

View File

@ -13,7 +13,7 @@ defmodule PlausibleWeb.Api.InternalControllerTest do
end
test "is READY when site has at least 1 pageview", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
conn = get(conn, "/api/#{site.domain}/status")

View File

@ -6,9 +6,9 @@ defmodule PlausibleWeb.Api.StatsController.BrowsersTest do
setup [:create_user, :log_in, :create_site]
test "returns top browsers by new visitors", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, browser: "Chrome", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, browser: "Chrome", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, browser: "Firefox", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, browser: "Chrome", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, browser: "Chrome", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, browser: "Firefox", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/browsers?period=day&date=2019-01-01")

View File

@ -8,10 +8,10 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do
test "returns custom event conversions", %{conn: conn, site: site} do
insert(:goal, %{domain: site.domain, event_name: "Register"})
insert(:goal, %{domain: site.domain, event_name: "Newsletter signup"})
insert(:event, name: "Register", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Register", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Newsletter signup", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Irrelevant", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Register", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Register", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Newsletter signup", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Irrelevant", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
conn = get(conn, "/api/stats/#{site.domain}/conversions?period=day&date=2019-01-01")
@ -25,10 +25,10 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do
insert(:goal, %{domain: site.domain, page_path: "/success"})
insert(:goal, %{domain: site.domain, page_path: "/register"})
insert(:event, name: "pageview", pathname: "/success", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/success", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/register", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/irrelevant", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/success", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/success", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/register", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/irrelevant", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
conn = get(conn, "/api/stats/#{site.domain}/conversions?period=day&date=2019-01-01")
@ -42,9 +42,9 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do
insert(:goal, %{domain: site.domain, page_path: "/success"})
insert(:goal, %{domain: site.domain, event_name: "Signup"})
insert(:event, name: "Signup", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/success", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/success", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/success", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/success", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
conn = get(conn, "/api/stats/#{site.domain}/conversions?period=day&date=2019-01-01")
@ -62,8 +62,8 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do
insert(:goal, %{domain: site.domain, page_path: "/success"})
insert(:goal, %{domain: site.domain, event_name: "Signup"})
insert(:event, name: "Signup", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/success", hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "pageview", pathname: "/success", domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
filters = Jason.encode!(%{goal: "Signup"})
conn = get(conn, "/api/stats/#{site.domain}/conversions?period=day&date=2019-01-01&filters=#{filters}")

View File

@ -6,9 +6,9 @@ defmodule PlausibleWeb.Api.StatsController.CountriesTest do
setup [:create_user, :log_in, :create_site]
test "returns top countries by new visitors", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, country_code: "EE", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, country_code: "EE", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, country_code: "GB", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, country_code: "EE", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, country_code: "EE", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, country_code: "GB", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/countries?period=day&date=2019-01-01")

View File

@ -6,10 +6,10 @@ defmodule PlausibleWeb.Api.StatsController.CurrentVisitorsTest do
setup [:create_user, :log_in, :create_site]
test "returns unique users in the last 5 minutes", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain)
event2 = insert(:pageview, hostname: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -3))
insert(:pageview, hostname: site.domain, user_id: event2.user_id, timestamp: Timex.now() |> Timex.shift(minutes: -4))
insert(:pageview, hostname: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -6))
insert(:pageview, domain: site.domain)
event2 = insert(:pageview, domain: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -3))
insert(:pageview, domain: site.domain, user_id: event2.user_id, timestamp: Timex.now() |> Timex.shift(minutes: -4))
insert(:pageview, domain: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -6))
conn = get(conn, "/api/stats/#{site.domain}/current-visitors?period=day&date=2019-01-01")

View File

@ -7,8 +7,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
setup [:create_user, :log_in, :create_site]
test "displays visitors for a day", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 00:00:00])
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 23:59:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 00:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 23:59:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
@ -22,7 +22,7 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
test "displays hourly stats in configured timezone", %{conn: conn, user: user} do
site = insert(:site, members: [user], timezone: "CET") # UTC+1
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 00:00:00]) # Timestamp is in UTC
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 00:00:00]) # Timestamp is in UTC
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
@ -34,8 +34,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "displays visitors for a month", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 12:00:00])
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-31 12:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 12:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-31 12:00:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=month&date=2019-01-01")
@ -47,8 +47,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "displays visitors for 6 months", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain)
insert(:pageview, hostname: site.domain, timestamp: months_ago(5))
insert(:pageview, domain: site.domain)
insert(:pageview, domain: site.domain, timestamp: months_ago(5))
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=6mo")
@ -88,8 +88,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
setup [:create_user, :log_in, :create_site]
test "unique users counts distinct user ids", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00])
insert(:pageview, hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00])
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00])
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
@ -98,8 +98,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "does not count custom events in custom user ids", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 00:00:00])
insert(:event, name: "Custom", hostname: site.domain, timestamp: ~N[2019-01-01 00:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 00:00:00])
insert(:event, name: "Custom", domain: site.domain, timestamp: ~N[2019-01-01 00:00:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
@ -108,8 +108,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "counts total pageviews even from same user ids", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00])
insert(:pageview, hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00])
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00])
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
@ -118,10 +118,10 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "compares pageviews with previous time period", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-02 02:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-02 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-02")
@ -130,8 +130,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "calculates bounce rate", %{conn: conn, site: site} do
insert(:session, hostname: site.domain, is_bounce: true, start: ~N[2019-01-01 01:00:00])
insert(:session, hostname: site.domain, is_bounce: false, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, is_bounce: true, start: ~N[2019-01-01 01:00:00])
insert(:session, domain: site.domain, is_bounce: false, start: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
@ -140,12 +140,12 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "calculates change in bounce rate", %{conn: conn, site: site} do
insert(:session, hostname: site.domain, is_bounce: true, start: ~N[2019-01-01 01:00:00])
insert(:session, hostname: site.domain, is_bounce: false, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, is_bounce: true, start: ~N[2019-01-01 01:00:00])
insert(:session, domain: site.domain, is_bounce: false, start: ~N[2019-01-01 02:00:00])
insert(:session, hostname: site.domain, is_bounce: true, start: ~N[2019-01-02 01:00:00])
insert(:session, hostname: site.domain, is_bounce: true, start: ~N[2019-01-02 01:00:00])
insert(:session, hostname: site.domain, is_bounce: false, start: ~N[2019-01-02 02:00:00])
insert(:session, domain: site.domain, is_bounce: true, start: ~N[2019-01-02 01:00:00])
insert(:session, domain: site.domain, is_bounce: true, start: ~N[2019-01-02 01:00:00])
insert(:session, domain: site.domain, is_bounce: false, start: ~N[2019-01-02 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-02")
@ -159,9 +159,9 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
setup [:create_user, :log_in, :create_site]
test "returns total unique visitors", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00])
filters = Jason.encode!(%{goal: "Signup"})
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01&filters=#{filters}")
@ -171,9 +171,9 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "returns converted visitors", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00])
filters = Jason.encode!(%{goal: "Signup"})
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=month&date=2019-01-01&filters=#{filters}")
@ -183,9 +183,9 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "returns conversion rate", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", hostname: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00])
filters = Jason.encode!(%{goal: "Signup"})
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01&filters=#{filters}")

View File

@ -6,9 +6,9 @@ defmodule PlausibleWeb.Api.StatsController.OperatingSystemsTest do
setup [:create_user, :log_in, :create_site]
test "returns operating systems by new visitors", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, operating_system: "Mac", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, operating_system: "Mac", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, operating_system: "Android", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, operating_system: "Mac", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, operating_system: "Mac", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, operating_system: "Android", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/operating-systems?period=day&date=2019-01-01")

View File

@ -6,9 +6,9 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
setup [:create_user, :log_in, :create_site]
test "returns top pages sources by pageviews", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, pathname: "/", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, pathname: "/", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, pathname: "/contact", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, pathname: "/", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, pathname: "/", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, pathname: "/contact", timestamp: ~N[2019-01-01 01:00:00])
conn = get(conn, "/api/stats/#{site.domain}/pages?period=day&date=2019-01-01")
@ -19,12 +19,12 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
end
test "calculates bounce rate for pages", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, pathname: "/", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, pathname: "/", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, pathname: "/contact", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, pathname: "/", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, pathname: "/", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, pathname: "/contact", timestamp: ~N[2019-01-01 02:00:00])
insert(:session, hostname: site.domain, entry_page: "/", is_bounce: true, start: ~N[2019-01-01 02:00:00])
insert(:session, hostname: site.domain, entry_page: "/", is_bounce: false, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, entry_page: "/", is_bounce: true, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, entry_page: "/", is_bounce: false, start: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/pages?period=day&date=2019-01-01&include=bounce_rate")

View File

@ -6,10 +6,10 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
setup [:create_user, :log_in, :create_site]
test "returns top referrer sources by new visitors", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, referrer_source: "Google", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "Google", new_visitor: false, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "Google", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "Bing", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Google", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Google", new_visitor: false, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Google", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Bing", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/referrers?period=day&date=2019-01-01")
@ -20,12 +20,12 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
end
test "calculates bounce rate for referrers", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "Bing", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Bing", timestamp: ~N[2019-01-01 02:00:00])
insert(:session, hostname: site.domain, referrer_source: "Google", is_bounce: true, start: ~N[2019-01-01 02:00:00])
insert(:session, hostname: site.domain, referrer_source: "Google", is_bounce: false, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, referrer_source: "Google", is_bounce: true, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, referrer_source: "Google", is_bounce: false, start: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/referrers?period=day&date=2019-01-01&include=bounce_rate")
@ -40,9 +40,9 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
setup [:create_user, :log_in, :create_site]
test "returns top referrers for a custom goal", %{conn: conn, site: site} do
insert(:event, name: "Signup", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:event, name: "Signup", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
filters = Jason.encode!(%{goal: "Signup"})
conn = get(conn, "/api/stats/#{site.domain}/goal/referrers?period=day&date=2019-01-01&filters=#{filters}")
@ -53,9 +53,9 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
end
test "returns top referrers for a pageview goal", %{conn: conn, site: site} do
insert(:pageview, pathname: "/register", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/register", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/irrelevant", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, pathname: "/register", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/register", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/irrelevant", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
filters = Jason.encode!(%{goal: "Visit /register"})
conn = get(conn, "/api/stats/#{site.domain}/goal/referrers?period=day&date=2019-01-01&filters=#{filters}")
@ -71,7 +71,7 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
test "returns top referrers for a particular source", %{conn: conn, site: site} do
insert(:pageview, %{
hostname: site.domain,
domain: site.domain,
referrer: "10words.io/somepage",
referrer_source: "10words",
new_visitor: true,
@ -79,7 +79,7 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
})
insert(:pageview, %{
hostname: site.domain,
domain: site.domain,
referrer: "10words.io/somepage",
referrer_source: "10words",
new_visitor: true,
@ -87,7 +87,7 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
})
insert(:pageview, %{
hostname: site.domain,
domain: site.domain,
referrer: "10words.io/some_other_page",
referrer_source: "10words",
new_visitor: true,
@ -106,12 +106,12 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
end
test "calculates bounce rate for referrer urls", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "10words", referrer: "10words.io/hello", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "10words", referrer: "10words.io/hello", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer_source: "10words", referrer: "10words.io/", timestamp: ~N[2019-01-01 02:00:00])
insert(:session, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello", is_bounce: true, start: ~N[2019-01-01 02:00:00])
insert(:session, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello",is_bounce: false, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, referrer_source: "10words", referrer: "10words.io/hello", is_bounce: true, start: ~N[2019-01-01 02:00:00])
insert(:session, domain: site.domain, referrer_source: "10words", referrer: "10words.io/hello",is_bounce: false, start: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/referrers/10words?period=day&date=2019-01-01&include=bounce_rate")
@ -126,8 +126,8 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
test "gets keywords from Google", %{conn: conn, user: user, site: site} do
insert(:google_auth, user: user, user: user,site: site, property: "sc-domain:example.com")
insert(:pageview, hostname: site.domain, referrer: "google.com", referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, referrer: "google.com", referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer: "google.com", referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, referrer: "google.com", referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/referrers/Google?period=day&date=2019-01-01")
{:ok, terms} = Plausible.Google.Api.Mock.fetch_stats(nil, nil)
@ -139,9 +139,9 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
end
test "enriches twitter referrers with tweets if available", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, referrer: "t.co/some-link", referrer_source: "Twitter", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, referrer: "t.co/some-link", referrer_source: "Twitter", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, referrer: "t.co/nonexistent-link", referrer_source: "Twitter", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, referrer: "t.co/some-link", referrer_source: "Twitter", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, referrer: "t.co/some-link", referrer_source: "Twitter", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, referrer: "t.co/nonexistent-link", referrer_source: "Twitter", timestamp: ~N[2019-01-01 02:00:00])
insert(:tweet, link: "t.co/some-link", text: "important tweet")
@ -159,9 +159,9 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
setup [:create_user, :log_in, :create_site]
test "returns top referring urls for a custom goal", %{conn: conn, site: site} do
insert(:event, name: "Signup", hostname: site.domain, referrer_source: "Twitter", referrer: "a", timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", hostname: site.domain, referrer_source: "Twitter", referrer: "a", timestamp: ~N[2019-01-01 02:00:00])
insert(:event, name: "Signup", hostname: site.domain, referrer_source: "Twitter", referrer: "b", timestamp: ~N[2019-01-01 02:00:00])
insert(:event, name: "Signup", domain: site.domain, referrer_source: "Twitter", referrer: "a", timestamp: ~N[2019-01-01 01:00:00])
insert(:event, name: "Signup", domain: site.domain, referrer_source: "Twitter", referrer: "a", timestamp: ~N[2019-01-01 02:00:00])
insert(:event, name: "Signup", domain: site.domain, referrer_source: "Twitter", referrer: "b", timestamp: ~N[2019-01-01 02:00:00])
filters = Jason.encode!(%{goal: "Signup"})
conn = get(conn, "/api/stats/#{site.domain}/goal/referrers/Twitter?period=day&date=2019-01-01&filters=#{filters}")
@ -176,9 +176,9 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
end
test "returns top referring urls for a pageview goal", %{conn: conn, site: site} do
insert(:pageview, pathname: "/register", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/register", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/irrelevant", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, pathname: "/register", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/register", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, pathname: "/irrelevant", domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
filters = Jason.encode!(%{goal: "Visit /register"})
conn = get(conn, "/api/stats/#{site.domain}/goal/referrers?period=day&date=2019-01-01&filters=#{filters}")

View File

@ -6,9 +6,9 @@ defmodule PlausibleWeb.Api.StatsController.ScreenSizesTest do
setup [:create_user, :log_in, :create_site]
test "returns screen sizes by new visitors", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain, screen_size: "Desktop", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, hostname: site.domain, screen_size: "Desktop", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, hostname: site.domain, screen_size: "Mobile", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, screen_size: "Desktop", new_visitor: true, timestamp: ~N[2019-01-01 01:00:00])
insert(:pageview, domain: site.domain, screen_size: "Desktop", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
insert(:pageview, domain: site.domain, screen_size: "Mobile", new_visitor: true, timestamp: ~N[2019-01-01 02:00:00])
conn = get(conn, "/api/stats/#{site.domain}/screen-sizes?period=day&date=2019-01-01")

View File

@ -124,7 +124,7 @@ defmodule PlausibleWeb.SiteControllerTest do
setup [:create_user, :log_in, :create_site]
test "deletes the site and all pageviews", %{conn: conn, user: user, site: site} do
pageview = insert(:pageview, hostname: site.domain)
pageview = insert(:pageview, domain: site.domain)
insert(:google_auth, user: user, site: site)
delete(conn, "/#{site.domain}")

View File

@ -6,14 +6,14 @@ defmodule PlausibleWeb.StatsControllerTest do
describe "GET /:website - anonymous user" do
test "public site - shows site stats", %{conn: conn} do
insert(:site, domain: "public-site.io", public: true)
insert(:pageview, hostname: "public-site.io")
insert(:pageview, domain: "public-site.io")
conn = get(conn, "/public-site.io")
assert html_response(conn, 200) =~ "stats-react-container"
end
test "can not view stats of a private website", %{conn: conn} do
insert(:pageview, hostname: "some-other-site.com")
insert(:pageview, domain: "some-other-site.com")
conn = get(conn, "/some-other-site.com")
assert html_response(conn, 404) =~ "There&#39;s nothing here"
@ -24,14 +24,14 @@ defmodule PlausibleWeb.StatsControllerTest do
setup [:create_user, :log_in, :create_site]
test "can view stats of a website I've created", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
conn = get(conn, "/" <> site.domain)
assert html_response(conn, 200) =~ "stats-react-container"
end
test "can not view stats of someone else's website", %{conn: conn} do
insert(:pageview, hostname: "some-other-site.com")
insert(:pageview, domain: "some-other-site.com")
conn = get(conn, "/some-other-site.com")
assert html_response(conn, 404) =~ "There&#39;s nothing here"
@ -42,7 +42,7 @@ defmodule PlausibleWeb.StatsControllerTest do
setup [:create_user, :log_in, :create_site]
test "exports graph as csv", %{conn: conn, site: site} do
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
today = Timex.today() |> Timex.format!("{ISOdate}")
conn = get(conn, "/" <> site.domain <> "/visitors.csv")
@ -63,7 +63,7 @@ defmodule PlausibleWeb.StatsControllerTest do
test "logs anonymous user in straight away if the link is not password-protected", %{conn: conn} do
site = insert(:site)
link = insert(:shared_link, site: site)
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
conn = get(conn, "/share/#{link.slug}")
assert redirected_to(conn, 302) == "/#{site.domain}"
@ -77,7 +77,7 @@ defmodule PlausibleWeb.StatsControllerTest do
test "logs anonymous user in with correct password", %{conn: conn} do
site = insert(:site)
link = insert(:shared_link, site: site, password_hash: Plausible.Auth.Password.hash("password"))
insert(:pageview, hostname: site.domain)
insert(:pageview, domain: site.domain)
conn = post(conn, "/share/#{link.slug}/authenticate", %{password: "password"})
assert redirected_to(conn, 302) == "/#{site.domain}"

View File

@ -27,6 +27,7 @@ defmodule Plausible.Factory do
%Plausible.Session{
hostname: hostname,
domain: hostname,
new_visitor: true,
entry_page: "/",
user_id: UUID.uuid4(),
@ -49,6 +50,7 @@ defmodule Plausible.Factory do
%Plausible.Event{
hostname: hostname,
domain: hostname,
pathname: "/",
new_visitor: true, user_id: UUID.uuid4(),
}