update viewers page
This commit is contained in:
+80
-13
@@ -173,6 +173,10 @@ function ensureAnalyticsDb() {
|
||||
language text not null default '',
|
||||
timezone text not null default '',
|
||||
country text not null default '',
|
||||
region text not null default '',
|
||||
city text not null default '',
|
||||
latitude real,
|
||||
longitude real,
|
||||
consent text not null default 'analytics',
|
||||
metadata text not null default '{}'
|
||||
);
|
||||
@@ -193,7 +197,11 @@ function ensureAnalyticsDb() {
|
||||
screen text not null default '',
|
||||
language text not null default '',
|
||||
timezone text not null default '',
|
||||
country text not null default ''
|
||||
country text not null default '',
|
||||
region text not null default '',
|
||||
city text not null default '',
|
||||
latitude real,
|
||||
longitude real
|
||||
);
|
||||
|
||||
create index if not exists viewer_events_occurred_at_idx
|
||||
@@ -209,6 +217,14 @@ function ensureAnalyticsDb() {
|
||||
for (const statement of [
|
||||
`alter table viewer_events add column country text not null default ''`,
|
||||
`alter table active_viewers add column country text not null default ''`,
|
||||
`alter table viewer_events add column region text not null default ''`,
|
||||
`alter table active_viewers add column region text not null default ''`,
|
||||
`alter table viewer_events add column city text not null default ''`,
|
||||
`alter table active_viewers add column city text not null default ''`,
|
||||
`alter table viewer_events add column latitude real`,
|
||||
`alter table active_viewers add column latitude real`,
|
||||
`alter table viewer_events add column longitude real`,
|
||||
`alter table active_viewers add column longitude real`,
|
||||
]) {
|
||||
try {
|
||||
analyticsDb.exec(statement)
|
||||
@@ -417,6 +433,22 @@ function countryFromHeaders(req) {
|
||||
return country
|
||||
}
|
||||
|
||||
function numberHeader(req, name, min, max) {
|
||||
const value = Number(headerValue(req, name, 40))
|
||||
if (!Number.isFinite(value) || value < min || value > max) return null
|
||||
return value
|
||||
}
|
||||
|
||||
function locationFromHeaders(req) {
|
||||
return {
|
||||
country: countryFromHeaders(req),
|
||||
region: headerValue(req, 'cf-region', 120),
|
||||
city: headerValue(req, 'cf-ipcity', 120),
|
||||
latitude: numberHeader(req, 'cf-iplatitude', -90, 90),
|
||||
longitude: numberHeader(req, 'cf-iplongitude', -180, 180),
|
||||
}
|
||||
}
|
||||
|
||||
function analyticsMetadata(req, payload) {
|
||||
const metadata = payload.metadata && typeof payload.metadata === 'object' ? payload.metadata : {}
|
||||
const preferences = metadata.preferences && typeof metadata.preferences === 'object'
|
||||
@@ -519,6 +551,7 @@ function recordViewerEvent(req, payload) {
|
||||
purgeOldAnalytics(db)
|
||||
|
||||
const serverClient = parseClient(req.headers['user-agent'] || '')
|
||||
const location = locationFromHeaders(req)
|
||||
const shareUserAgent = payload.user_agent !== 'Not shared'
|
||||
const event = {
|
||||
visitor_id: sanitizeText(payload.visitor_id, 80) || crypto.randomUUID(),
|
||||
@@ -540,7 +573,11 @@ function recordViewerEvent(req, payload) {
|
||||
screen: sanitizeText(payload.screen, 40),
|
||||
language: sanitizeText(payload.language, 40),
|
||||
timezone: sanitizeText(payload.timezone, 80),
|
||||
country: countryFromHeaders(req),
|
||||
country: location.country,
|
||||
region: location.region,
|
||||
city: location.city,
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
consent: payload.consent === 'analytics' ? 'analytics' : '',
|
||||
metadata: JSON.stringify(analyticsMetadata(req, payload)),
|
||||
}
|
||||
@@ -553,19 +590,23 @@ function recordViewerEvent(req, payload) {
|
||||
db.prepare(`
|
||||
insert into viewer_events
|
||||
(occurred_at, visitor_id, session_id, ip_hash, event_type, page_path, page_title,
|
||||
referrer, user_agent, browser, os, device, screen, language, timezone, country, consent, metadata)
|
||||
referrer, user_agent, browser, os, device, screen, language, timezone,
|
||||
country, region, city, latitude, longitude, consent, metadata)
|
||||
values
|
||||
(@occurred_at, @visitor_id, @session_id, @ip_hash, @event_type, @page_path, @page_title,
|
||||
@referrer, @user_agent, @browser, @os, @device, @screen, @language, @timezone, @country, @consent, @metadata)
|
||||
@referrer, @user_agent, @browser, @os, @device, @screen, @language, @timezone,
|
||||
@country, @region, @city, @latitude, @longitude, @consent, @metadata)
|
||||
`).run({ ...event, occurred_at: now })
|
||||
|
||||
db.prepare(`
|
||||
insert into active_viewers
|
||||
(session_id, visitor_id, ip_hash, first_seen_at, last_seen_at, page_path, page_title,
|
||||
referrer, user_agent, browser, os, device, screen, language, timezone, country)
|
||||
referrer, user_agent, browser, os, device, screen, language, timezone,
|
||||
country, region, city, latitude, longitude)
|
||||
values
|
||||
(@session_id, @visitor_id, @ip_hash, @now, @now, @page_path, @page_title,
|
||||
@referrer, @user_agent, @browser, @os, @device, @screen, @language, @timezone, @country)
|
||||
@referrer, @user_agent, @browser, @os, @device, @screen, @language, @timezone,
|
||||
@country, @region, @city, @latitude, @longitude)
|
||||
on conflict(session_id) do update set
|
||||
last_seen_at = excluded.last_seen_at,
|
||||
page_path = excluded.page_path,
|
||||
@@ -578,7 +619,11 @@ function recordViewerEvent(req, payload) {
|
||||
screen = excluded.screen,
|
||||
language = excluded.language,
|
||||
timezone = excluded.timezone,
|
||||
country = excluded.country
|
||||
country = excluded.country,
|
||||
region = excluded.region,
|
||||
city = excluded.city,
|
||||
latitude = excluded.latitude,
|
||||
longitude = excluded.longitude
|
||||
`).run({ ...event, now })
|
||||
}
|
||||
|
||||
@@ -620,7 +665,8 @@ function viewerDashboard() {
|
||||
const thirtyDaysSince = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
|
||||
const active = db.prepare(`
|
||||
select session_id, visitor_id, first_seen_at, last_seen_at, page_path, page_title,
|
||||
referrer, browser, os, device, screen, language, timezone, country
|
||||
referrer, browser, os, device, screen, language, timezone,
|
||||
country, region, city, latitude, longitude
|
||||
from active_viewers
|
||||
where last_seen_at >= ?
|
||||
order by last_seen_at desc
|
||||
@@ -640,6 +686,10 @@ function viewerDashboard() {
|
||||
language: row.language,
|
||||
timezone: row.timezone,
|
||||
country: row.country,
|
||||
region: row.region,
|
||||
city: row.city,
|
||||
latitude: row.latitude,
|
||||
longitude: row.longitude,
|
||||
}))
|
||||
|
||||
const activePageMap = new Map()
|
||||
@@ -728,21 +778,38 @@ function viewerDashboard() {
|
||||
`).all(thirtyDaysSince)
|
||||
|
||||
const countries = db.prepare(`
|
||||
select country, count(*) as events, count(distinct visitor_id) as visitors
|
||||
select
|
||||
country,
|
||||
avg(latitude) as latitude,
|
||||
avg(longitude) as longitude,
|
||||
count(*) as events,
|
||||
count(distinct visitor_id) as visitors
|
||||
from viewer_events
|
||||
where occurred_at >= ?
|
||||
and country != ''
|
||||
and latitude is not null
|
||||
and longitude is not null
|
||||
group by country
|
||||
order by visitors desc, events desc
|
||||
limit 80
|
||||
`).all(thirtyDaysSince)
|
||||
|
||||
const locations = db.prepare(`
|
||||
select country, timezone, language, count(*) as events, count(distinct visitor_id) as visitors
|
||||
select
|
||||
country,
|
||||
region,
|
||||
city,
|
||||
latitude,
|
||||
longitude,
|
||||
timezone,
|
||||
language,
|
||||
count(*) as events,
|
||||
count(distinct visitor_id) as visitors
|
||||
from viewer_events
|
||||
where occurred_at >= ?
|
||||
and (country != '' or (timezone != '' and timezone != 'Not shared'))
|
||||
group by country, timezone, language
|
||||
and latitude is not null
|
||||
and longitude is not null
|
||||
group by country, region, city, latitude, longitude, timezone, language
|
||||
order by visitors desc, events desc
|
||||
limit 32
|
||||
`).all(thirtyDaysSince)
|
||||
@@ -791,7 +858,7 @@ function viewerDashboard() {
|
||||
{ key: 'page', label: 'Page activity', detail: 'Page path, title, page views, and heartbeat state' },
|
||||
{ key: 'browser', label: 'Browser and device', detail: 'Browser, operating system, broad device type, and user-agent only when allowed' },
|
||||
{ key: 'display', label: 'Display', detail: 'Screen size, viewport size, pixel ratio, and colour depth when allowed' },
|
||||
{ key: 'locale', label: 'Language and coarse location', detail: 'Browser language and timezone when allowed, plus country code when supplied by the hosting edge' },
|
||||
{ key: 'locale', label: 'Language and coarse location', detail: 'Browser language and timezone when allowed, plus Cloudflare country/city coordinates when supplied by the edge' },
|
||||
{ key: 'referrer', label: 'Referrer', detail: 'The referring page when allowed and provided by the browser' },
|
||||
{ key: 'diagnostics', label: 'Diagnostics', detail: 'Privacy signals, network hints, request headers, and browser capability details when allowed' },
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user