Files
big-brother/api/app/controllers/overview_controller.ts
T
2026-10-12 14:25:22 +02:00

72 lines
2.3 KiB
TypeScript

import os from 'node:os'
import Site from '#models/site'
import AuditLog from '#models/audit_log'
import backupService from '#services/backup_service'
import dockerService from '#services/docker_service'
import metricsService from '#services/metrics_service'
const CERT_WARNING_DAYS = 14
export default class OverviewController {
async index() {
const now = Math.floor(Date.now() / 1000)
const [containers, sites, backups, recent, uptime] = await Promise.all([
dockerService.list().catch((error: Error) => error),
Site.query().where('active', true).orderBy('domain'),
backupService.list(),
AuditLog.query().orderBy('id', 'desc').limit(8),
metricsService.uptime(),
])
const docker =
containers instanceof Error
? { ok: false as const, error: containers.message }
: {
ok: true as const,
total: containers.length,
running: containers.filter((c) => c.state === 'running').length,
unhealthy: containers.filter((c) => c.health === 'unhealthy').map((c) => c.name),
stopped: containers.filter((c) => c.state !== 'running').map((c) => c.name),
}
return {
host: {
latest: metricsService.latest,
uptime,
cpus: os.cpus().length,
},
docker,
sites: {
total: sites.length,
up: sites.filter((site) => site.lastOk === true).length,
down: sites
.filter((site) => site.lastOk === false)
.map((site) => ({ id: site.id, domain: site.domain, error: site.lastError })),
certSoon: sites
.filter(
(site) => site.certExpiresAt && site.certExpiresAt - now < CERT_WARNING_DAYS * 86_400
)
.map((site) => ({
id: site.id,
domain: site.domain,
days: Math.floor((site.certExpiresAt! - now) / 86_400),
})),
},
backups: {
last: backups[0] ?? null,
count: backups.length,
schedule: backupService.schedule,
},
recent: recent.map((entry) => ({
id: entry.id,
action: entry.action,
target: entry.target,
status: entry.status,
username: entry.username,
createdAt: entry.createdAt.toISO(),
})),
}
}
}