PhoenixKitWebAnalytics.Web.BeaconPayload (PhoenixKitWebAnalytics v0.2.0)

Copy Markdown View Source

Turns an untrusted beacon payload into a hit map for PhoenixKitWebAnalytics.Collector.

This is the trust boundary for the two public collection endpoints, kept out of the controller so it can be tested directly for exactly the things that matter: what a client can and cannot influence.

A client controls only the content of a hit — which path, which event name, which properties. It cannot influence identity or origin:

  • site is conn.host, so a payload can't attribute hits to another site
  • only the path is read from the client's URL; scheme and host are dropped
  • user_uuid comes from the session, never from the body
  • visitor_id is derived server-side downstream and isn't representable here at all

Free-form content is capped so an unbounded jsonb blob per event can't turn the analytics table into the largest one in the database: 20 properties, 200 bytes per value, 120 bytes of event name.

Summary

Functions

Whether the payload describes a custom event or a page view.

Extracts just the path from a client-sent URL.

Caps custom event properties by count, key length, and value size.

Builds the hit map for params received on conn.

Extracts campaign parameters from a client-sent URL's query string.

Functions

event_type(arg1)

@spec event_type(map()) :: String.t()

Whether the payload describes a custom event or a page view.

A payload with a non-empty n is an event; everything else — including an explicit "e" => "pageview" — is a page view.

path(url)

@spec path(term()) :: String.t()

Extracts just the path from a client-sent URL.

Anything that isn't an absolute path becomes "/"URI.parse/1 happily reports a bare word as a relative path, and a "path" that doesn't start with a slash would corrupt every pages report it appeared in.

iex> PhoenixKitWebAnalytics.Web.BeaconPayload.path("https://evil.example/steal?x=1")
"/steal"

iex> PhoenixKitWebAnalytics.Web.BeaconPayload.path("garbage")
"/"

props(props)

@spec props(term()) :: map()

Caps custom event properties by count, key length, and value size.

to_hit(conn, params)

@spec to_hit(Plug.Conn.t(), map()) :: map()

Builds the hit map for params received on conn.

Never raises: every field degrades to nil or a default, because the caller is a public endpoint that must answer 204 no matter what it was sent.

utm_params(url)

@spec utm_params(term()) :: map()

Extracts campaign parameters from a client-sent URL's query string.