File size: 5,120 Bytes
88211d3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | defmodule PlausibleWeb.Endpoint do
use Plausible
use Sentry.PlugCapture
use Phoenix.Endpoint, otp_app: :plausible
on_ce do
plug :maybe_handle_acme_challenge
plug :maybe_force_ssl, Plug.SSL.init(_no_opts = [])
end
@session_options [
# in EE key is replaced dynamically via runtime_session_opts, see below
key: "_plausible_key",
store: :cookie,
signing_salt: "I45i0SKHEku2f3tJh6y4v8gztrb/eG5KGCOe/o/AwFb7VHeuvDOn7AAq6KsdmOFM",
# 5 years, this is super long but the SlidingSessionTimeout will log people out if they don't return for 2 weeks
max_age: 60 * 60 * 24 * 365 * 5,
extra: "SameSite=Lax"
# in EE domain is added dynamically via runtime_session_opts, see below
]
socket("/live", Phoenix.LiveView.Socket,
websocket: [
check_origin: true,
connect_info: [
:peer_data,
:uri,
:user_agent,
session: {__MODULE__, :runtime_session_opts, []}
]
]
)
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phx.digest
# when deploying your static files in production.
plug(PlausibleWeb.TrackerPlug)
plug(PlausibleWeb.Favicon)
static_paths = ~w(css js images favicon.ico)
static_paths =
on_ee do
# NOTE: The Cloud uses custom robots.txt from https://github.com/plausible/website: https://plausible.io/robots.txt
static_paths
else
static_paths ++ ["robots.txt"]
end
static_compression =
if ce?() do
[brotli: true, gzip: true]
else
[gzip: false]
end
plug(
Plug.Static,
[at: "/", from: :plausible, only: static_paths] ++ static_compression
)
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
socket("/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket)
plug(Phoenix.LiveReloader)
plug(Phoenix.CodeReloader)
end
plug(Plug.RequestId)
plug(PromEx.Plug, prom_ex_module: Plausible.PromEx)
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint], log: false)
plug(Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()
)
plug(Sentry.PlugContext)
plug(Plug.MethodOverride)
plug(Plug.Head)
plug(:runtime_session)
plug(CORSPlug)
plug(PlausibleWeb.Router)
def secure_cookie?, do: config!(:secure_cookie)
def websocket_url() do
config!(:websocket_url)
end
def runtime_session(conn, _opts) do
Plug.run(conn, [{Plug.Session, runtime_session_opts()}])
end
def runtime_session_opts() do
session_options =
on_ee do
# `host()` provided by Phoenix.Endpoint's compilation hooks
# is used to inject the domain - this way we can authenticate
# websocket requests within single root domain, in case websocket_url()
# returns a ws{s}:// scheme (in which case SameSite=Lax is not applicable).
Keyword.put(@session_options, :domain, host())
|> Keyword.put(:key, "_plausible_#{Application.fetch_env!(:plausible, :environment)}")
else
# CE setup is simpler and we don't need to worry about WS domain being different
@session_options
end
session_options
|> Keyword.put(:secure, secure_cookie?())
end
defp config!(key) do
:plausible
|> Application.fetch_env!(__MODULE__)
|> Keyword.fetch!(key)
end
on_ce do
require SiteEncrypt
@behaviour SiteEncrypt
@force_https_key {:plausible, :force_https}
@allow_acme_challenges_key {:plausible, :allow_acme_challenges}
@doc false
def force_https do
:persistent_term.put(@force_https_key, true)
end
@doc false
def allow_acme_challenges do
:persistent_term.put(@allow_acme_challenges_key, true)
end
defp maybe_handle_acme_challenge(conn, _opts) do
if :persistent_term.get(@allow_acme_challenges_key, false) do
SiteEncrypt.AcmeChallenge.call(conn, _endpoint = __MODULE__)
else
conn
end
end
defp maybe_force_ssl(conn, opts) do
if :persistent_term.get(@force_https_key, false) do
Plug.SSL.call(conn, opts)
else
conn
end
end
@impl SiteEncrypt
def handle_new_cert, do: :ok
@doc false
def app_env_config do
# this function is being used by site_encrypt
Application.get_env(:plausible, _endpoint = __MODULE__, [])
end
@impl SiteEncrypt
def certification do
selfhost_config = Application.fetch_env!(:plausible, :selfhost)
config = Keyword.fetch!(selfhost_config, :site_encrypt)
domain = Keyword.fetch!(config, :domain)
email = Keyword.fetch!(config, :email)
db_folder = Keyword.fetch!(config, :db_folder)
directory_url = Keyword.fetch!(config, :directory_url)
SiteEncrypt.configure(
mode: :auto,
log_level: :notice,
client: :certbot,
domains: [domain],
emails: [email],
db_folder: db_folder,
directory_url: directory_url
)
end
end
end
|