← back to Rentv 826 Tracker
report.js
69 lines
'use strict';
// Time-on-server report for the /826/ client area. Usage: node report.js [client_slug]
const path = require('path');
const fs = require('fs');
const { Pool } = require('pg');
(function loadEnv() {
const p = path.join(__dirname, '.env');
if (!fs.existsSync(p)) return;
for (const raw of fs.readFileSync(p, 'utf8').split('\n')) {
const line = raw.trim();
if (!line || line.startsWith('#')) continue;
const i = line.indexOf('='); if (i === -1) continue;
const k = line.slice(0, i).trim();
if (!(k in process.env)) process.env[k] = line.slice(i + 1).trim();
}
})();
const CLIENT = process.argv[2] || process.env.CLIENT_SLUG || 'boomer';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
function fmt(sec) {
sec = Math.round(sec || 0);
const h = Math.floor(sec / 3600), m = Math.floor((sec % 3600) / 60), s = sec % 60;
return `${h}h ${String(m).padStart(2, '0')}m ${String(s).padStart(2, '0')}s`;
}
(async () => {
const c = (await pool.query(`SELECT name, email FROM clients WHERE slug=$1`, [CLIENT])).rows[0] || { name: CLIENT };
const tot = (await pool.query(
`SELECT count(*) sessions, coalesce(sum(duration_seconds),0) secs, coalesce(sum(hit_count),0) hits,
min(started_at) first_seen, max(last_seen_at) last_seen
FROM sessions WHERE client_slug=$1`, [CLIENT])).rows[0];
console.log(`\n=== Time on server — ${c.name}${c.email ? ' <' + c.email + '>' : ''} (/826/) ===`);
console.log(`Total time on server : ${fmt(tot.secs)} (${tot.sessions} sessions, ${tot.hits} page hits)`);
console.log(`First seen : ${tot.first_seen ? new Date(tot.first_seen).toISOString() : '—'}`);
console.log(`Last seen : ${tot.last_seen ? new Date(tot.last_seen).toISOString() : '—'}`);
const byUser = (await pool.query(
`SELECT coalesce(remote_user,'(anon)') u, count(*) n, coalesce(sum(duration_seconds),0) secs
FROM sessions WHERE client_slug=$1 GROUP BY 1 ORDER BY secs DESC`, [CLIENT])).rows;
if (byUser.length) {
console.log(`\nBy login:`);
for (const r of byUser) console.log(` ${r.u.padEnd(14)} ${fmt(r.secs).padEnd(16)} ${r.n} sessions`);
}
const byDay = (await pool.query(
`SELECT to_char(date_trunc('day', started_at),'YYYY-MM-DD') d, count(*) n, coalesce(sum(duration_seconds),0) secs
FROM sessions WHERE client_slug=$1 GROUP BY 1 ORDER BY 1 DESC LIMIT 14`, [CLIENT])).rows;
if (byDay.length) {
console.log(`\nLast ${byDay.length} active days:`);
for (const r of byDay) console.log(` ${r.d} ${fmt(r.secs).padEnd(16)} ${r.n} sessions`);
}
const recent = (await pool.query(
`SELECT started_at, last_seen_at, duration_seconds, hit_count, remote_user, host(ip) ip
FROM sessions WHERE client_slug=$1 ORDER BY started_at DESC LIMIT 10`, [CLIENT])).rows;
if (recent.length) {
console.log(`\nMost recent sessions:`);
for (const r of recent) {
console.log(` ${new Date(r.started_at).toISOString()} ${fmt(r.duration_seconds).padEnd(16)} ${String(r.hit_count).padStart(3)} hits ${r.remote_user || '(anon)'} ${r.ip}`);
}
}
console.log('');
await pool.end();
})().catch((e) => { console.error(e.message); process.exit(1); });