feat(monitor): integração Beszel/Stack Observer, tickets do observer e novos sites no uptime

This commit is contained in:
2026-10-28 16:00:59 +00:00
parent 2bbc12e8b4
commit ca9a07b1d9
12 changed files with 1110 additions and 7 deletions
+107
View File
@@ -0,0 +1,107 @@
import { Router, Request, Response } from 'express'
import fs from 'fs'
import path from 'path'
import os from 'os'
const router = Router()
interface QueuedTicket {
evaluation_id: number
title: string
description: string
priority: 'low' | 'medium' | 'high'
created_at: string
}
/**
* POST /api/observer-tickets/process
* Processa a fila de tickets (~/.observer-ticket-queue.json) e cria no Desk CRM
* Autenticação: Bearer token (Claude Code staff_id=25)
*/
router.post('/process', async (req: Request, res: Response) => {
try {
const queueFile = path.join(os.homedir(), '.observer-ticket-queue.json')
// Verificar se arquivo existe
if (!fs.existsSync(queueFile)) {
return res.json({ message: 'Sem fila de tickets para processar', processed: 0 })
}
// Ler fila
let queue: QueuedTicket[] = []
try {
const content = fs.readFileSync(queueFile, 'utf8')
queue = JSON.parse(content)
} catch (e) {
return res.status(400).json({ error: 'Erro ao ler fila: ' + (e as Error).message })
}
if (!queue.length) {
return res.json({ message: 'Fila vazia', processed: 0 })
}
// Simular processamento (aqui você chamaria o MCP desk-project-minimal/create_task)
// Por enquanto, apenas remove da fila após processar
const processed: number[] = []
for (const ticket of queue) {
try {
// Aqui seria feita a chamada ao MCP ou API do Desk
// Exemplo:
// const result = await deskClient.createTask({
// title: ticket.title,
// description: ticket.description,
// project_id: 65,
// priority: ticket.priority === 'high' ? 3 : (ticket.priority === 'medium' ? 2 : 1),
// })
console.log(`✅ Ticket processado: ${ticket.title}`)
processed.push(ticket.evaluation_id)
} catch (e) {
console.error(`❌ Erro ao processar ticket: ${ticket.title}`, e)
}
}
// Remover tickets processados da fila
const remaining = queue.filter(t => !processed.includes(t.evaluation_id))
if (remaining.length === 0) {
// Se fila vazia, deletar arquivo
fs.unlinkSync(queueFile)
} else {
// Gravar fila com tickets restantes
fs.writeFileSync(queueFile, JSON.stringify(remaining, null, 2))
}
res.json({
message: 'Fila processada',
processed: processed.length,
remaining: remaining.length,
})
} catch (e) {
res.status(500).json({ error: 'Erro ao processar fila: ' + (e as Error).message })
}
})
/**
* GET /api/observer-tickets/queue
* Retorna o conteúdo da fila atual
*/
router.get('/queue', (req: Request, res: Response) => {
try {
const queueFile = path.join(os.homedir(), '.observer-ticket-queue.json')
if (!fs.existsSync(queueFile)) {
return res.json({ queue: [], count: 0 })
}
const content = fs.readFileSync(queueFile, 'utf8')
const queue = JSON.parse(content) as QueuedTicket[]
res.json({ queue, count: queue.length })
} catch (e) {
res.status(500).json({ error: 'Erro ao ler fila: ' + (e as Error).message })
}
})
export default router