Changeset bb82e7f in Klonkt


Ignore:
Timestamp:
06/20/2026 12:29:15 PM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
bc83cc8
Parents:
92bad27
Message:

feat(stats): bot filter + clear labels (visitor-days ≠ unique visitors)

  • recordPageview/recordPostView now skip known bots/crawlers + empty-UA scripts (BOT_RE) → less inflated views/visitor-days.
  • Label "Bezoekers (Nd)" → "Visitor-days (Nd)" (it is the sum of daily uniques, not unique persons). Chart legend + tooltip updated accordingly. Expandable "What do these numbers mean?" explanation added.

Co-Authored-By: Claude <noreply@…>

Location:
src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/services/StatsService.js

    r92bad27 rbb82e7f  
    4545}
    4646
     47// Bekende bots/crawlers + link-preview-fetchers + scripts overslaan, zodat ze de
     48// weergaven/bezoeker-dagen niet opblazen. Geen UA = vrijwel altijd geautomatiseerd.
     49const BOT_RE = /bot|crawl|spider|slurp|mediapartners|bingpreview|facebookexternalhit|whatsapp|telegram|discord|twitter|linkedin|embedly|pinterest|redditbot|applebot|petalbot|yandex|baidu|duckduckbot|semrush|ahrefs|mj12|dotbot|uptimerobot|pingdom|statuscake|headless|lighthouse|gptbot|claude|ccbot|perplexity|bytespider|amazonbot|googleother|google-read-aloud|python-requests|scrapy|curl|wget|axios|node-fetch|go-http|java\/|okhttp|libwww|httpclient/i;
     50function isBot(req) {
     51  const ua = (req && req.headers && req.headers['user-agent']) || '';
     52  if (!ua) return true;          // lege UA = script/bot
     53  return BOT_RE.test(ua);
     54}
     55
    4756// Lazy prepares — tabellen bestaan pas ná initializeDatabase(); dit module wordt
    4857// geïmporteerd vóór die call.
     
    8190
    8291export function recordPageview(siteId, req) {
    83   if (!siteId || isOperator(req)) return;
     92  if (!siteId || isOperator(req) || isBot(req)) return;
    8493  try {
    8594    const d = today();
     
    91100
    92101export function recordPostView(post, req) {
    93   if (!post || !post.id || isOperator(req)) return;
     102  if (!post || !post.id || isOperator(req) || isBot(req)) return;
    94103  try {
    95104    stmts().bumpPost.run(post.id);
  • src/views/pages/admin-stats.ejs

    r92bad27 rbb82e7f  
    33  <h1>Statistieken</h1>
    44  <p class="stats-note">Cookievrij gemeten — geen tracking-cookies, geen toestemmingsbanner.
    5     Unieke bezoekers via een dagelijks roterende, anonieme hash (IP/browser worden niet bewaard).
    6     Je eigen beheerder-bezoeken tellen niet mee.</p>
     5    Bezoekers worden per dag geteld via een dagelijks roterende, anonieme hash (IP/browser worden
     6    niet bewaard). Je eigen beheerder-bezoeken en bekende bots/crawlers tellen niet mee.</p>
    77
    88  <% var s = stats; var _max = Math.max(1, ...s.series.map(function(d){ return d.pageviews; })); %>
    99
    1010  <div class="stats-cards">
    11     <div class="stat-card"><div class="stat-num"><%= s.totals.visitors %></div><div class="stat-label">Bezoekers (<%= s.days %>d)</div></div>
     11    <div class="stat-card"><div class="stat-num"><%= s.totals.visitors %></div><div class="stat-label">Bezoeker-dagen (<%= s.days %>d)</div></div>
    1212    <div class="stat-card"><div class="stat-num"><%= s.totals.pageviews %></div><div class="stat-label">Weergaven (<%= s.days %>d)</div></div>
    1313    <div class="stat-card"><div class="stat-num"><%= s.totals.plays %></div><div class="stat-label">Plays (totaal)</div></div>
     
    1818    <p class="stats-note">All-time: <strong><%= s.allTime.pageviews %></strong> weergaven · <strong><%= s.allTime.visitorDays %></strong> bezoeker-dagen.</p>
    1919  <% } %>
     20
     21  <details class="stats-help">
     22    <summary>Wat betekenen deze cijfers?</summary>
     23    <ul>
     24      <li><strong>Bezoeker-dagen</strong> — het aantal unieke bezoekers <em>per dag, bij elkaar opgeteld</em>. Eén persoon die 5 dagen langskomt = 5 bezoeker-dagen. Cookieloos kan er niet over dagen heen geteld worden, dus dit is géén aantal unieke personen — het echte aantal mensen ligt (vaak fors) lager.</li>
     25      <li><strong>Weergaven</strong> — hoe vaak de home/feed of een post geladen is (ook bij klikken binnen de site). Andere pagina's (agenda, downloads, links) tellen hier niet in mee.</li>
     26      <li><strong>Plays</strong> — totaal aantal keren dat een track is gestart.</li>
     27      <li><strong>Post-weergaven</strong> — totaal over alle posts samen.</li>
     28    </ul>
     29    <p>Beheerder-bezoeken en bekende bots/crawlers worden overgeslagen. Het ruwe IP wordt nooit bewaard. Goed voor trends; neem absolute aantallen met een korrel zout.</p>
     30  </details>
    2031
    2132  <div class="stats-period">
     
    3041      <h2>Laatste <%= s.days %> dagen</h2>
    3142      <div class="stats-legend">
    32         <span class="lg lg-vis">Bezoekers</span>
     43        <span class="lg lg-vis">Bezoeker-dagen</span>
    3344        <span class="lg lg-pv">Weergaven</span>
    3445      </div>
     
    4051           var visPct = Math.round(d.visitors / _max * 100);
    4152      %>
    42         <div class="sc-col" title="<%= d.day %> — <%= d.pageviews %> weergaven, <%= d.visitors %> bezoekers">
     53        <div class="sc-col" title="<%= d.day %> — <%= d.pageviews %> weergaven, <%= d.visitors %> bezoeker-dagen">
    4354          <div class="sc-bar">
    4455            <span class="sc-pv" style="height: <%= pvPct %>%"></span>
Note: See TracChangeset for help on using the changeset viewer.