2026-06-27 12:03:57 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
|
|
|
|
|
function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
|
|
|
|
|
|
|
|
|
|
|
define('VPS_API', 'https://web.xpto.ooguy.com/esp_api.php?key=esp_api_xupas_2025');
|
|
|
|
|
|
|
|
|
|
|
|
function vps_get(): array {
|
|
|
|
|
|
$ch = curl_init(VPS_API.'&action=list');
|
|
|
|
|
|
curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_TIMEOUT=>8]);
|
|
|
|
|
|
$r = json_decode(curl_exec($ch),true); curl_close($ch);
|
|
|
|
|
|
return $r['devices'] ?? [];
|
|
|
|
|
|
}
|
|
|
|
|
|
function vps_post(array $data): array {
|
|
|
|
|
|
$ch = curl_init(VPS_API);
|
|
|
|
|
|
curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_POST=>true,CURLOPT_POSTFIELDS=>http_build_query($data),CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_TIMEOUT=>8]);
|
|
|
|
|
|
$r = json_decode(curl_exec($ch),true); curl_close($ch);
|
|
|
|
|
|
return $r ?? [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$msg = ''; $msgOk = true;
|
|
|
|
|
|
|
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
|
|
|
|
$uid = preg_replace('/[^0-9A-Fa-f]/', '', $_POST['uid'] ?? '');
|
|
|
|
|
|
if (!$uid) { $msg = 'UID invalido.'; $msgOk = false; }
|
|
|
|
|
|
else {
|
|
|
|
|
|
$res = vps_post($_POST);
|
|
|
|
|
|
if (!empty($res['ok'])) { $msg = "[$uid] {$_POST['action']} OK"; }
|
|
|
|
|
|
else { $msg = $res['error'] ?? 'Erro'; $msgOk = false; }
|
|
|
|
|
|
}
|
|
|
|
|
|
$qs = $msg ? ($msgOk ? '?ok='.urlencode($msg) : '?err='.urlencode($msg)) : '';
|
|
|
|
|
|
header("Location: /esp_vps.php$qs"); exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isset($_GET['ok'])) { $msg = htmlspecialchars($_GET['ok']); $msgOk = true; }
|
|
|
|
|
|
if (isset($_GET['err'])) { $msg = htmlspecialchars($_GET['err']); $msgOk = false; }
|
|
|
|
|
|
|
|
|
|
|
|
$devices = vps_get(); usort($devices, function($a,$b){ return strcmp((string)($a['uid']??''),(string)($b['uid']??'')); });
|
|
|
|
|
|
$title = "ESP Devices — VPS";
|
|
|
|
|
|
ob_start();
|
|
|
|
|
|
?>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="page-header">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="breadcrumb"><a href="/painel.php">Painel</a><span class="sep">›</span>ESP Devices<span class="sep">›</span>VPS</div>
|
|
|
|
|
|
<h1>ESP Devices <span style="color:var(--muted);font-weight:400;font-size:16px">VPS broker</span></h1>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<a href="/esp_proxmox.php" class="btn btn-secondary">Ver Proxmox →</a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<?php if ($msg): ?>
|
|
|
|
|
|
<div class="alert <?= $msgOk ? 'alert-success' : 'alert-error' ?>"><?= $msgOk ? '✓' : '⚠' ?> <?= $msg ?></div>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
|
|
<?php if (!$devices): ?>
|
|
|
|
|
|
<div class="empty-state"><p>Sem dispositivos ou erro a ligar a VPS.</p></div>
|
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
|
<div class="card">
|
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
|
<h2>Dispositivos <span style="color:var(--muted);font-weight:400;font-size:13px">(<?= count($devices) ?>)</span></h2>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="table-wrap">
|
|
|
|
|
|
<table>
|
2026-06-27 21:47:24 +00:00
|
|
|
|
<thead><tr><th>UID</th><th>Versão</th><th>Online</th><th>Estado</th><th>Ultimo contacto</th><th>Brilho</th><th></th><th>Broker</th></tr></thead>
|
2026-06-27 12:03:57 +00:00
|
|
|
|
<tbody>
|
|
|
|
|
|
<?php foreach ($devices as $d):
|
|
|
|
|
|
$uid=$d['uid']; $online=(bool)$d['online']; $blocked=(bool)$d['blocked']; $auth_ok=(bool)$d['auth_ok'];
|
|
|
|
|
|
$last=$d['last_seen']??'-'; $bri=(int)($d['brightness']??128);
|
|
|
|
|
|
$diff=$last&&$last!=='-'?time()-strtotime($last):null;
|
|
|
|
|
|
// bolinha verde no botão do broker onde está ligado
|
|
|
|
|
|
$broker=(string)($d['current_broker']??'');
|
|
|
|
|
|
$isPmx=$online && strpos($broker,'remote')===0;
|
|
|
|
|
|
$isVps=$online && !$isPmx && $broker!=='';
|
|
|
|
|
|
$dot='<span style="display:inline-block;width:7px;height:7px;border-radius:50%;background:#3fb950;box-shadow:0 0 5px #3fb950;margin-right:4px;vertical-align:middle"></span>';
|
|
|
|
|
|
$ago=$diff===null?'-':($diff<60?$diff.'s':($diff<3600?round($diff/60).'min':round($diff/3600).'h')).' atras';
|
|
|
|
|
|
?>
|
|
|
|
|
|
<tr>
|
2026-06-27 21:47:24 +00:00
|
|
|
|
<td style="font-family:monospace;font-size:12px"><?= h($uid) ?></td><td style="font-size:12px;color:#8b949e"><?= ($d['fw']??'')!=='' ? h($d['fw']) : '—' ?><?php $ch=$d['chip']??''; echo $ch==='s3'?' <span class="badge" style="background:#0d1f3c;color:#58a6ff;font-size:10px">S3</span>':($ch==='wroom'?' <span class="badge" style="background:#1e2733;color:#8b949e;font-size:10px">WROOM</span>':''); ?></td>
|
2026-06-27 12:03:57 +00:00
|
|
|
|
<td><?php if($online):?><span class="badge" style="background:#0d2818;color:#3fb950">online</span><?php else:?><span class="badge" style="background:#1e2733;color:var(--muted)">offline</span><?php endif;?></td>
|
|
|
|
|
|
<td><?php if($blocked):?><span class="badge" style="background:#2d1a1a;color:#f85149">bloqueado</span><?php elseif($auth_ok):?><span class="badge" style="background:#0d2818;color:#3fb950">aprovado</span><?php else:?><span class="badge" style="background:#1e2733;color:var(--muted)">pendente</span><?php endif;?></td>
|
|
|
|
|
|
<td style="color:var(--muted);font-size:12px"><?= h($last) ?><br><?= $ago ?></td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<div style="display:flex;align-items:center;gap:6px">
|
|
|
|
|
|
<input type="range" min="0" max="255" value="<?= $bri ?>" id="bri_<?= h($uid) ?>" style="width:90px;accent-color:var(--accent)" oninput="setBri('<?= h($uid) ?>',this.value)" <?= $online?'':'disabled'?>>
|
|
|
|
|
|
<span id="brival_<?= h($uid) ?>" style="font-size:11px;color:var(--muted);min-width:24px"><?= $bri ?></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td style="white-space:nowrap">
|
|
|
|
|
|
<?php if($blocked):?>
|
|
|
|
|
|
<button class="btn btn-secondary btn-sm" data-uid="<?= h($uid) ?>" data-act="approve">Aprovar</button>
|
|
|
|
|
|
<?php else:?>
|
|
|
|
|
|
<button class="btn btn-danger btn-sm" data-uid="<?= h($uid) ?>" data-act="block">Bloquear</button>
|
|
|
|
|
|
<?php endif;?>
|
|
|
|
|
|
<button class="btn btn-secondary btn-sm" data-uid="<?= h($uid) ?>" data-act="cmd" data-cmd="REBOOT">Reboot</button>
|
|
|
|
|
|
<button class="btn btn-danger btn-sm" data-uid="<?= h($uid) ?>" data-act="delete">✕</button>
|
|
|
|
|
|
</td>
|
2026-06-27 21:43:45 +00:00
|
|
|
|
<td style="text-align:center">
|
|
|
|
|
|
<span title="VPS" data-uid="<?= h($uid) ?>" data-act="switch_pair" data-pair="0"
|
|
|
|
|
|
style="cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;font-size:9px;font-weight:700;margin:0 3px;background:<?= $isVps?'#3fb950':'#333' ?>;color:<?= $isVps?'#0d1117':'#666' ?>;<?= $isVps?'box-shadow:0 0 5px #3fb950;':'' ?>">V</span>
|
|
|
|
|
|
<span title="Proxmox" data-uid="<?= h($uid) ?>" data-act="switch_pair" data-pair="1"
|
|
|
|
|
|
style="cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;font-size:9px;font-weight:700;margin:0 3px;background:<?= $isPmx?'#3fb950':'#333' ?>;color:<?= $isPmx?'#0d1117':'#666' ?>;<?= $isPmx?'box-shadow:0 0 5px #3fb950;':'' ?>">P</span>
|
2026-06-27 12:03:57 +00:00
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
<?php endforeach;?>
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<?php endif;?>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
const _bt={};
|
|
|
|
|
|
function act(uid, action, extra){
|
|
|
|
|
|
const fd=new FormData(); fd.append('uid',uid); fd.append('action',action);
|
|
|
|
|
|
if(extra) Object.keys(extra).forEach(k=>fd.append(k,extra[k]));
|
|
|
|
|
|
fetch('/esp_vps.php',{method:'POST',body:fd}).then(()=>location.reload());
|
|
|
|
|
|
}
|
|
|
|
|
|
document.addEventListener('click',function(e){
|
2026-06-27 21:43:45 +00:00
|
|
|
|
const b=e.target.closest('[data-act]'); if(!b) return;
|
2026-06-27 12:03:57 +00:00
|
|
|
|
if(b.dataset.act==='delete' && !confirm('Remover '+b.dataset.uid+'?')) return;
|
|
|
|
|
|
const extra={};
|
|
|
|
|
|
if(b.dataset.pair!==undefined) extra.pair=b.dataset.pair;
|
|
|
|
|
|
if(b.dataset.cmd!==undefined) extra.cmd=b.dataset.cmd;
|
|
|
|
|
|
act(b.dataset.uid,b.dataset.act,extra);
|
|
|
|
|
|
});
|
|
|
|
|
|
function setBri(uid,val){
|
|
|
|
|
|
document.getElementById('brival_'+uid).textContent=val;
|
|
|
|
|
|
clearTimeout(_bt[uid]);
|
|
|
|
|
|
_bt[uid]=setTimeout(()=>{
|
|
|
|
|
|
const fd=new FormData();
|
|
|
|
|
|
fd.append('action','brightness');fd.append('uid',uid);fd.append('value',val);
|
|
|
|
|
|
fetch('/esp_vps.php',{method:'POST',headers:{'X-Requested-With':'XMLHttpRequest'},body:fd});
|
|
|
|
|
|
},400);
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<?php $content = ob_get_clean(); require __DIR__.'/inc/layout.php';
|