syco.me Homelab Dashboard
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
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
|
||||
Reference in New Issue
Block a user