Files
linumiq_net-web_app/lib/admin/metrics.ts
T

84 lines
2.1 KiB
TypeScript

import { getSupabaseAdmin } from '@/lib/supabase/admin';
import { withAdminRetry } from '@/lib/admin/retry';
export type AdminMetrics = {
totalUsers: number;
totalTunnels: number;
activeTunnels: number;
inactiveTunnels: number;
overQuota: number;
bytesUsedTotal: number;
quotaTotal: number;
signups7d: number;
signups30d: number;
recentlyActive: number;
};
type TunnelAgg = {
is_active: boolean;
bytes_used: number;
quota_bytes: number;
last_seen_at: string | null;
};
export async function computeMetrics(): Promise<AdminMetrics> {
const admin = getSupabaseAdmin();
const { data: tunnelsData } = await admin
.from('tunnels')
.select('is_active, bytes_used, quota_bytes, last_seen_at');
const tunnels = (tunnelsData ?? []) as TunnelAgg[];
const now = Date.now();
const day = 24 * 60 * 60 * 1000;
let activeTunnels = 0;
let inactiveTunnels = 0;
let overQuota = 0;
let bytesUsedTotal = 0;
let quotaTotal = 0;
let recentlyActive = 0;
for (const t of tunnels) {
if (t.is_active) activeTunnels++;
else inactiveTunnels++;
if (t.quota_bytes > 0 && t.bytes_used >= t.quota_bytes) overQuota++;
bytesUsedTotal += Number(t.bytes_used) || 0;
quotaTotal += Number(t.quota_bytes) || 0;
if (t.last_seen_at && now - new Date(t.last_seen_at).getTime() <= day) {
recentlyActive++;
}
}
let totalUsers = 0;
let signups7d = 0;
let signups30d = 0;
const perPage = 1000;
for (let page = 1; page <= 50; page++) {
const { data, error } = await withAdminRetry(() =>
admin.auth.admin.listUsers({ page, perPage }),
);
if (error) break;
const users = data.users;
if (users.length === 0) break;
totalUsers += users.length;
for (const u of users) {
const created = new Date(u.created_at).getTime();
if (now - created <= 7 * day) signups7d++;
if (now - created <= 30 * day) signups30d++;
}
if (users.length < perPage) break;
}
return {
totalUsers,
totalTunnels: tunnels.length,
activeTunnels,
inactiveTunnels,
overQuota,
bytesUsedTotal,
quotaTotal,
signups7d,
signups30d,
recentlyActive,
};
}