Files
2026-05-10 21:23:42 +02:00

63 lines
2.3 KiB
TypeScript

import { Router } from 'express'
import axios from 'axios'
const router = Router()
router.get('/stats', async (_req, res) => {
try {
const host = process.env.AUTHENTIK_HOST
const token = process.env.AUTHENTIK_TOKEN
if (!host || !token) {
res.status(503).json({ error: 'AUTHENTIK_HOST / AUTHENTIK_TOKEN not configured' })
return
}
const headers = { Authorization: `Bearer ${token.trim()}` }
const since24h = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
const [usersRes, loginsRes, failedRes, failedCountRes] = await Promise.all([
axios.get(`${host}/api/v3/core/users/`, { headers, params: { page_size: 1, attributes: '' } }),
axios.get(`${host}/api/v3/events/events/`, { headers, params: { action: 'login', ordering: '-created', page_size: 5 } }),
axios.get(`${host}/api/v3/events/events/`, { headers, params: { action: 'login_failed', ordering: '-created', page_size: 5 } }),
axios.get(`${host}/api/v3/events/events/`, { headers, params: { action: 'login_failed', created__gte: since24h, page_size: 1 } }),
])
type AuthentikEvent = {
created: string
user: { username: string }
client_ip: string
}
const toEntry = (e: AuthentikEvent, success: boolean) => ({
username: e.user?.username ?? 'unknown',
created: e.created,
clientIp: e.client_ip ?? '',
success,
})
const combined = [
...(loginsRes.data.results ?? []).map((e: AuthentikEvent) => toEntry(e, true)),
...(failedRes.data.results ?? []).map((e: AuthentikEvent) => toEntry(e, false)),
]
.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime())
.reduce<ReturnType<typeof toEntry>[]>((acc, entry) => {
const dupe = acc.find(x => x.created === entry.created && x.username === entry.username)
if (dupe) { if (!entry.success) dupe.success = false }
else acc.push(entry)
return acc
}, [])
.slice(0, 5)
res.json({
userCount: usersRes.data.pagination?.count ?? 0,
failedLast24h: failedCountRes.data.pagination?.count ?? 0,
recentLogins: combined,
})
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Unknown error'
res.status(500).json({ error: msg })
}
})
export default router