PhoenixKitWebAnalytics.InternalTraffic (PhoenixKitWebAnalytics v0.6.1)

Copy Markdown View Source

Works out a hit's PhoenixKitWebAnalytics.TrafficFlags — whether it is the site's own people or their networks — without a query on the way.

  • internal_network — the client address falls in one of the networks in config :phoenix_kit_web_analytics, internal_networks: [...] (CIDR, IPv4 or IPv6; a bare address is one host). Parsed once and kept until the config changes.
  • admin — the signed-in user holds a role listed in web_analytics_internal_roles (Owner and Admin by default), by PhoenixKit.Users.Auth.Scope.held_roles/1: the roles they really hold, whichever one they are acting as. A hit that names only a user (no scope) is judged by a five-minute cache of that user's roles; on a miss the roles are looked up off the hit's path, so that hit goes unflagged and the next one — with the whole visit — gets the bit.
  • admin_network — the client's network (an IPv4 address, an IPv6 /64, as PhoenixKit.Utils.IpAddress.network/1 keys it) had a staff member signed in within the last web_analytics_admin_network_hours (24 by default; 0 turns it off). Learnt from two places: a staff sign-in (core's {:session_created, …}, which every node hears — the address is the session token's, so an admin who only ever works in the admin panel, which is never tracked, still counts) and a staff member's own request through PhoenixKitWebAnalytics.Plug, shared with the other nodes. Private, loopback and unparseable addresses are never taken.

A network is remembered with the time a staff member was last seen on it, and judged against the hours set now: shortening them takes effect at once for every network already learnt.

A network learnt this way is held in memory only, under its plain key (hashing it would not protect anything: the key would sit on the same node), and forgotten when its time is up. Nothing about it is written to the database or the settings.

Behind carrier-grade NAT, a mobile network or an office gateway, one address is many people: a staff sign-in from a phone flags everyone behind the same public address for those hours. That's the trade-off of the admin_network flag, and why it can be counted back in from Settings or its hours set to 0.

Summary

Functions

Whether ip's network had a staff sign-in within the configured hours.

Returns a specification to start this module under a supervisor.

The flags of a hit: :ip (a tuple), :roles (the signed-in user's held roles, when the hit came with a scope) and :user_uuid. Never raises; 0 when nothing can be told.

Forgets, now rather than at the next sweep, the networks past the hours set now (all of them at 0) and the expired role entries. Called after a settings save.

Whether ip is in one of the configured internal_networks. An invalid entry is skipped, with one warning.

How many configured internal_networks are networks and how many aren't (those are skipped) — for the settings page.

The flags an address alone gives — internal_network and admin_network. What the recording endpoint can tell before it knows the visit.

Notes that a staff member was seen at ip (a tuple or a string): its network counts as a staff network for the configured hours. Told to the other nodes only when the network is new here or past half its time, so a staff member browsing doesn't cost a broadcast per request. Private, loopback and unparseable addresses are ignored. Never raises.

"203.0.113.0/24" → [{32, base, 24}]; [] for anything that isn't a network.

Whether any of roles is a staff role under config.

Whether a hit is a staff member's — by the roles it carries, or by its user's cached roles (a miss starts the lookup and answers false).

Functions

admin_network?(ip, arg2)

Whether ip's network had a staff sign-in within the configured hours.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

flags(hit, config)

The flags of a hit: :ip (a tuple), :roles (the signed-in user's held roles, when the hit came with a scope) and :user_uuid. Never raises; 0 when nothing can be told.

forget_expired()

@spec forget_expired() :: :ok

Forgets, now rather than at the next sweep, the networks past the hours set now (all of them at 0) and the expired role entries. Called after a settings save.

internal_network?(ip)

@spec internal_network?(:inet.ip_address() | nil) :: boolean()

Whether ip is in one of the configured internal_networks. An invalid entry is skipped, with one warning.

network_counts()

@spec network_counts() :: %{valid: non_neg_integer(), invalid: non_neg_integer()}

How many configured internal_networks are networks and how many aren't (those are skipped) — for the settings page.

network_flags(ip, config)

The flags an address alone gives — internal_network and admin_network. What the recording endpoint can tell before it knows the visit.

note_admin_network(ip, arg2)

@spec note_admin_network(
  :inet.ip_address() | String.t() | nil,
  PhoenixKitWebAnalytics.Config.collection_config()
) :: :ok

Notes that a staff member was seen at ip (a tuple or a string): its network counts as a staff network for the configured hours. Told to the other nodes only when the network is new here or past half its time, so a staff member browsing doesn't cost a broadcast per request. Private, loopback and unparseable addresses are ignored. Never raises.

parse_cidr(cidr)

@spec parse_cidr(term()) :: [{32 | 128, non_neg_integer(), non_neg_integer()}]

"203.0.113.0/24" → [{32, base, 24}]; [] for anything that isn't a network.

staff?(roles, config)

Whether any of roles is a staff role under config.

staff_hit?(hit, config)

Whether a hit is a staff member's — by the roles it carries, or by its user's cached roles (a miss starts the lookup and answers false).