source: Klonkt/src/routes/admin-stats.js@ 1019a90

main
Last change on this file since 1019a90 was e3a5aed, checked in by roboburr <roboburr@…>, 2 months ago

feat(stats): let the admin exclude their own IP from statistics

Logged-in admins were already skipped, but an admin browsing logged out (incognito, another browser)
still inflated the numbers. Admin -> Statistics now shows your current IP with a one-click "Don't
count my visits" toggle, stored in a stats_exclude_ips app_setting and checked on every pageview
(recordPageview/recordPostView). Trust-proxy gives the real client IP; normalised for matching.

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

  • Property mode set to 100644
File size: 1.9 KB
Line 
1/**
2 * Admin: Statistics (premium module, god-only).
3 *
4 * GET /admin/stats -> cookie-free statistics: visitors/views per day,
5 * plays, and the most popular posts/tracks.
6 *
7 * Premium-gated via premiumUnlocked() (premium layer off = freely available;
8 * on = Patreon required). Tracking is in StatsService (no cookies).
9 */
10
11import express from 'express';
12import db from '../config/database.js';
13import { renderPage } from '../middleware/render.js';
14import { requireGod } from '../middleware/auth.js';
15import { premiumUnlocked } from '../services/PatreonService.js';
16import { getStats, currentIp, getExcludedIps, setExcludedIps } from '../services/StatsService.js';
17
18const router = express.Router();
19
20router.get('/', requireGod, (req, res) => {
21 if (!premiumUnlocked()) {
22 return res.status(403).send('Statistieken is een premium-functie — koppel Patreon in Beheer → Instellingen.');
23 }
24 // Link-in-bio clicks (premium #6) for the current site.
25 let linkClicks = [];
26 if (res.locals.site) {
27 try {
28 linkClicks = db.prepare(
29 'SELECT url, clicks FROM link_clicks WHERE site_id = ? AND clicks > 0 ORDER BY clicks DESC LIMIT 50'
30 ).all(res.locals.site.id);
31 } catch { linkClicks = []; }
32 }
33 const days = [7, 14, 30, 90].includes(parseInt(req.query.days, 10)) ? parseInt(req.query.days, 10) : 14;
34 const myIp = currentIp(req);
35 renderPage(req, res, 'pages/admin-stats', {
36 pageTitleKey: 'admin.t_stats',
37 bodyClass: 'on-admin',
38 stats: getStats(days),
39 linkClicks,
40 myIp,
41 ipExcluded: !!myIp && getExcludedIps().includes(myIp),
42 });
43});
44
45// Toggle whether the admin's current IP is counted in statistics.
46router.post('/exclude-ip', requireGod, (req, res) => {
47 const ip = currentIp(req);
48 if (ip) {
49 const list = getExcludedIps();
50 const i = list.indexOf(ip);
51 if (i >= 0) list.splice(i, 1); else list.push(ip);
52 setExcludedIps(list);
53 }
54 res.redirect('/admin/stats');
55});
56
57export default router;
Note: See TracBrowser for help on using the repository browser.