mirror of
https://github.com/plausible/analytics.git
synced 2024-09-11 09:56:02 +03:00
Custom domains (#34)
* UI to create custom domains * Only call ssh once per domain * Update copy for custom domain setup * Use correct user for ssh
This commit is contained in:
parent
da58d8d87e
commit
79b9f72b52
30
lib/mix/tasks/obtain_ssl_certificates.ex
Normal file
30
lib/mix/tasks/obtain_ssl_certificates.ex
Normal file
@ -0,0 +1,30 @@
|
||||
defmodule Mix.Tasks.ObtainSslCertificates do
|
||||
use Plausible.Repo
|
||||
|
||||
def run(_args) do
|
||||
Application.ensure_all_started(:plausible)
|
||||
execute()
|
||||
end
|
||||
|
||||
def execute(system \\ System) do
|
||||
recent_custom_domains = Repo.all(
|
||||
from cd in Plausible.Site.CustomDomain,
|
||||
where: cd.updated_at > fragment("now() - '3 days'::interval"),
|
||||
where: not cd.has_ssl_certificate
|
||||
)
|
||||
|
||||
for domain <- recent_custom_domains do
|
||||
system.cmd("ssh", ["-t", "ubuntu@custom.plausible.io", "sudo certbot certonly --nginx -n -d #{domain.domain}"])
|
||||
|> report_result(domain)
|
||||
end
|
||||
end
|
||||
|
||||
defp report_result({_, 0}, domain) do
|
||||
Ecto.Changeset.change(domain, has_ssl_certificate: true) |> Repo.update!
|
||||
Plausible.Slack.notify("Obtained SSL cert for #{domain.domain}")
|
||||
end
|
||||
|
||||
defp report_result({error_msg, error_code}, domain) do
|
||||
Sentry.capture_message("Error obtaining SSL certificate", extra: %{error_msg: error_msg, error_code: error_code, domain: domain.domain})
|
||||
end
|
||||
end
|
20
lib/plausible/site/custom_domain.ex
Normal file
20
lib/plausible/site/custom_domain.ex
Normal file
@ -0,0 +1,20 @@
|
||||
defmodule Plausible.Site.CustomDomain do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
@domain_name_regex ~r/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/
|
||||
|
||||
schema "custom_domains" do
|
||||
field :domain, :string
|
||||
field :has_ssl_certificate, :boolean
|
||||
belongs_to :site, Plausible.Site
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(custom_domain, attrs) do
|
||||
custom_domain
|
||||
|> cast(attrs, [:domain, :site_id])
|
||||
|> validate_required([:domain, :site_id])
|
||||
|> validate_format(:domain, @domain_name_regex, message: "please enter a valid domain name")
|
||||
end
|
||||
end
|
@ -13,6 +13,7 @@ defmodule Plausible.Site do
|
||||
has_one :google_auth, GoogleAuth
|
||||
has_one :weekly_report, Plausible.Site.WeeklyReport
|
||||
has_one :monthly_report, Plausible.Site.MonthlyReport
|
||||
has_one :custom_domain, Plausible.Site.CustomDomain
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
@ -1,5 +1,6 @@
|
||||
defmodule Plausible.Sites do
|
||||
use Plausible.Repo
|
||||
alias Plausible.Site.CustomDomain
|
||||
|
||||
def get_for_user!(user_id, domain) do
|
||||
Repo.one!(
|
||||
@ -31,4 +32,11 @@ defmodule Plausible.Sites do
|
||||
where: sm.user_id == ^user_id and sm.site_id == ^site.id
|
||||
)
|
||||
end
|
||||
|
||||
def add_custom_domain(site, custom_domain) do
|
||||
CustomDomain.changeset(%CustomDomain{}, %{
|
||||
site_id: site.id,
|
||||
domain: custom_domain
|
||||
}) |> Repo.insert
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,7 @@
|
||||
defmodule Plausible.Slack do
|
||||
@app_env System.get_env("APP_ENV") || "dev"
|
||||
@feed_channel_url "https://hooks.slack.com/services/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"
|
||||
@feed_channel_url "https://hooks.slack.com/services/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"
|
||||
require Logger
|
||||
|
||||
def notify(text) do
|
||||
Task.start(fn ->
|
||||
@ -8,7 +9,7 @@ defmodule Plausible.Slack do
|
||||
"prod" ->
|
||||
HTTPoison.post!(@feed_channel_url, Poison.encode!(%{text: text}))
|
||||
_ ->
|
||||
nil
|
||||
Logger.debug(text)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
@ -75,6 +75,7 @@ defmodule PlausibleWeb.SiteController do
|
||||
def settings(conn, %{"website" => website}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|> Repo.preload(:google_auth)
|
||||
|> Repo.preload(:custom_domain)
|
||||
|
||||
search_console_domains = if site.google_auth do
|
||||
Plausible.Google.Api.fetch_verified_properties(site.google_auth)
|
||||
@ -290,6 +291,38 @@ defmodule PlausibleWeb.SiteController do
|
||||
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings#visibility")
|
||||
end
|
||||
|
||||
def new_custom_domain(conn, %{"website" => website}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
changeset = Plausible.Site.CustomDomain.changeset(%Plausible.Site.CustomDomain{}, %{})
|
||||
|
||||
render(conn, "new_custom_domain.html", site: site, changeset: changeset, layout: {PlausibleWeb.LayoutView, "focus.html"})
|
||||
end
|
||||
|
||||
def custom_domain_dns_setup(conn, %{"website" => website}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|> Repo.preload(:custom_domain)
|
||||
|
||||
render(conn, "custom_domain_dns_setup.html", site: site, layout: {PlausibleWeb.LayoutView, "focus.html"})
|
||||
end
|
||||
|
||||
def custom_domain_snippet(conn, %{"website" => website}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|> Repo.preload(:custom_domain)
|
||||
|
||||
render(conn, "custom_domain_snippet.html", site: site, layout: {PlausibleWeb.LayoutView, "focus.html"})
|
||||
end
|
||||
|
||||
def add_custom_domain(conn, %{"website" => website, "custom_domain" => domain}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|
||||
case Sites.add_custom_domain(site, domain["domain"]) do
|
||||
{:ok, _custom_domain} ->
|
||||
redirect(conn, to: "/sites/#{URI.encode_www_form(site.domain)}/custom-domains/dns-setup")
|
||||
{:error, changeset} ->
|
||||
render(conn, "new_custom_domain.html", site: site, changeset: changeset, layout: {PlausibleWeb.LayoutView, "focus.html"})
|
||||
end
|
||||
end
|
||||
|
||||
defp insert_site(user_id, params) do
|
||||
site_changeset = Plausible.Site.changeset(%Plausible.Site{}, params)
|
||||
|
||||
|
@ -113,6 +113,11 @@ defmodule PlausibleWeb.Router do
|
||||
post "/sites/:website/shared-links", SiteController, :create_shared_link
|
||||
delete "/sites/:website/shared-links/:slug", SiteController, :delete_shared_link
|
||||
|
||||
get "/sites/:website/custom-domains/new", SiteController, :new_custom_domain
|
||||
get "/sites/:website/custom-domains/dns-setup", SiteController, :custom_domain_dns_setup
|
||||
get "/sites/:website/custom-domains/snippet", SiteController, :custom_domain_snippet
|
||||
post "/sites/:website/custom-domains", SiteController, :add_custom_domain
|
||||
|
||||
get "/sites/:website/weekly-report/unsubscribe", UnsubscribeController, :weekly_report
|
||||
get "/sites/:website/monthly-report/unsubscribe", UnsubscribeController, :monthly_report
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
<div class="max-w-sm w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8">
|
||||
<h2>DNS for <%= @site.custom_domain.domain %></h2>
|
||||
<ol class="my-6">
|
||||
<li>Go to your DNS provider’s website</li>
|
||||
<li class="mt-4">Create a new CNAME record for <code><%= @site.custom_domain.domain %></code></li>
|
||||
<li class="mt-4">Point the record to <code>custom.plausible.io.</code> (including the dot)</li>
|
||||
</ol>
|
||||
|
||||
<%= link("Done ->", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/snippet", class: "button w-full") %>
|
||||
</div>
|
@ -0,0 +1,27 @@
|
||||
<%= form_for @conn, "/", [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 -> %>
|
||||
<div class="flex items-center justify-between">
|
||||
<h2>Update javascript snippet</h2>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<p>
|
||||
Allow up to 4 hours for DNS changes to propagate and for us to obtain an SSL certificate for <code><%= @site.custom_domain.domain %></code>
|
||||
</p>
|
||||
<p class="mt-4">
|
||||
The setup is working when <a href="//<%= @site.custom_domain.domain %>/js/plausible.js" target="_blank" class="text-indigo"><%= @site.custom_domain.domain %>/js/plausible.js</a> loads the javascript tracker.
|
||||
</p>
|
||||
<p class="mt-4">
|
||||
To finish your setup, please update the tracking snippet on your site with your custom domain.
|
||||
</p>
|
||||
|
||||
<div class="relative">
|
||||
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 pr-6 text-grey-darker leading-normal appearance-none focus:outline-none focus:bg-white focus:border-grey-light text-xs mt-4 resize-none", value: snippet(@site), rows: 2, readonly: "readonly" %>
|
||||
<a onclick="var textarea = document.getElementById('snippet_code'); textarea.focus(); textarea.select(); document.execCommand('copy');" href="javascript:void(0)" class="no-underline text-indigo text-sm hover:underline">
|
||||
<svg class="absolute text-indigo" style="top: 24px; right: 12px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<%= link("Back to settings →", class: "button mt-4 w-full", to: "/#{URI.encode_www_form(@site.domain)}/settings#custom-domain") %>
|
||||
<p class="mt-4 text-grey-dark text-sm">
|
||||
Problems? <%= link("Get help via email", to: "mailto:uku@plausible.io", class: "text-indigo-darker underline") %>
|
||||
</p>
|
||||
<% end %>
|
15
lib/plausible_web/templates/site/new_custom_domain.html.eex
Normal file
15
lib/plausible_web/templates/site/new_custom_domain.html.eex
Normal file
@ -0,0 +1,15 @@
|
||||
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains", [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>Setup custom domain</h2>
|
||||
<div class="my-6">
|
||||
We recommend using a subdomain of the website you're running Plausible on.
|
||||
If your site is on <code>example.com</code> you can use <code>stats.example.com</code>.
|
||||
<br /><br /> The name of the subdomain can be anything, it doesn't have to be <code>stats</code>.
|
||||
</div>
|
||||
<div class="my-6">
|
||||
<%= label f, :domain, class: "block text-sm font-bold" %>
|
||||
<%= text_input f, :domain, class: "transition mt-3 bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:bg-white focus:border-grey-light", placeholder: "stats.[yourdomain].com" %>
|
||||
<%= error_tag f, :domain %>
|
||||
</div>
|
||||
|
||||
<%= submit "DNS setup ->", class: "button mt-4 w-full" %>
|
||||
<% end %>
|
@ -207,10 +207,22 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= form_for @conn, "/", [class: "bg-white max-w-md mx-auto shadow-md rounded rounded-t-none border-t-2 border-indigo-lightest px-8 pt-6 pb-8 mb-4 mt-16"], fn f -> %>
|
||||
<div class="flex items-center justify-between">
|
||||
<h2>Javascript snippet</h2>
|
||||
<div class="bg-white max-w-md mx-auto shadow-md rounded rounded-t-none border-t-2 border-indigo-lightest px-8 pt-6 pb-8 mb-4 mt-16" id="custom-domain">
|
||||
<h2>Custom domain</h2>
|
||||
<div class="my-4">
|
||||
Some browsers and extensions block analytics services. To get around that, Plausible offers a quick and easy way to serve the script from your own custom domain.
|
||||
</div>
|
||||
<%= if @site.custom_domain do %>
|
||||
Configured domain: <b><%= @site.custom_domain.domain %></b>
|
||||
<% else %>
|
||||
<div class="mt-4">
|
||||
<%= link("Add custom domain", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/new", class: "button") %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= form_for @conn, "/", [class: "bg-white max-w-md mx-auto shadow-md rounded rounded-t-none border-t-2 border-indigo-lightest px-8 pt-6 pb-8 mb-4 mt-16"], fn f -> %>
|
||||
<h2>Javascript snippet</h2>
|
||||
<div class="my-4">
|
||||
<p>Include this snippet in the <code><head></code> of your website.</p>
|
||||
<div class="relative">
|
||||
|
@ -6,7 +6,7 @@
|
||||
<p>Paste this snippet in the <code><head></code> of your website.</p>
|
||||
|
||||
<div class="relative">
|
||||
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 pr-6 text-grey-darker leading-normal appearance-none focus:outline-none focus:bg-white focus:border-grey-light text-xs mt-4 resize-none", value: snippet(@site), rows: 2 %>
|
||||
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 pr-6 text-grey-darker leading-normal appearance-none focus:outline-none focus:bg-white focus:border-grey-light text-xs mt-4 resize-none", value: snippet(@site), rows: 2, readonly: "readonly" %>
|
||||
<a onclick="var textarea = document.getElementById('snippet_code'); textarea.focus(); textarea.select(); document.execCommand('copy');" href="javascript:void(0)" class="no-underline text-indigo text-sm hover:underline">
|
||||
<svg class="absolute text-indigo" style="top: 24px; right: 12px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
|
||||
</a>
|
||||
|
@ -14,8 +14,10 @@ defmodule PlausibleWeb.SiteView do
|
||||
end
|
||||
|
||||
def snippet(site) do
|
||||
tracker_domain = if site.custom_domain, do: site.custom_domain.domain, else: "plausible.io"
|
||||
|
||||
"""
|
||||
<script async defer data-domain="#{site.domain}" src="https://plausible.io/js/plausible.js"></script>
|
||||
<script async defer data-domain="#{site.domain}" src="https://#{tracker_domain}/js/plausible.js"></script>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
5
mix.exs
5
mix.exs
@ -33,7 +33,7 @@ defmodule Plausible.MixProject do
|
||||
# Type `mix help deps` for examples and options.
|
||||
defp deps do
|
||||
[
|
||||
{:browser, "~> 0.4.3"},
|
||||
{:browser, "~> 0.4.3"}, # remove
|
||||
{:bcrypt_elixir, "~> 2.0"},
|
||||
{:cors_plug, "~> 1.5"},
|
||||
{:ecto_sql, "~> 3.0"},
|
||||
@ -47,7 +47,7 @@ defmodule Plausible.MixProject do
|
||||
{:phoenix_pubsub, "~> 1.1"},
|
||||
{:plug_cowboy, "~> 2.0"},
|
||||
{:postgrex, ">= 0.0.0"},
|
||||
{:poison, "~> 3.1"}, # Used in paddle_api
|
||||
{:poison, "~> 3.1"}, # Used in paddle_api, can remove
|
||||
{:ref_inspector, "~> 1.3"},
|
||||
{:timex, "~> 3.6"},
|
||||
{:ua_inspector, "~> 0.18"},
|
||||
@ -57,6 +57,7 @@ defmodule Plausible.MixProject do
|
||||
{:httpoison, "~> 1.4"},
|
||||
{:ex_machina, "~> 2.3", only: :test},
|
||||
{:excoveralls, "~> 0.10", only: :test},
|
||||
{:double, "~> 0.7.0", only: :test},
|
||||
{:joken, "~> 2.0"},
|
||||
{:php_serializer, "~> 0.9.0"},
|
||||
{:csv, "~> 2.3"},
|
||||
|
1
mix.lock
1
mix.lock
@ -14,6 +14,7 @@
|
||||
"csv": {:hex, :csv, "2.3.1", "9ce11eff5a74a07baf3787b2b19dd798724d29a9c3a492a41df39f6af686da0e", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
|
||||
"double": {:hex, :double, "0.7.0", "a7ee4c3488a0acc6d2ad9b69b6c7d3ddf3da2b54488d0f7c2d6ceb3a995887ca", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "3.0.8", "9eb6a1fcfc593e6619d45ef51afe607f1554c21ca188a1cd48eecc27223567f1", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.0.5", "7e44172b4f7aca4469f38d7f6a3da394dbf43a1bcf0ca975e958cb957becd74e", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.6", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.3.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.5.2", "96a28c79f5b8d34879cd95ebc04d2a0d678cfbbd3e74c43cb63a76adf0ee8054", [:mix], [], "hexpm"},
|
||||
|
@ -0,0 +1,15 @@
|
||||
defmodule Plausible.Repo.Migrations.CreateCustomDomains do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:custom_domains) do
|
||||
add :domain, :text, null: false
|
||||
add :site_id, references(:sites), null: false
|
||||
add :has_ssl_certificate, :boolean, null: false, default: false
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
create unique_index(:custom_domains, :site_id)
|
||||
end
|
||||
end
|
37
test/mix/tasks/obtain_ssl_certificates_test.exs
Normal file
37
test/mix/tasks/obtain_ssl_certificates_test.exs
Normal file
@ -0,0 +1,37 @@
|
||||
defmodule Mix.Tasks.ObtainSslCertificatesTest do
|
||||
use Plausible.DataCase
|
||||
alias Mix.Tasks.ObtainSslCertificates
|
||||
import Double
|
||||
|
||||
test "makes ssh call to certbot" do
|
||||
site = insert(:site)
|
||||
insert(:custom_domain, site: site, domain: "custom-site.com")
|
||||
|
||||
system_stub = stub(System, :cmd, fn(_cmd, _args) -> {"", 0} end)
|
||||
ObtainSslCertificates.execute(system_stub)
|
||||
|
||||
assert_receive({System, :cmd, ["ssh", ["-t", "ubuntu@custom.plausible.io", "sudo certbot certonly --nginx -n -d custom-site.com"]]})
|
||||
end
|
||||
|
||||
test "sets has_ssl_certficate=true if the ssh command is succesful" do
|
||||
site = insert(:site)
|
||||
insert(:custom_domain, site: site, domain: "custom-site.com")
|
||||
|
||||
system_stub = stub(System, :cmd, fn(_cmd, _args) -> {"", 0} end)
|
||||
ObtainSslCertificates.execute(system_stub)
|
||||
|
||||
domain = Repo.get_by(Plausible.Site.CustomDomain, site_id: site.id)
|
||||
assert domain.has_ssl_certificate
|
||||
end
|
||||
|
||||
test "does not set has_ssl_certficate=true if the ssh command fails" do
|
||||
site = insert(:site)
|
||||
insert(:custom_domain, site: site, domain: "custom-site.com")
|
||||
|
||||
failing_system_stub = stub(System, :cmd, fn(_cmd, _args) -> {"", 1} end)
|
||||
ObtainSslCertificates.execute(failing_system_stub)
|
||||
|
||||
domain = Repo.get_by(Plausible.Site.CustomDomain, site_id: site.id)
|
||||
refute domain.has_ssl_certificate
|
||||
end
|
||||
end
|
@ -296,7 +296,7 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "POSt /sites/:website/shared-links" do
|
||||
describe "POST /sites/:website/shared-links" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "creates shared link without password", %{conn: conn, site: site} do
|
||||
@ -332,4 +332,52 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
assert redirected_to(conn, 302) =~ "/#{site.domain}/settings"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /sites/:website/custom-domains/new" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "shows form for new custom domain", %{conn: conn, site: site} do
|
||||
conn = get(conn, "/sites/#{site.domain}/custom-domains/new")
|
||||
|
||||
assert html_response(conn, 200) =~ "Setup custom domain"
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /sites/:website/custom-domains" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "creates a custom domain", %{conn: conn, site: site} do
|
||||
conn = post(conn, "/sites/#{site.domain}/custom-domains", %{"custom_domain" => %{"domain" => "plausible.example.com"}})
|
||||
|
||||
domain = Repo.one(Plausible.Site.CustomDomain)
|
||||
|
||||
assert redirected_to(conn, 302) =~ "/sites/#{site.domain}/custom-domains/dns-setup"
|
||||
assert domain.domain == "plausible.example.com"
|
||||
end
|
||||
|
||||
test "validates presence of domain name", %{conn: conn, site: site} do
|
||||
conn = post(conn, "/sites/#{site.domain}/custom-domains", %{"custom_domain" => %{"domain" => ""}})
|
||||
|
||||
refute Repo.one(Plausible.Site.CustomDomain)
|
||||
assert html_response(conn, 200) =~ "Setup custom domain"
|
||||
end
|
||||
|
||||
test "validates format of domain name", %{conn: conn, site: site} do
|
||||
conn = post(conn, "/sites/#{site.domain}/custom-domains", %{"custom_domain" => %{"domain" => "ASD?/not-domain"}})
|
||||
|
||||
refute Repo.one(Plausible.Site.CustomDomain)
|
||||
assert html_response(conn, 200) =~ "Setup custom domain"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /sites/:website/custom-domains/dns-setup" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "shows instructions to set up dns", %{conn: conn, site: site} do
|
||||
domain = insert(:custom_domain, site: site)
|
||||
conn = get(conn, "/sites/#{site.domain}/custom-domains/dns-setup")
|
||||
|
||||
assert html_response(conn, 200) =~ "DNS for #{domain.domain}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -81,6 +81,12 @@ defmodule Plausible.Factory do
|
||||
}
|
||||
end
|
||||
|
||||
def custom_domain_factory do
|
||||
%Plausible.Site.CustomDomain{
|
||||
domain: sequence(:custom_domain, &"domain-#{&1}.com")
|
||||
}
|
||||
end
|
||||
|
||||
def tweet_factory do
|
||||
%Plausible.Twitter.Tweet{
|
||||
tweet_id: UUID.uuid4(),
|
||||
|
@ -1,3 +1,4 @@
|
||||
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
||||
ExUnit.start()
|
||||
Application.ensure_all_started(:double)
|
||||
Ecto.Adapters.SQL.Sandbox.mode(Plausible.Repo, :manual)
|
||||
|
Loading…
Reference in New Issue
Block a user