defmodule PlausibleWeb.Tracker do import Plug.Conn use Agent base_variants = [ "hash", "outbound-links", "exclusions", "compat", "local", "manual", "file-downloads", "pageview-props", "tagged-events", "revenue" ] # Generates Power Set of all variants variants = 1..Enum.count(base_variants) |> Enum.map(fn x -> Combination.combine(base_variants, x) |> Enum.map(fn y -> Enum.sort(y) |> Enum.join(".") end) end) |> List.flatten() @base_filenames ["plausible", "script", "analytics"] @files_available ["plausible.js", "p.js"] ++ Enum.map(variants, fn v -> "plausible.#{v}.js" end) def init(opts) do Keyword.merge(opts, files_available: MapSet.new(@files_available)) end def call(conn, files_available: files_available) do filename = case conn.request_path do "/js/p.js" -> "p.js" "/js/" <> requested_filename -> sorted_script_variant(requested_filename) _ -> nil end if filename && MapSet.member?(files_available, filename) do location = Application.app_dir(:plausible, "priv/tracker/js/" <> filename) conn |> put_resp_header("content-type", "application/javascript") |> put_resp_header("x-content-type-options", "nosniff") |> put_resp_header("cross-origin-resource-policy", "cross-origin") |> put_resp_header("access-control-allow-origin", "*") |> put_resp_header("cache-control", "public, max-age=86400, must-revalidate") |> send_file(200, location) |> halt() else conn end end defp sorted_script_variant(requested_filename) do case String.split(requested_filename, ".") do [base_filename | rest] when base_filename in @base_filenames -> sorted_variants = rest |> List.delete("js") |> Enum.sort() Enum.join(["plausible"] ++ sorted_variants ++ ["js"], ".") _ -> nil end end end