analytics/lib/plausible_web/controllers/stats_controller.ex

372 lines
14 KiB
Elixir
Raw Normal View History

2019-09-02 14:29:19 +03:00
defmodule PlausibleWeb.StatsController do
@moduledoc """
This controller is responsible for rendering stats dashboards.
The stats dashboards are currently the only part of the app that uses client-side
rendering. Since the dashboards are heavily interactive, they are built with React
2022-10-25 14:17:09 +03:00
which is an appropriate choice for highly interactive browser UIs.
<div class="mermaid">
sequenceDiagram
Browser->>StatsController: GET /mydomain.com
StatsController-->>Browser: StatsView.render("stats.html")
Note left of Browser: ReactDom.render(Dashboard)
Browser -) Api.StatsController: GET /api/stats/mydomain.com/top-stats
Api.StatsController --) Browser: {"top_stats": [...]}
Note left of Browser: TopStats.render()
Browser -) Api.StatsController: GET /api/stats/mydomain.com/main-graph
Api.StatsController --) Browser: [{"plot": [...], "labels": [...]}, ...]
Note left of Browser: VisitorGraph.render()
Browser -) Api.StatsController: GET /api/stats/mydomain.com/sources
Api.StatsController --) Browser: [{"name": "Google", "visitors": 292150}, ...]
Note left of Browser: Sources.render()
Note over Browser,StatsController: And so on, for all reports in the viewport
</div>
This reasoning for this sequence is as follows:
1. First paint is fast because it doesn't do any data aggregation yet - good UX
2. The basic structure of the dashboard is rendered with spinners before reports are ready - good UX
2. Rendering on the frontend allows for maximum interactivity. Re-rendering and re-fetching can be as granular as needed.
3. Routing on the frontend allows the user to navigate the dashboard without reloading the page and losing context
4. Rendering on the frontend allows caching results in the browser to reduce pressure on backends and storage
3.1 No client-side caching has been implemented yet. This is still theoretical. See https://github.com/plausible/analytics/discussions/1278
3.2 This is a big potential opportunity, because analytics data is mostly immutable. Clients can cache all historical data.
5. Since frontend rendering & navigation is harder to build and maintain than regular server-rendered HTML, we don't use SPA-style rendering anywhere else
2022-10-25 14:17:09 +03:00
.The only place currently where the benefits outweigh the costs is the dashboard.
"""
2019-09-02 14:29:19 +03:00
use PlausibleWeb, :controller
use Plausible.Repo
alias Plausible.Sites
alias Plausible.Stats.{Query, Filters}
alias PlausibleWeb.Api
2019-09-02 14:29:19 +03:00
plug(PlausibleWeb.AuthorizeSiteAccess when action in [:stats, :csv_export])
Support for docker based self-hosting (#64) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details
2020-05-26 16:09:34 +03:00
def stats(%{assigns: %{site: site}} = conn, _params) do
2022-04-06 11:52:19 +03:00
stats_start_date = Plausible.Sites.stats_start_date(site)
can_see_stats? = not Sites.locked?(site) or conn.assigns[:current_user_role] == :super_admin
cond do
stats_start_date && can_see_stats? ->
demo = site.domain == PlausibleWeb.Endpoint.host()
offer_email_report = get_session(conn, site.domain <> "_offer_email_report")
conn
|> assign(:skip_plausible_tracking, !demo)
|> remove_email_report_banner(site)
|> put_resp_header("x-robots-tag", "noindex, nofollow")
|> render("stats.html",
site: site,
has_goals: Plausible.Sites.has_goals?(site),
Implement Funnels view on dashboard (#3066) * Add Funnel react component assets/js/dashboard/stats/behaviours/funnel.js - restored from: 98a76cbd Remove console.info calls d94db99d Convert Funnel class component into a functional one 028036ad Review comments 3067a940 Stop doing maths in react 73407cc3 Fix error handling when local storage gets corrupted e8c6fc52 Format numbers on funnel labels c815709f Reorganize component responsibility 7a88fe44 Outline basic error handling 94caed7c Chart styling updates 4514608a Add percentages to funnel d622c32d Add funnel picker Co-authored-by: Uku Taht <uku.taht@gmail.com> * Pass funnels list to react via data-funnels * Implement Funnels react API lib/plausible_web/controllers/api/stats_controller.ex - restored from: f36ad234 Adjust to Plausible.Stats interface 9b532273 Test funnel stats controller 028036ad Review comments bea3725f Remove IO.inspect 7a88fe44 Outline basic error handling c8ae3eaf Move Funnels to StatsController and use base query 667cf222 Put private functions at the bottom * Tweak funnel presentation * Handle errors at the top * Do not register DataLabels plugin globally or else all the existing charts are affected * Calculate drop-off percentage evaluating funnels * Tweak dark mode + implement nicer tooltips * Make currently selected funnel bold in the picker * Count user_ids not session_ids when evaluating funnels So if a visitor goes: 1. Start session 2. Complete funnel step 1 3. Inactive for 30 minutes 4. Complete funnel step 2 We would not be able to track this funnel completion because of the session timeout. We like to o measure this as funnel completion even though the session expired in the middle. cc @ukutaht * Add extra properties to the funnels API cc @ukutaht * Improve tooltips so that step to data is rendered * Change tooltip number formatting * Remove debugging remnants * Quick & dirty mobile view * Fix mobile view: tweak dark mode & funnel switching * Ignore DOMException: the operation was aborted Otherwise this sometimes flashes the space shuttle screen when navigating quickly via a keyboard. * Format percentages on the main chart * Close missing tag 🙈 * Revert "Close missing tag 🙈" This reverts commit 9c2f970e22fd7e2980503242b414f42ce8bce1d2. * Use jsx to render funnel tooltip To get markup validated via lsp mostly... * Fixup: s/class/className * Fix className interpolation * Add a ruler to the tooltip * Tweak funnel chart style * Fix font distortion issue on chart/canvas labels * s/class/className * Put "Set up funnels" link behind a feature flag * Refactor internal selection storage Getting ready for live funnel evaluation * Don't try to connect LV socket if there's no CRSF token set up This is perfectly okay for some of the templates/layouts. * Fix up funnel creation typespecs Unfortunately we can't define a type with literal string keys, hence this must suffice. * Use uniq over count/distinct * Revert JSX in tooltips Ref: https://github.com/plausible/analytics/pull/3066#discussion_r1241891155 * Remove the extra query for counting all visitors cc @ukutaht * Add premium notice --------- Co-authored-by: Uku Taht <uku.taht@gmail.com>
2023-06-27 11:04:35 +03:00
funnels: Plausible.Funnels.list(site),
allowed_event_props: site.allowed_event_props,
2022-04-06 11:52:19 +03:00
stats_start_date: stats_start_date,
native_stats_start_date: NaiveDateTime.to_date(site.native_stats_start_at),
title: title(conn, site),
offer_email_report: offer_email_report,
2022-04-21 10:54:08 +03:00
demo: demo,
2022-05-03 10:38:59 +03:00
flags: get_flags(conn.assigns[:current_user]),
is_dbip: is_dbip()
)
!stats_start_date && can_see_stats? ->
conn
|> assign(:skip_plausible_tracking, true)
|> render("waiting_first_pageview.html", site: site)
Sites.locked?(site) ->
owner = Sites.owner_for(site)
2021-10-27 12:38:24 +03:00
conn
|> assign(:skip_plausible_tracking, true)
2021-10-27 12:38:24 +03:00
|> render("site_locked.html", owner: owner, site: site)
2019-09-02 14:29:19 +03:00
end
end
2021-11-15 18:06:39 +03:00
@doc """
The export is limited to 300 entries for other reports and 100 entries for pages because bigger result sets
start causing failures. Since we request data like time on page or bounce_rate for pages in a separate query
using the IN filter, it causes the requests to balloon in payload size.
"""
def csv_export(conn, params) do
if is_nil(params["interval"]) or Plausible.Stats.Interval.valid?(params["interval"]) do
site = conn.assigns[:site]
query = Query.from(site, params) |> Filters.add_prefix()
metrics =
if query.filters["event:goal"] do
[:visitors]
else
[:visitors, :pageviews, :visits, :views_per_visit, :bounce_rate, :visit_duration]
end
graph = Plausible.Stats.timeseries(site, query, metrics)
columns = [:date | metrics]
column_headers =
if query.filters["event:goal"] do
[:date, :unique_conversions]
else
columns
end
visitors =
Enum.map(graph, fn row -> Enum.map(columns, &row[&1]) end)
|> (fn data -> [column_headers | data] end).()
|> CSV.encode()
|> Enum.join()
filename =
'Plausible export #{params["domain"]} #{Timex.format!(query.date_range.first, "{ISOdate} ")} to #{Timex.format!(query.date_range.last, "{ISOdate} ")}.zip'
params = Map.merge(params, %{"limit" => "300", "csv" => "True", "detailed" => "True"})
limited_params = Map.merge(params, %{"limit" => "100"})
csvs = %{
'sources.csv' => fn -> Api.StatsController.sources(conn, params) end,
'utm_mediums.csv' => fn -> Api.StatsController.utm_mediums(conn, params) end,
'utm_sources.csv' => fn -> Api.StatsController.utm_sources(conn, params) end,
'utm_campaigns.csv' => fn -> Api.StatsController.utm_campaigns(conn, params) end,
'utm_contents.csv' => fn -> Api.StatsController.utm_contents(conn, params) end,
'utm_terms.csv' => fn -> Api.StatsController.utm_terms(conn, params) end,
'pages.csv' => fn -> Api.StatsController.pages(conn, limited_params) end,
'entry_pages.csv' => fn -> Api.StatsController.entry_pages(conn, params) end,
'exit_pages.csv' => fn -> Api.StatsController.exit_pages(conn, limited_params) end,
'countries.csv' => fn -> Api.StatsController.countries(conn, params) end,
'regions.csv' => fn -> Api.StatsController.regions(conn, params) end,
'cities.csv' => fn -> Api.StatsController.cities(conn, params) end,
'browsers.csv' => fn -> Api.StatsController.browsers(conn, params) end,
'operating_systems.csv' => fn -> Api.StatsController.operating_systems(conn, params) end,
'devices.csv' => fn -> Api.StatsController.screen_sizes(conn, params) end,
'conversions.csv' => fn -> Api.StatsController.conversions(conn, params) end,
'prop_breakdown.csv' => fn -> Api.StatsController.all_props_breakdown(conn, params) end
}
csvs =
if FunWithFlags.enabled?(:props, for: conn.assigns[:current_user]) do
Map.put(csvs, 'custom_props.csv', fn ->
Api.StatsController.all_custom_prop_values(conn, params)
end)
else
csvs
end
csv_values =
Map.values(csvs)
|> Plausible.ClickhouseRepo.parallel_tasks()
csvs =
Map.keys(csvs)
|> Enum.zip(csv_values)
csvs = [{'visitors.csv', visitors} | csvs]
{:ok, {_, zip_content}} = :zip.create(filename, csvs, [:memory])
conn
|> put_resp_content_type("application/zip")
|> put_resp_header("content-disposition", "attachment; filename=\"#{filename}\"")
|> delete_resp_cookie("exporting")
|> send_resp(200, zip_content)
else
conn
|> send_resp(400, "")
|> halt()
end
end
@doc """
Authorizes and renders a shared link:
1. Shared link with no password protection: needs to just make sure the shared link entry is still
in our database. This check makes sure shared link access can be revoked by the site admins. If the
shared link exists, render it directly.
2. Shared link with password protection: Same checks as without the password, but an extra step is taken to
protect the page with a password. When the user passes the password challenge, a cookie is set with Plausible.Auth.Token.sign_shared_link().
The cookie allows the user to access the dashboard for 24 hours without entering the password again.
### Backwards compatibility
The URL format for shared links was changed in [this pull request](https://github.com/plausible/analytics/pull/752) in order
to make the URLs easier to bookmark. The old format is supported along with the new in order to not break old links.
See: https://plausible.io/docs/shared-links
"""
def shared_link(conn, %{"domain" => domain, "auth" => auth}) do
case find_shared_link(domain, auth) do
{:password_protected, shared_link} ->
render_password_protected_shared_link(conn, shared_link)
{:unlisted, shared_link} ->
render_shared_link(conn, shared_link)
:not_found ->
render_error(conn, 404)
end
end
@old_format_deprecation_date ~N[2022-01-01 00:00:00]
def shared_link(conn, %{"domain" => slug}) do
shared_link =
Repo.one(
from(l in Plausible.Site.SharedLink,
where: l.slug == ^slug and l.inserted_at < ^@old_format_deprecation_date,
preload: :site
)
)
if shared_link do
new_link_format = Routes.stats_path(conn, :shared_link, shared_link.site.domain, auth: slug)
redirect(conn, to: new_link_format)
else
render_error(conn, 404)
end
end
def shared_link(conn, _) do
render_error(conn, 400)
end
defp render_password_protected_shared_link(conn, shared_link) do
with conn <- Plug.Conn.fetch_cookies(conn),
{:ok, token} <- Map.fetch(conn.req_cookies, shared_link_cookie_name(shared_link.slug)),
{:ok, %{slug: token_slug}} <- Plausible.Auth.Token.verify_shared_link(token),
true <- token_slug == shared_link.slug do
render_shared_link(conn, shared_link)
else
_e ->
conn
|> assign(:skip_plausible_tracking, true)
|> render("shared_link_password.html",
link: shared_link,
layout: {PlausibleWeb.LayoutView, "focus.html"}
)
end
end
defp find_shared_link(domain, auth) do
link_query =
from(link in Plausible.Site.SharedLink,
inner_join: site in assoc(link, :site),
where: link.slug == ^auth,
where: site.domain == ^domain,
limit: 1,
preload: [site: site]
)
case Repo.one(link_query) do
%Plausible.Site.SharedLink{password_hash: hash} = link when not is_nil(hash) ->
{:password_protected, link}
%Plausible.Site.SharedLink{} = link ->
{:unlisted, link}
nil ->
:not_found
end
end
def authenticate_shared_link(conn, %{"slug" => slug, "password" => password}) do
Formatting only changes - No code change (#75) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details * removing deleted files from upstream * minor config adjustments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me>
2020-06-08 10:35:13 +03:00
shared_link =
Repo.get_by(Plausible.Site.SharedLink, slug: slug)
|> Repo.preload(:site)
if shared_link do
if Plausible.Auth.Password.match?(password, shared_link.password_hash) do
token = Plausible.Auth.Token.sign_shared_link(slug)
conn
|> put_resp_cookie(shared_link_cookie_name(slug), token)
|> redirect(to: "/share/#{URI.encode_www_form(shared_link.site.domain)}?auth=#{slug}")
else
2020-05-19 16:20:21 +03:00
conn
|> assign(:skip_plausible_tracking, true)
Formatting only changes - No code change (#75) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details * removing deleted files from upstream * minor config adjustments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me>
2020-06-08 10:35:13 +03:00
|> render("shared_link_password.html",
link: shared_link,
error: "Incorrect password. Please try again.",
layout: {PlausibleWeb.LayoutView, "focus.html"}
)
end
else
render_error(conn, 404)
end
end
defp render_shared_link(conn, shared_link) do
2021-12-14 13:10:34 +03:00
cond do
!shared_link.site.locked ->
conn
|> assign(:skip_plausible_tracking, true)
|> put_resp_header("x-robots-tag", "noindex, nofollow")
2021-12-14 13:10:34 +03:00
|> delete_resp_header("x-frame-options")
|> render("stats.html",
site: shared_link.site,
has_goals: Sites.has_goals?(shared_link.site),
Implement Funnels view on dashboard (#3066) * Add Funnel react component assets/js/dashboard/stats/behaviours/funnel.js - restored from: 98a76cbd Remove console.info calls d94db99d Convert Funnel class component into a functional one 028036ad Review comments 3067a940 Stop doing maths in react 73407cc3 Fix error handling when local storage gets corrupted e8c6fc52 Format numbers on funnel labels c815709f Reorganize component responsibility 7a88fe44 Outline basic error handling 94caed7c Chart styling updates 4514608a Add percentages to funnel d622c32d Add funnel picker Co-authored-by: Uku Taht <uku.taht@gmail.com> * Pass funnels list to react via data-funnels * Implement Funnels react API lib/plausible_web/controllers/api/stats_controller.ex - restored from: f36ad234 Adjust to Plausible.Stats interface 9b532273 Test funnel stats controller 028036ad Review comments bea3725f Remove IO.inspect 7a88fe44 Outline basic error handling c8ae3eaf Move Funnels to StatsController and use base query 667cf222 Put private functions at the bottom * Tweak funnel presentation * Handle errors at the top * Do not register DataLabels plugin globally or else all the existing charts are affected * Calculate drop-off percentage evaluating funnels * Tweak dark mode + implement nicer tooltips * Make currently selected funnel bold in the picker * Count user_ids not session_ids when evaluating funnels So if a visitor goes: 1. Start session 2. Complete funnel step 1 3. Inactive for 30 minutes 4. Complete funnel step 2 We would not be able to track this funnel completion because of the session timeout. We like to o measure this as funnel completion even though the session expired in the middle. cc @ukutaht * Add extra properties to the funnels API cc @ukutaht * Improve tooltips so that step to data is rendered * Change tooltip number formatting * Remove debugging remnants * Quick & dirty mobile view * Fix mobile view: tweak dark mode & funnel switching * Ignore DOMException: the operation was aborted Otherwise this sometimes flashes the space shuttle screen when navigating quickly via a keyboard. * Format percentages on the main chart * Close missing tag 🙈 * Revert "Close missing tag 🙈" This reverts commit 9c2f970e22fd7e2980503242b414f42ce8bce1d2. * Use jsx to render funnel tooltip To get markup validated via lsp mostly... * Fixup: s/class/className * Fix className interpolation * Add a ruler to the tooltip * Tweak funnel chart style * Fix font distortion issue on chart/canvas labels * s/class/className * Put "Set up funnels" link behind a feature flag * Refactor internal selection storage Getting ready for live funnel evaluation * Don't try to connect LV socket if there's no CRSF token set up This is perfectly okay for some of the templates/layouts. * Fix up funnel creation typespecs Unfortunately we can't define a type with literal string keys, hence this must suffice. * Use uniq over count/distinct * Revert JSX in tooltips Ref: https://github.com/plausible/analytics/pull/3066#discussion_r1241891155 * Remove the extra query for counting all visitors cc @ukutaht * Add premium notice --------- Co-authored-by: Uku Taht <uku.taht@gmail.com>
2023-06-27 11:04:35 +03:00
funnels: Plausible.Funnels.list(shared_link.site),
allowed_event_props: shared_link.site.allowed_event_props,
2022-04-06 11:52:19 +03:00
stats_start_date: shared_link.site.stats_start_date,
native_stats_start_date: NaiveDateTime.to_date(shared_link.site.native_stats_start_at),
title: title(conn, shared_link.site),
2021-12-14 13:10:34 +03:00
offer_email_report: false,
demo: false,
skip_plausible_tracking: true,
shared_link_auth: shared_link.slug,
embedded: conn.params["embed"] == "true",
background: conn.params["background"],
2022-04-21 10:54:08 +03:00
theme: conn.params["theme"],
2022-05-03 10:49:39 +03:00
flags: get_flags(conn.assigns[:current_user]),
is_dbip: is_dbip()
2021-12-14 13:10:34 +03:00
)
Sites.locked?(shared_link.site) ->
owner = Sites.owner_for(shared_link.site)
2021-12-14 13:10:34 +03:00
conn
|> assign(:skip_plausible_tracking, true)
|> render("site_locked.html", owner: owner, site: shared_link.site)
end
end
defp remove_email_report_banner(conn, site) do
if conn.assigns[:current_user] do
delete_session(conn, site.domain <> "_offer_email_report")
else
conn
end
end
defp shared_link_cookie_name(slug), do: "shared-link-" <> slug
2022-04-21 10:54:08 +03:00
defp get_flags(user) do
%{
Funnel site settings (#3039) * Update formatter config * Install LiveView JS integration & hooks * Temporarily update endpoint/session config * Optionally allow preloading funnels for goals * Site controller * Implement funnel settings lib/plausible_web/live/funnel_settings/combo_box.ex - restored from: 054de6e2 Fix the tab/blur bug again 20da4c89 Rename InputPicker to ComboBox lib/plausible_web/live/funnel_settings/form.ex - restored from: 9bedda3b Remove potential FIXME 20da4c89 Rename InputPicker to ComboBox 028036ad Review comments aea4ebc4 Access Funnel min/max steps via the __using__/1 macro 0dde27fd Remove inspect call eed588a7 Start testing the funnel editor 0e95228b Extract funnel settings test module 7b16ace5 Leverage aplinejs to deal with the tyranny 8dc6a3e7 wip cf228630 wip 30a43fd1 wip 89f10ecb wip 950a18d9 Dirty funnel save 298a6a53 wip 7690d50f wip 639c6238 fixup aa59adeb wip ff75c00b wip lib/plausible_web/live/funnel_settings/list.ex - restored from: 4eae122c Fix data-confirm attr interpolation 51f0397d Implement deleting funnels 1f6fe25d Add number of steps to funnels list 298a6a53 wip ff75c00b wip test/plausible_web/live/funnel_settings/funnel_settings/combo_box_test.exs - restored from: 20da4c89 Rename InputPicker to ComboBox test/plausible_web/live/funnel_settings/funnel_settings_test.exs - restored from: 34822ff4 Bootstrap InputPicker tests lib/plausible_web/live/funnel_settings.ex - restored from: 028036ad Review comments acd9c4f2 Prepare ephemeral funnel definitions so that users can test funnels 51f0397d Implement deleting funnels 0e95228b Extract funnel settings test module 8dc6a3e7 wip 89f10ecb wip 950a18d9 Dirty funnel save 298a6a53 wip aa59adeb wip ff75c00b wip test/plausible_web/controllers/error_report_controller_test.exs - restored from: 34822ff4 Bootstrap InputPicker tests test/support/html.ex - restored from: 0a53979d Improve InputPicker tests - include AlpineJS assertions 34822ff4 Bootstrap InputPicker tests lib/plausible_web/views/layout_view.ex - restored from: b490403b !ifxup lib/plausible_web/templates/site/settings_funnels.html.eex - restored from: 51f0397d Implement deleting funnels ea1315f3 Test funnels list in settings 7b16ace5 Leverage aplinejs to deal with the tyranny ff75c00b wip 4da25c35 Fixup lib/plausible_web/templates/layout/app.html.eex - restored from: ff75c00b wip * Add funnel settings route * Warn about funnels deletion when deleting goals lib/plausible_web/templates/site/settings_goals.html.eex - restored from: fdd9bcd0 Fixup f1e6364d Merge remote-tracking branch 'origin/master' into funnels-rebase 9d0b7c6d Fix markup error 4a4ddbdc Optionally preload funnels for goals and stub funnel-goal deletion ebdc4333 Extend the prompt in case of funnel-goal deletion 639c6238 fixup aa59adeb wip * Split new JS LiveView additions * Put funnels behind a feature flag * Integrate dashboard feature toggle * Update signing salt for live view * Update moduledocs * Update live reloader config * Use Phoenix.HTML.Safe for goal names * Workaround to get flashes working in embedded liveview * Keep feature toggles idempotent, rename property to setting We'll still retain the ability to flip bools on a lower level. * Update moduledocs * Make live flash disappear after 5s * Tailwind: purge .heex files too * Update docs link * Add live components to tailwind purge config * Update another flaky test Ref f0bdf872 cc @vinibrsl * Fix combobox input length w/ WebKit * Intoduce generic notice component * Revert "Fix combobox input length w/ WebKit" This reverts commit 3c653a6d85d5000167631e10ef45a93c13b41ed1. * Fix combobox input length on webkit * Make whole combobox item clickable, not only text * Fix glitch moving Save button on activation * Tweak dark mode * Show funnel form without waiting for funnel name input * Tweak dark mode * Include static Phoenix components in tailwind purge * Tune funnels form into a liveview of its own This is so that ComboBoxes can publish their selections and unavailable choices can be propagated to other siblings. * Push less data over websocket * Undo Lsp/formatter race condition * Fixup typespecs * Bust CI cache
2023-06-22 10:00:07 +03:00
funnels: Plausible.Funnels.enabled_for?(user),
props: FunWithFlags.enabled?(:props, for: user)
}
2022-04-21 10:54:08 +03:00
end
2022-05-03 10:38:59 +03:00
defp is_dbip() do
is_or_nil =
if Application.get_env(:plausible, :is_selfhost) do
if type = Plausible.Geo.database_type() do
2022-05-03 10:38:59 +03:00
String.starts_with?(type, "DBIP")
end
2022-05-03 10:38:59 +03:00
end
!!is_or_nil
2022-05-03 10:38:59 +03:00
end
defp title(%{path_info: ["plausible.io"]}, _) do
"Plausible Analytics: Live Demo"
end
defp title(_conn, site) do
"Plausible · " <> site.domain
end
2019-09-02 14:29:19 +03:00
end