import vine from '@vinejs/vine' import type { HttpContext } from '@adonisjs/core/http' import Site from '#models/site' import siteMonitor from '#services/site_monitor' import UserError from '#exceptions/user_error' const showValidator = vine.create({ range: vine.enum(['24h', '7d', '30d'] as const).optional(), }) const RANGES = { '24h': { seconds: 86_400, bucket: 300 }, '7d': { seconds: 7 * 86_400, bucket: 1800 }, '30d': { seconds: 30 * 86_400, bucket: 7200 }, } as const export default class SitesController { async index() { const [sites, day, week] = await Promise.all([ Site.query().orderBy('active', 'desc').orderBy('domain'), siteMonitor.stats(86_400), siteMonitor.stats(7 * 86_400), ]) return sites.map((site) => ({ ...serializeSite(site), uptime24h: day.get(site.id)?.uptime ?? null, uptime7d: week.get(site.id)?.uptime ?? null, avgResponseMs24h: day.get(site.id)?.avgResponseMs ?? null, })) } async show({ params, request }: HttpContext) { const { range = '24h' } = await showValidator.validate(request.qs()) const site = await Site.find(params.id) if (!site) throw new UserError('Site introuvable.', 404) const { seconds, bucket } = RANGES[range] const [history, stats] = await Promise.all([ siteMonitor.history(site.id, seconds, bucket), siteMonitor.stats(seconds), ]) return { site: serializeSite(site), range, bucket, uptime: stats.get(site.id)?.uptime ?? null, avgResponseMs: stats.get(site.id)?.avgResponseMs ?? null, ...history, } } /** * Runs the checks right away instead of waiting for the next minute. */ async check() { await siteMonitor.checkAll() return this.index() } } function serializeSite(site: Site) { return { id: site.id, domain: site.domain, active: site.active, kind: site.kind, upstream: site.upstream, lastOk: site.lastOk, lastStatus: site.lastStatus, lastResponseMs: site.lastResponseMs, lastError: site.lastError, lastCheckedAt: site.lastCheckedAt, certExpiresAt: site.certExpiresAt, } }