299 lines
10 KiB
PHP
299 lines
10 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . "/api/protecao.php";
|
|
require __DIR__ . "/api/db.php";
|
|
require_once __DIR__ . "/vendor/autoload.php";
|
|
|
|
use PhpMqtt\Client\MqttClient;
|
|
use PhpMqtt\Client\ConnectionSettings;
|
|
|
|
$mqttCfg = require __DIR__ . "/config.php";
|
|
|
|
function mqtt_publish(array $cfg, string $topic, array $payload): void
|
|
{
|
|
try {
|
|
$settings = (new ConnectionSettings())
|
|
->setUsername($cfg['username'])
|
|
->setPassword($cfg['password'])
|
|
->setUseTls(true)
|
|
->setTlsCertificateAuthorityFile('/etc/mosquitto/certs/chain.pem')
|
|
->setConnectTimeout(3);
|
|
|
|
$client = new MqttClient(
|
|
$cfg['host'], $cfg['port'],
|
|
'web-devices-' . getmypid()
|
|
);
|
|
|
|
$client->connect($settings, true);
|
|
$client->publish($topic, json_encode($payload), 1);
|
|
$client->disconnect();
|
|
|
|
} catch (Throwable $e) {
|
|
error_log("MQTT publish error: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
// =====================================================
|
|
// APROVAR — desbloqueia e manda REJOIN via MQTT
|
|
// =====================================================
|
|
|
|
if (isset($_GET['approve'])) {
|
|
|
|
$uid = $_GET['approve'];
|
|
|
|
$pdo->prepare("
|
|
UPDATE contadores
|
|
SET blocked=0
|
|
WHERE uid=?
|
|
")->execute([$uid]);
|
|
|
|
// REJOIN para o ESP fazer auth imediatamente
|
|
mqtt_publish($mqttCfg, "esp/$uid/cmd", ['cmd' => 'REJOIN']);
|
|
|
|
header("Location: devices.php");
|
|
exit;
|
|
}
|
|
|
|
// =====================================================
|
|
// REJEITAR / REMOVER
|
|
// =====================================================
|
|
|
|
if (isset($_GET['reject'])) {
|
|
|
|
$uid = $_GET['reject'];
|
|
|
|
$pdo->prepare("
|
|
DELETE FROM contadores
|
|
WHERE uid=?
|
|
")->execute([$uid]);
|
|
|
|
header("Location: devices.php");
|
|
exit;
|
|
}
|
|
|
|
// =====================================================
|
|
// BLOQUEAR / DESBLOQUEAR
|
|
// =====================================================
|
|
|
|
if (isset($_GET['toggle'])) {
|
|
|
|
$uid = $_GET['toggle'];
|
|
|
|
$pdo->prepare("
|
|
UPDATE contadores
|
|
SET blocked = 1 - blocked
|
|
WHERE uid=?
|
|
")->execute([$uid]);
|
|
|
|
header("Location: devices.php");
|
|
exit;
|
|
}
|
|
|
|
// =====================================================
|
|
// DESPROMOVER — volta a pendente
|
|
// =====================================================
|
|
|
|
if (isset($_GET['demote'])) {
|
|
|
|
$uid = $_GET['demote'];
|
|
|
|
$pdo->prepare("
|
|
UPDATE contadores
|
|
SET blocked=1, auth_ok=0
|
|
WHERE uid=?
|
|
")->execute([$uid]);
|
|
|
|
header("Location: devices.php");
|
|
exit;
|
|
}
|
|
|
|
// =====================================================
|
|
// QUERY
|
|
// =====================================================
|
|
|
|
$pending = $pdo->query("
|
|
SELECT uid, note, last_seen, blocked, auth_ok,
|
|
TIMESTAMPDIFF(SECOND, last_seen, NOW()) AS ago
|
|
FROM contadores
|
|
WHERE blocked=1 AND auth_ok=0
|
|
ORDER BY last_seen DESC
|
|
")->fetchAll();
|
|
|
|
$approved = $pdo->query("
|
|
SELECT uid, note, last_seen, online, blocked, auth_ok, current_broker,
|
|
COALESCE(brightness, 128) AS brightness,
|
|
TIMESTAMPDIFF(SECOND, last_seen, NOW()) AS ago
|
|
FROM contadores
|
|
WHERE blocked=0 OR auth_ok=1
|
|
ORDER BY online DESC, last_seen DESC
|
|
")->fetchAll();
|
|
|
|
?>
|
|
<!doctype html>
|
|
<html lang="pt" data-bs-theme="dark">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Dispositivos</title>
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
|
<style>
|
|
body { background:#0b0b0b; color:#e0e0e0; font-family:Arial,sans-serif; }
|
|
.card { background:#151515; border:1px solid #333; }
|
|
.header-title { color:#00ffcc; text-shadow:0 0 8px rgba(0,255,180,0.4); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-fluid py-4">
|
|
|
|
<h1 class="mb-4 header-title">🔌 Gestão de Dispositivos</h1>
|
|
|
|
<!-- PENDENTES -->
|
|
<h4 class="text-warning mb-3">⏳ Pendentes de Aprovação (<?= count($pending) ?>)</h4>
|
|
|
|
<?php if (empty($pending)): ?>
|
|
<div class="alert alert-secondary mb-4">Nenhum dispositivo pendente.</div>
|
|
<?php else: ?>
|
|
<div class="card shadow-sm mb-5">
|
|
<div class="card-body">
|
|
<table class="table table-dark table-hover align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>UID</th>
|
|
<th>NOTA</th>
|
|
<th>ÚLTIMO HELLO</th>
|
|
<th>AÇÕES</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($pending as $r): ?>
|
|
<?php
|
|
$ago = (int)$r['ago'];
|
|
$lastSeen = $ago < 60 ? "{$ago}s" : round($ago/60) . "m";
|
|
?>
|
|
<tr>
|
|
<td class="fw-bold text-warning"><?= htmlspecialchars($r['uid']) ?></td>
|
|
<td><?= htmlspecialchars($r['note'] ?? '') ?></td>
|
|
<td><?= $lastSeen ?></td>
|
|
<td>
|
|
<div class="d-flex gap-2">
|
|
<a href="?approve=<?= urlencode($r['uid']) ?>"
|
|
class="btn btn-sm btn-success"
|
|
onclick="return confirm('Aprovar <?= htmlspecialchars($r['uid']) ?>?')">
|
|
✅ Aprovar
|
|
</a>
|
|
<a href="?reject=<?= urlencode($r['uid']) ?>"
|
|
class="btn btn-sm btn-danger"
|
|
onclick="return confirm('Rejeitar e apagar <?= htmlspecialchars($r['uid']) ?>?')">
|
|
❌ Rejeitar
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- APROVADOS -->
|
|
<h4 class="text-success mb-3">✅ Dispositivos Aprovados (<?= count($approved) ?>)</h4>
|
|
|
|
<div class="card shadow-sm">
|
|
<div class="card-body">
|
|
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>UID</th>
|
|
<th>NOTA</th>
|
|
<th>ESTADO</th>
|
|
<th>BROKER</th>
|
|
<th>ÚLTIMO</th>
|
|
<th>BRILHO</th>
|
|
<th>AÇÕES</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($approved as $r): ?>
|
|
<?php
|
|
$ago = (int)$r['ago'];
|
|
$lastSeen = $ago < 60 ? "{$ago}s" : round($ago/60) . "m";
|
|
$online = (int)$r['online'] === 1;
|
|
$blocked = (int)$r['blocked'] === 1;
|
|
|
|
if ($blocked) {
|
|
$state = '<span class="badge bg-danger">BLOQUEADO</span>';
|
|
} elseif ($online) {
|
|
$state = '<span class="badge bg-success">ONLINE</span>';
|
|
} else {
|
|
$state = '<span class="badge bg-secondary">OFFLINE</span>';
|
|
}
|
|
|
|
$broker = ($r['current_broker'] ?? '') === 'recovery'
|
|
? '<span class="badge bg-warning text-dark">RECOVERY</span>'
|
|
: '<span class="badge bg-primary">PRIMARY</span>';
|
|
?>
|
|
<?php $bri = (int)($r['brightness'] ?? 128); ?>
|
|
<tr>
|
|
<td class="fw-bold"><?= htmlspecialchars($r['uid']) ?></td>
|
|
<td><?= htmlspecialchars($r['note'] ?? '') ?></td>
|
|
<td><?= $state ?></td>
|
|
<td><?= $broker ?></td>
|
|
<td><?= $lastSeen ?></td>
|
|
<td>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<input type="range" class="form-range" min="0" max="255"
|
|
value="<?= $bri ?>"
|
|
id="bri_<?= htmlspecialchars($r['uid']) ?>"
|
|
style="width:110px"
|
|
oninput="setBri('<?= htmlspecialchars($r['uid']) ?>',this.value)"
|
|
<?= $online ? '' : 'disabled' ?>>
|
|
<span id="brival_<?= htmlspecialchars($r['uid']) ?>"
|
|
class="text-muted" style="min-width:28px;font-size:.85em">
|
|
<?= $bri ?>
|
|
</span>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex gap-2">
|
|
<a href="?toggle=<?= urlencode($r['uid']) ?>"
|
|
class="btn btn-sm <?= $blocked ? 'btn-success' : 'btn-danger' ?>">
|
|
<?= $blocked ? 'Desbloquear' : 'Bloquear' ?>
|
|
</a>
|
|
<a href="?demote=<?= urlencode($r['uid']) ?>"
|
|
class="btn btn-sm btn-warning"
|
|
onclick="return confirm('Despromover para pendente?')">
|
|
↩ Pendente
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<a href="api/menu.php" class="btn btn-outline-secondary">← Menu</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
const briTimers = {};
|
|
function setBri(uid, val) {
|
|
document.getElementById('brival_' + uid).textContent = val;
|
|
clearTimeout(briTimers[uid]);
|
|
briTimers[uid] = setTimeout(() => {
|
|
const body = new URLSearchParams({ uid, value: val });
|
|
fetch('/api/set_brightness.php', { method: 'POST', body })
|
|
.then(r => r.json())
|
|
.then(d => { if (!d.ok) console.warn('brightness err', d); })
|
|
.catch(e => console.error(e));
|
|
}, 350);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|