242 lines
12 KiB
PHP
Executable File
242 lines
12 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* WEVAL SSH API - Exécute des commandes SSH sur les serveurs
|
|
* GET = Interface de test | POST = API
|
|
*/
|
|
|
|
$servers = [
|
|
'weval-app' => ['name' => 'WEVAL Marketing', 'host' => '89.167.40.150', 'user' => 'root', 'local' => true],
|
|
'tracking' => ['name' => 'WEVAL Tracking', 'host' => '151.80.235.110', 'user' => 'ubuntu', 'pass' => 'vr3xjMvwMtWW', 'local' => false],
|
|
'consulting' => ['name' => 'WEVAL Consulting', 'host' => '46.62.220.135', 'user' => 'root', 'pass' => 'vr3xjMvwMtWW', 'local' => false]
|
|
];
|
|
|
|
$blocked = ['rm -rf /','rm -rf /*','mkfs',':(){ :|:& };:','dd if=/dev/zero','chmod -R 777 /','> /dev/sda'];
|
|
|
|
// POST = API JSON
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
header('Content-Type: application/json');
|
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
$command = trim($input['command'] ?? '');
|
|
$serverKey = $input['server'] ?? 'weval-app';
|
|
|
|
if (empty($command)) { echo json_encode(['error' => 'Command required']); exit; }
|
|
foreach ($blocked as $b) { if (stripos($command, $b) !== false) { echo json_encode(['error' => 'Blocked command']); exit; } }
|
|
|
|
$server = $servers[$serverKey] ?? $servers['weval-app'];
|
|
$start = microtime(true);
|
|
|
|
if ($server['local']) {
|
|
exec($command . ' 2>&1', $output, $code);
|
|
} else {
|
|
putenv("HOME=/var/www");
|
|
$ssh = "/usr/bin/sshpass -p " . $server['pass'] . " /usr/bin/ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 " . $server['user'] . "@" . $server['host'] . " " . escapeshellarg($command) . " 2>&1";
|
|
$raw = shell_exec($ssh);
|
|
$lines = explode(chr(10), $raw); $clean = []; foreach($lines as $l){ $l=str_replace(chr(13),"",$l); if(strpos($l,"Warning:")===false && trim($l)!=="") $clean[]=$l; } $raw = implode(chr(10),$clean);
|
|
$output = $raw ? explode("
|
|
", trim($raw)) : [];
|
|
$code = 0;
|
|
if (stripos($raw, 'Permission denied') !== false || stripos($raw, 'Connection refused') !== false) $code = 1;
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => $code === 0,
|
|
'output' => implode("\n", $output),
|
|
'exit_code' => $code,
|
|
'duration' => round((microtime(true) - $start) * 1000) . 'ms',
|
|
'server' => $server['name'],
|
|
'host' => $server['host']
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// GET = Interface de test
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>WEVAL SSH API - Test Interface</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: -apple-system, sans-serif; background: #0a0a0f; color: #f1f5f9; min-height: 100vh; padding: 30px; }
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
h1 { font-size: 24px; margin-bottom: 8px; display: flex; align-items: center; gap: 12px; }
|
|
.subtitle { color: #64748b; font-size: 14px; margin-bottom: 30px; }
|
|
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
|
|
@media (max-width: 900px) { .grid { grid-template-columns: 1fr; } }
|
|
.card { background: #12121a; border: 1px solid #2a2a3e; border-radius: 16px; overflow: hidden; }
|
|
.card-header { padding: 16px 20px; border-bottom: 1px solid #2a2a3e; font-weight: 600; display: flex; align-items: center; gap: 10px; }
|
|
.card-body { padding: 20px; }
|
|
.form-group { margin-bottom: 16px; }
|
|
.form-group label { display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px; }
|
|
.form-group select, .form-group input { width: 100%; padding: 12px; background: #1a1a2e; border: 1px solid #2a2a3e; border-radius: 8px; color: #f1f5f9; font-size: 14px; }
|
|
.form-group select:focus, .form-group input:focus { outline: none; border-color: #22d3ee; }
|
|
.btn { padding: 12px 24px; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; transition: all 0.2s; }
|
|
.btn-primary { background: linear-gradient(135deg, #22d3ee, #3b82f6); color: white; }
|
|
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(34,211,238,0.3); }
|
|
.output { background: #0d1117; border: 1px solid #2a2a3e; border-radius: 8px; padding: 16px; font-family: 'Fira Code', monospace; font-size: 12px; min-height: 300px; max-height: 500px; overflow-y: auto; white-space: pre-wrap; color: #8b949e; }
|
|
.output .success { color: #22c55e; }
|
|
.output .error { color: #ef4444; }
|
|
.output .info { color: #22d3ee; }
|
|
.servers-list { display: flex; flex-direction: column; gap: 12px; }
|
|
.server-item { background: #1a1a2e; border: 1px solid #2a2a3e; border-radius: 10px; padding: 14px; cursor: pointer; transition: all 0.2s; display: flex; justify-content: space-between; align-items: center; }
|
|
.server-item:hover { border-color: #22d3ee; }
|
|
.server-item.active { border-color: #22c55e; background: rgba(34,197,94,0.1); }
|
|
.server-item .name { font-weight: 600; }
|
|
.server-item .host { font-size: 12px; color: #64748b; font-family: monospace; }
|
|
.server-item .status { width: 10px; height: 10px; border-radius: 50%; background: #22c55e; }
|
|
.quick-cmds { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
|
|
.quick-cmd { background: #1a1a2e; border: 1px solid #2a2a3e; padding: 8px 12px; border-radius: 6px; font-size: 11px; cursor: pointer; transition: all 0.2s; font-family: monospace; }
|
|
.quick-cmd:hover { border-color: #22d3ee; background: #2a2a3e; }
|
|
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 20px; }
|
|
.stat { background: #1a1a2e; border-radius: 10px; padding: 14px; text-align: center; cursor: pointer; transition: all 0.2s; }
|
|
.stat:hover { background: #2a2a3e; }
|
|
.stat .value { font-size: 24px; font-weight: 700; color: #22d3ee; }
|
|
.stat .label { font-size: 11px; color: #64748b; margin-top: 4px; }
|
|
</style>
|
|
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🔌 WEVAL SSH API</h1>
|
|
<p class="subtitle">Interface de test pour l'API SSH - Exécutez des commandes sur vos serveurs</p>
|
|
|
|
<div class="stats">
|
|
<div class="stat" onclick="runQuickCmd('uptime')">
|
|
<div class="value" id="statUptime">--</div>
|
|
<div class="label">Uptime</div>
|
|
</div>
|
|
<div class="stat" onclick="runQuickCmd('free -h | grep Mem')">
|
|
<div class="value" id="statMem">--</div>
|
|
<div class="label">Mémoire</div>
|
|
</div>
|
|
<div class="stat" onclick="runQuickCmd('df -h / | tail -1')">
|
|
<div class="value" id="statDisk">--</div>
|
|
<div class="label">Disque</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid">
|
|
<div class="card">
|
|
<div class="card-header">🖥️ Serveurs</div>
|
|
<div class="card-body">
|
|
<div class="servers-list">
|
|
<?php foreach ($servers as $key => $srv): ?>
|
|
<div class="server-item <?= $key === 'weval-app' ? 'active' : '' ?>" onclick="selectServer('<?= $key ?>')">
|
|
<div>
|
|
<div class="name"><?= $srv['name'] ?></div>
|
|
<div class="host"><?= $srv['host'] ?> <?= $srv['local'] ? '(local)' : '(remote)' ?></div>
|
|
</div>
|
|
<div class="status"></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<div class="quick-cmds">
|
|
<div class="quick-cmd" onclick="runQuickCmd('hostname')">hostname</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('uptime')">uptime</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('df -h')">df -h</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('free -h')">free -h</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('top -bn1 | head -15')">top</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('ps aux | head -20')">ps aux</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('netstat -tlnp')">netstat</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('systemctl status apache2')">apache</div>
|
|
<div class="quick-cmd" onclick="runQuickCmd('pmta status')">pmta</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">⌨️ Commande</div>
|
|
<div class="card-body">
|
|
<div class="form-group">
|
|
<label>Commande à exécuter</label>
|
|
<input type="text" id="cmdInput" placeholder="Ex: ls -la /opt/wevads" onkeypress="if(event.key==='Enter')runCmd()">
|
|
</div>
|
|
<button class="btn btn-primary" onclick="runCmd()">▶️ Exécuter</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card" style="margin-top:24px;">
|
|
<div class="card-header">📟 Output</div>
|
|
<div class="card-body">
|
|
<div class="output" id="output">Sélectionnez un serveur et entrez une commande...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let currentServer = 'weval-app';
|
|
|
|
function selectServer(key) {
|
|
currentServer = key;
|
|
document.querySelectorAll('.server-item').forEach(el => el.classList.remove('active'));
|
|
event.currentTarget.classList.add('active');
|
|
appendOutput('<span class="info">→ Serveur sélectionné: ' + key + '</span>\n');
|
|
}
|
|
|
|
function runQuickCmd(cmd) {
|
|
document.getElementById('cmdInput').value = cmd;
|
|
runCmd();
|
|
}
|
|
|
|
async function runCmd() {
|
|
const cmd = document.getElementById('cmdInput').value.trim();
|
|
if (!cmd) return;
|
|
|
|
appendOutput('<span class="info">$ ' + cmd + '</span>\n');
|
|
|
|
try {
|
|
const r = await fetch(location.href, {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({command: cmd, server: currentServer})
|
|
});
|
|
const d = await r.json();
|
|
|
|
if (d.success) {
|
|
appendOutput(d.output + '\n');
|
|
appendOutput('<span class="success">[✓ ' + d.server + ' | ' + d.duration + ']</span>\n\n');
|
|
} else {
|
|
appendOutput('<span class="error">' + (d.output || d.error) + '</span>\n');
|
|
appendOutput('<span class="error">[✗ Exit: ' + d.exit_code + ']</span>\n\n');
|
|
}
|
|
} catch (e) {
|
|
appendOutput('<span class="error">Erreur: ' + e.message + '</span>\n\n');
|
|
}
|
|
}
|
|
|
|
function appendOutput(text) {
|
|
const el = document.getElementById('output');
|
|
el.innerHTML += text;
|
|
el.scrollTop = el.scrollHeight;
|
|
}
|
|
|
|
// Load stats on page load
|
|
async function loadStats() {
|
|
try {
|
|
// Uptime
|
|
let r = await fetch(location.href, {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({command:'uptime -p',server:'weval-app'})});
|
|
let d = await r.json();
|
|
document.getElementById('statUptime').textContent = d.output?.replace('up ','') || '--';
|
|
|
|
// Memory
|
|
r = await fetch(location.href, {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({command:"free -h | awk '/Mem:/{print $3\"/\"$2}'",server:'weval-app'})});
|
|
d = await r.json();
|
|
document.getElementById('statMem').textContent = d.output || '--';
|
|
|
|
// Disk
|
|
r = await fetch(location.href, {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({command:"df -h / | awk 'NR==2{print $5}'",server:'weval-app'})});
|
|
d = await r.json();
|
|
document.getElementById('statDisk').textContent = d.output || '--';
|
|
} catch(e) {}
|
|
}
|
|
|
|
loadStats();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|