Analytics overhaul: privacy fixes, All Time range, readability improvements; v2.0.29
Publish Container / docker (push) Successful in 14m17s
Publish Container / docker (push) Successful in 14m17s
- Remove raw IP storage; store only SHA-256 hash prefix in visitor records - Non-blocking geo lookup: recordVisitor now synchronous, geo resolved in background - UTM capture race condition fixed via sessionStorage pre-consent capture - Add consent + bot checks to play endpoint (was missing) - Add rate limiter to pageview endpoint (60 req/min per IP) - Add 'All time' range option alongside 7d/30d/90d with trendData aggregation - AnalyticsPanel: tabbed layout (Traffic/Engagement/System), dynamic chart titles, range buttons with active state, visitor log uses ipHash instead of raw IP, trendData drives main chart with weekly bucketing for all-time view - Re-add prop-types dependency (required by react-simple-maps bundler) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
da5c1c5078
commit
4002f1ee93
6 files changed
+333
-231
No files matched your search
+4
-4
@@ -546,7 +546,7 @@ export interface AdminStats {
|
||||
recentVisits: Array<{
|
||||
at: string
|
||||
visitorId: string
|
||||
ip: string
|
||||
ipHash?: string
|
||||
path: string
|
||||
referrer?: string
|
||||
device?: string
|
||||
@@ -1531,7 +1531,7 @@ export default function AdminPage({ content, onSave, onLogout }: Props) {
|
||||
const [navOpenGroups, setNavOpenGroups] = useState<Set<string>>(new Set())
|
||||
const [stats, setStats] = useState<AdminStats | null>(null)
|
||||
const [statsStatus, setStatsStatus] = useState<'loading' | 'ready' | 'error'>('loading')
|
||||
const [statsRange, setStatsRange] = useState<'7d' | '30d' | '90d'>('30d')
|
||||
const [statsRange, setStatsRange] = useState<'7d' | '30d' | '90d' | 'all'>('30d')
|
||||
const [maintenanceMsg, setMaintenanceMsg] = useState('')
|
||||
const [opsMsg, setOpsMsg] = useState('')
|
||||
const [backupFiles, setBackupFiles] = useState<BackupPreview[]>([])
|
||||
@@ -1883,7 +1883,7 @@ const [dashboardNow, setDashboardNow] = useState(() => new Date())
|
||||
})
|
||||
}, [selectedBackup])
|
||||
|
||||
async function reloadStats(range?: '7d' | '30d' | '90d') {
|
||||
async function reloadStats(range?: '7d' | '30d' | '90d' | 'all') {
|
||||
const r = await fetch(`/api/admin-stats?range=${range ?? statsRange}`)
|
||||
if (!r.ok) throw new Error('Could not refresh stats')
|
||||
const data = await r.json()
|
||||
@@ -5592,7 +5592,7 @@ async function handleAssetUpload(event: ChangeEvent<HTMLInputElement>) {
|
||||
stats={stats}
|
||||
statsStatus={statsStatus}
|
||||
statsRange={statsRange}
|
||||
onRangeChange={async (range) => {
|
||||
onRangeChange={async (range: '7d' | '30d' | '90d' | 'all') => {
|
||||
setStatsRange(range)
|
||||
setStatsStatus('loading')
|
||||
try { await reloadStats(range) } catch { setStatsStatus('error') }
|
||||
|
||||
Reference in new issue
Block a user