import vine from '@vinejs/vine' import type { HttpContext } from '@adonisjs/core/http' import dockerService from '#services/docker_service' import { audit } from '#services/audit' import { openSse } from '#services/sse' const ACTION_LABELS = { start: 'Démarrage du conteneur', stop: 'Arrêt du conteneur', restart: 'Redémarrage du conteneur', } as const const actionValidator = vine.create({ action: vine.enum(['start', 'stop', 'restart'] as const), }) const logsValidator = vine.create({ tail: vine.number().withoutDecimals().range([0, 5000]).optional(), }) export default class ContainersController { async index() { return dockerService.list() } async show({ params }: HttpContext) { return dockerService.inspect(params.id) } async action(ctx: HttpContext) { const { action } = await actionValidator.validate(ctx.params) const info = await dockerService.inspect(ctx.params.id) try { await dockerService.action(info.id, action) } catch (error) { await audit(ctx, { action: ACTION_LABELS[action], target: info.name, status: 'error', output: error instanceof Error ? error.message : String(error), }) throw error } await audit(ctx, { action: ACTION_LABELS[action], target: info.name, status: 'success' }) return dockerService.inspect(info.id) } /** * Follows the logs of the container (Server-Sent Events). */ async logs(ctx: HttpContext) { const { tail } = await logsValidator.validate(ctx.request.qs()) const channel = openSse(ctx) try { const stop = await dockerService.followLogs( ctx.params.id, { tail: tail ?? 300 }, (line) => channel.send('line', line), () => { channel.send('end', {}) channel.close() } ) channel.onClose(stop) } catch (error) { channel.send('failure', { message: error instanceof Error ? error.message : String(error) }) channel.close() } } }