251 lines
8.0 KiB
JavaScript
251 lines
8.0 KiB
JavaScript
|
|
// Rotas MDM para cliente Windows buscar e executar comandos
|
||
|
|
const express = require('express');
|
||
|
|
const router = express.Router();
|
||
|
|
const { Pool } = require('pg');
|
||
|
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||
|
|
|
||
|
|
// Cliente busca comandos pendentes
|
||
|
|
router.post('/commands/poll', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { device_id } = req.body;
|
||
|
|
|
||
|
|
if (!device_id) {
|
||
|
|
return res.status(400).json({ success: false, message: 'device_id é obrigatório' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Buscar comandos pendentes ordenados por prioridade
|
||
|
|
const result = await pool.query(`
|
||
|
|
SELECT
|
||
|
|
pc.*,
|
||
|
|
p.name as policy_name,
|
||
|
|
p.description as policy_description
|
||
|
|
FROM policy_commands pc
|
||
|
|
INNER JOIN policies p ON pc.policy_id = p.id
|
||
|
|
WHERE pc.device_id = $1
|
||
|
|
AND pc.status IN ('pending', 'sent')
|
||
|
|
AND pc.retry_count < pc.max_retries
|
||
|
|
ORDER BY pc.priority DESC, pc.created_at ASC
|
||
|
|
LIMIT 10
|
||
|
|
`, [device_id]);
|
||
|
|
|
||
|
|
if (result.rows.length > 0) {
|
||
|
|
// Marcar comandos como 'sent'
|
||
|
|
const commandIds = result.rows.map(r => r.id);
|
||
|
|
await pool.query(`
|
||
|
|
UPDATE policy_commands
|
||
|
|
SET status = 'sent', sent_at = NOW()
|
||
|
|
WHERE id = ANY($1) AND status = 'pending'
|
||
|
|
`, [commandIds]);
|
||
|
|
|
||
|
|
console.log(`📤 Enviando ${result.rows.length} comando(s) para ${device_id}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
commands: result.rows.map(cmd => ({
|
||
|
|
command_id: cmd.id,
|
||
|
|
policy_id: cmd.policy_id,
|
||
|
|
policy_name: cmd.policy_name,
|
||
|
|
policy_description: cmd.policy_description,
|
||
|
|
command_type: cmd.command_type,
|
||
|
|
command_data: cmd.command_data,
|
||
|
|
priority: cmd.priority
|
||
|
|
}))
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao buscar comandos:', error);
|
||
|
|
res.status(500).json({ success: false, message: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Cliente reporta início de execução
|
||
|
|
router.post('/commands/:id/start', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
|
||
|
|
await pool.query(`
|
||
|
|
UPDATE policy_commands
|
||
|
|
SET status = 'executing'
|
||
|
|
WHERE id = $1
|
||
|
|
`, [id]);
|
||
|
|
|
||
|
|
res.json({ success: true, message: 'Execução iniciada' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao iniciar execução:', error);
|
||
|
|
res.status(500).json({ success: false, message: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Cliente reporta resultado da execução
|
||
|
|
router.post('/commands/:id/result', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const {
|
||
|
|
status, // 'success' ou 'failed'
|
||
|
|
result,
|
||
|
|
error_message,
|
||
|
|
duration_seconds
|
||
|
|
} = req.body;
|
||
|
|
|
||
|
|
// Buscar informações do comando
|
||
|
|
const commandResult = await pool.query(`
|
||
|
|
SELECT device_id, policy_id
|
||
|
|
FROM policy_commands
|
||
|
|
WHERE id = $1
|
||
|
|
`, [id]);
|
||
|
|
|
||
|
|
if (commandResult.rows.length === 0) {
|
||
|
|
return res.status(404).json({ success: false, message: 'Comando não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const { device_id, policy_id } = commandResult.rows[0];
|
||
|
|
|
||
|
|
// Atualizar comando
|
||
|
|
if (status === 'success') {
|
||
|
|
await pool.query(`
|
||
|
|
UPDATE policy_commands
|
||
|
|
SET status = $1,
|
||
|
|
completed_at = NOW(),
|
||
|
|
result = $2
|
||
|
|
WHERE id = $3
|
||
|
|
`, [status, JSON.stringify(result), id]);
|
||
|
|
} else {
|
||
|
|
// Se falhou, incrementar retry_count
|
||
|
|
await pool.query(`
|
||
|
|
UPDATE policy_commands
|
||
|
|
SET status = $1,
|
||
|
|
completed_at = NOW(),
|
||
|
|
result = $2,
|
||
|
|
error_message = $3,
|
||
|
|
retry_count = retry_count + 1
|
||
|
|
WHERE id = $4
|
||
|
|
`, [status, JSON.stringify(result), error_message, id]);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Registrar no histórico
|
||
|
|
await pool.query(`
|
||
|
|
INSERT INTO policy_executions (
|
||
|
|
device_id,
|
||
|
|
policy_id,
|
||
|
|
command_id,
|
||
|
|
status,
|
||
|
|
duration_seconds,
|
||
|
|
result,
|
||
|
|
error_message
|
||
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||
|
|
`, [
|
||
|
|
device_id,
|
||
|
|
policy_id,
|
||
|
|
id,
|
||
|
|
status,
|
||
|
|
duration_seconds,
|
||
|
|
JSON.stringify(result),
|
||
|
|
error_message
|
||
|
|
]);
|
||
|
|
|
||
|
|
console.log(`${status === 'success' ? '✅' : '❌'} Comando ${id} executado: ${status}`);
|
||
|
|
res.json({ success: true, message: 'Resultado registrado' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao registrar resultado:', error);
|
||
|
|
res.status(500).json({ success: false, message: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Buscar políticas aplicadas ao dispositivo
|
||
|
|
router.get('/policies/:device_id', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { device_id } = req.params;
|
||
|
|
|
||
|
|
const result = await pool.query(`
|
||
|
|
SELECT
|
||
|
|
p.*,
|
||
|
|
dp.assigned_at,
|
||
|
|
pe.last_execution,
|
||
|
|
pe.last_status
|
||
|
|
FROM policies p
|
||
|
|
INNER JOIN device_policies dp ON p.id = dp.policy_id
|
||
|
|
LEFT JOIN LATERAL (
|
||
|
|
SELECT
|
||
|
|
executed_at as last_execution,
|
||
|
|
status as last_status
|
||
|
|
FROM policy_executions
|
||
|
|
WHERE device_id = $1 AND policy_id = p.id
|
||
|
|
ORDER BY executed_at DESC
|
||
|
|
LIMIT 1
|
||
|
|
) pe ON true
|
||
|
|
WHERE dp.device_id = $1 AND p.enabled = true
|
||
|
|
ORDER BY p.priority DESC, p.name
|
||
|
|
`, [device_id]);
|
||
|
|
|
||
|
|
res.json({ success: true, policies: result.rows });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao buscar políticas:', error);
|
||
|
|
res.status(500).json({ success: false, message: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Listar templates de políticas disponíveis
|
||
|
|
router.get('/templates', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { category } = req.query;
|
||
|
|
|
||
|
|
let query = 'SELECT * FROM policy_templates';
|
||
|
|
const params = [];
|
||
|
|
|
||
|
|
if (category) {
|
||
|
|
query += ' WHERE category = $1';
|
||
|
|
params.push(category);
|
||
|
|
}
|
||
|
|
|
||
|
|
query += ' ORDER BY category, name';
|
||
|
|
|
||
|
|
const result = await pool.query(query, params);
|
||
|
|
|
||
|
|
res.json({ success: true, templates: result.rows });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao buscar templates:', error);
|
||
|
|
res.status(500).json({ success: false, message: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Estatísticas de comandos por dispositivo
|
||
|
|
router.get('/stats/:device_id', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { device_id } = req.params;
|
||
|
|
|
||
|
|
const stats = await pool.query(`
|
||
|
|
SELECT
|
||
|
|
COUNT(*) FILTER (WHERE status = 'pending') as pending,
|
||
|
|
COUNT(*) FILTER (WHERE status = 'sent') as sent,
|
||
|
|
COUNT(*) FILTER (WHERE status = 'executing') as executing,
|
||
|
|
COUNT(*) FILTER (WHERE status = 'success') as success,
|
||
|
|
COUNT(*) FILTER (WHERE status = 'failed') as failed
|
||
|
|
FROM policy_commands
|
||
|
|
WHERE device_id = $1
|
||
|
|
AND created_at > NOW() - INTERVAL '7 days'
|
||
|
|
`, [device_id]);
|
||
|
|
|
||
|
|
const recentExecutions = await pool.query(`
|
||
|
|
SELECT
|
||
|
|
status,
|
||
|
|
COUNT(*) as count
|
||
|
|
FROM policy_executions
|
||
|
|
WHERE device_id = $1
|
||
|
|
AND executed_at > NOW() - INTERVAL '30 days'
|
||
|
|
GROUP BY status
|
||
|
|
`, [device_id]);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
commands: stats.rows[0],
|
||
|
|
recent_executions: recentExecutions.rows
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao buscar estatísticas:', error);
|
||
|
|
res.status(500).json({ success: false, message: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
module.exports = router;
|
||
|
|
|