mirror of
https://gitlab.com/pancakes1234/wdiscordbotserver.git
synced 2025-06-16 07:14:21 -06:00
374 lines
14 KiB
PHP
374 lines
14 KiB
PHP
<?php
|
|
// Improved Discord Bot Admin API
|
|
|
|
// Error reporting (disable on production)
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 1);
|
|
|
|
// Configure secure session parameters:
|
|
session_set_cookie_params([
|
|
'httponly' => true,
|
|
'secure' => true, // Ensure HTTPS is used in production
|
|
'samesite' => 'Strict'
|
|
]);
|
|
session_start();
|
|
|
|
// --- CSRF Utility Functions ---
|
|
function getCsrfToken() {
|
|
if (empty($_SESSION['csrf_token'])) {
|
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
}
|
|
return $_SESSION['csrf_token'];
|
|
}
|
|
|
|
function validateCsrfToken($token) {
|
|
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
|
}
|
|
|
|
// --- Login and Authentication ---
|
|
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username'], $_POST['password'], $_POST['csrf_token'])) {
|
|
if (!validateCsrfToken($_POST['csrf_token'])) {
|
|
$error = "Invalid CSRF token.";
|
|
} else {
|
|
$envUser = getenv('user');
|
|
$envPass = getenv('pass');
|
|
// Using hash_equals for timing attack prevention
|
|
if (hash_equals($_POST['username'], $envUser) && hash_equals($_POST['password'], $envPass)) {
|
|
$_SESSION['logged_in'] = true;
|
|
session_regenerate_id(true);
|
|
header("Location: " . $_SERVER['PHP_SELF']);
|
|
exit;
|
|
} else {
|
|
$error = "Invalid credentials.";
|
|
}
|
|
}
|
|
}
|
|
$loginToken = getCsrfToken();
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Login</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: #f0f0f0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
}
|
|
.login-container {
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 5px;
|
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
|
width: 300px;
|
|
}
|
|
input[type="text"], input[type="password"] {
|
|
width: 100%;
|
|
padding: 8px;
|
|
margin: 5px 0 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 3px;
|
|
}
|
|
input[type="submit"] {
|
|
background: #007BFF;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px;
|
|
width: 100%;
|
|
cursor: pointer;
|
|
border-radius: 3px;
|
|
}
|
|
input[type="submit"]:hover {
|
|
background: #0056b3;
|
|
}
|
|
.error {
|
|
color: red;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="login-container">
|
|
<h2>Login</h2>
|
|
<?php if(isset($error)) { echo "<p class='error'>" . htmlspecialchars($error) . "</p>"; } ?>
|
|
<form method="POST" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
|
|
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($loginToken); ?>">
|
|
<label>Username</label>
|
|
<input type="text" name="username" required>
|
|
<label>Password</label>
|
|
<input type="password" name="password" required>
|
|
<input type="submit" value="Login">
|
|
</form>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
<?php
|
|
exit;
|
|
}
|
|
|
|
// --- Action Handlers ---
|
|
$output = "";
|
|
|
|
function handleVersionAction() {
|
|
$botDir = '/home/server/wdiscordbotserver';
|
|
if (is_dir($botDir)) {
|
|
$cmd = 'cd ' . escapeshellarg($botDir) . ' && git rev-parse HEAD 2>&1';
|
|
return trim(shell_exec($cmd));
|
|
}
|
|
return "Directory not found.";
|
|
}
|
|
|
|
function handleUpdateAction() {
|
|
// Only allow updates using a POST request with a valid CSRF token.
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['csrf_token']) || !validateCsrfToken($_POST['csrf_token'])) {
|
|
return "Unauthorized update request.";
|
|
}
|
|
$botDir = '/home/server/wdiscordbotserver';
|
|
$result = "";
|
|
if (is_dir($botDir)) {
|
|
$rmCmd = 'rm -rf ' . escapeshellarg($botDir) . ' 2>&1';
|
|
$result .= shell_exec($rmCmd);
|
|
}
|
|
$cloneCmd = 'git clone https://gitlab.com/pancakes1234/wdiscordbotserver.git ' . escapeshellarg($botDir) . ' 2>&1';
|
|
$result .= shell_exec($cloneCmd);
|
|
return $result;
|
|
}
|
|
|
|
function handleDataAction() {
|
|
$baseDir = realpath('/home/server');
|
|
$file = $_GET['file'] ?? null;
|
|
$response = "";
|
|
if ($file) {
|
|
$realFile = realpath($file);
|
|
if ($realFile === false || strpos($realFile, $baseDir) !== 0) {
|
|
$response = "Invalid file.";
|
|
} else {
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content'], $_POST['csrf_token'])) {
|
|
if (!validateCsrfToken($_POST['csrf_token'])) {
|
|
$response = "Invalid CSRF token.";
|
|
} else {
|
|
if (file_put_contents($realFile, $_POST['content']) !== false) {
|
|
$response = "File updated successfully.";
|
|
} else {
|
|
$response = "Failed to update file.";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $response;
|
|
}
|
|
|
|
// --- Process Request Actions ---
|
|
$action = $_GET['action'] ?? ($_POST['action'] ?? "");
|
|
switch ($action) {
|
|
case "version":
|
|
$output = handleVersionAction();
|
|
break;
|
|
case "update":
|
|
$output = handleUpdateAction();
|
|
break;
|
|
case "data":
|
|
$output = handleDataAction();
|
|
break;
|
|
// Additional action cases (e.g., "terminal") can be handled below.
|
|
default:
|
|
break;
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Discord Bot Admin API</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: #e9e9e9;
|
|
margin: 0;
|
|
padding: 20px;
|
|
}
|
|
.container {
|
|
max-width: 900px;
|
|
margin: auto;
|
|
background: #fff;
|
|
padding: 20px;
|
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
|
}
|
|
header {
|
|
margin-bottom: 20px;
|
|
}
|
|
header button, header form button {
|
|
padding: 10px 20px;
|
|
margin-right: 10px;
|
|
border: none;
|
|
background: #007BFF;
|
|
color: white;
|
|
cursor: pointer;
|
|
border-radius: 3px;
|
|
}
|
|
header button:hover, header form button:hover {
|
|
background: #0056b3;
|
|
}
|
|
.output {
|
|
background: #f4f4f4;
|
|
padding: 10px;
|
|
margin-top: 20px;
|
|
white-space: pre-wrap;
|
|
}
|
|
.file-list {
|
|
list-style-type: none;
|
|
padding: 0;
|
|
}
|
|
.file-list li {
|
|
margin: 5px 0;
|
|
}
|
|
a {
|
|
text-decoration: none;
|
|
color: #007BFF;
|
|
}
|
|
a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
textarea {
|
|
width: 100%;
|
|
height: 300px;
|
|
}
|
|
/* Terminal tabs styling */
|
|
#terminal-tabs button {
|
|
padding: 8px 16px;
|
|
border: none;
|
|
margin-right: 5px;
|
|
background: #007BFF;
|
|
color: white;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
}
|
|
#terminal-tabs button:hover {
|
|
background: #0056b3;
|
|
}
|
|
</style>
|
|
<script>
|
|
// For Version and Update actions we can use AJAX.
|
|
function doAction(action) {
|
|
if (action === "data" || action === "terminal") {
|
|
window.location.href = "?action=" + action;
|
|
} else {
|
|
fetch("?action=" + action)
|
|
.then(response => response.text())
|
|
.then(data => {
|
|
document.getElementById("output").innerText = data;
|
|
})
|
|
.catch(err => {
|
|
document.getElementById("output").innerText = "Error: " + err;
|
|
});
|
|
}
|
|
}
|
|
// SSH Connect functionality using the "ssh://" protocol.
|
|
function doSSH() {
|
|
var sshUser = "<?php echo htmlspecialchars(getenv('user')); ?>";
|
|
var sshHost = window.location.hostname;
|
|
window.location.href = "ssh://" + sshUser + "@" + sshHost;
|
|
}
|
|
function showTab(tab) {
|
|
document.getElementById('wetty').style.display = (tab === 'wetty') ? 'block' : 'none';
|
|
document.getElementById('xterm').style.display = (tab === 'xterm') ? 'block' : 'none';
|
|
}
|
|
window.addEventListener('load', function() {
|
|
if(document.getElementById('xterm-container')) {
|
|
const terminal = new Terminal();
|
|
terminal.open(document.getElementById('xterm-container'));
|
|
|
|
const socket = new WebSocket('ws://' + window.location.hostname + ':3001');
|
|
socket.onopen = function() {
|
|
terminal.write("Connected to shell\r\n");
|
|
};
|
|
terminal.onData(function(data) {
|
|
socket.send(data);
|
|
});
|
|
socket.onmessage = function(event) {
|
|
terminal.write(event.data);
|
|
};
|
|
socket.onerror = function() {
|
|
terminal.write("\r\nError connecting to shell.\r\n");
|
|
};
|
|
socket.onclose = function() {
|
|
terminal.write("\r\nConnection closed.\r\n");
|
|
};
|
|
}
|
|
});
|
|
</script>
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm/css/xterm.css" />
|
|
<script src="https://cdn.jsdelivr.net/npm/xterm/lib/xterm.js"></script>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<h1>Discord Bot Admin API</h1>
|
|
<button onclick="doAction('version')">Version</button>
|
|
<!-- Update action now uses a form (POST with CSRF token) to improve safety -->
|
|
<form style="display:inline;" method="POST" action="?action=update" onsubmit="return confirm('Are you sure you want to update the bot?');">
|
|
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getCsrfToken()); ?>">
|
|
<button type="submit">Update</button>
|
|
</form>
|
|
<button onclick="window.location.href='?action=data'">Data</button>
|
|
<button onclick="doSSH()">SSH Connect</button>
|
|
<button onclick="window.location.href='?action=terminal'">Terminal</button>
|
|
</header>
|
|
<!-- AJAX Output Area -->
|
|
<div id="output" class="output"><?php echo htmlspecialchars($output); ?></div>
|
|
|
|
<?php if ($action === "data"):
|
|
$baseDir = realpath('/home/server');
|
|
if (!isset($_GET['file'])): ?>
|
|
<h2>Files in <?php echo htmlspecialchars($baseDir); ?></h2>
|
|
<ul class="file-list">
|
|
<?php
|
|
try {
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($baseDir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
foreach ($iterator as $fileInfo) {
|
|
$filePath = $fileInfo->getPathname();
|
|
echo "<li><a href='?action=data&file=" . urlencode($filePath) . "'>" . htmlspecialchars($filePath) . "</a></li>";
|
|
}
|
|
} catch (Exception $e) {
|
|
echo "<li>Error reading files: " . htmlspecialchars($e->getMessage()) . "</li>";
|
|
}
|
|
?>
|
|
</ul>
|
|
<?php else:
|
|
$file = $_GET['file'];
|
|
$realFile = realpath($file);
|
|
if ($realFile === false || strpos($realFile, $baseDir) !== 0): ?>
|
|
<p>Invalid file.</p>
|
|
<?php else: ?>
|
|
<h2>Editing: <?php echo htmlspecialchars($realFile); ?></h2>
|
|
<form method="POST" action="?action=data&file=<?php echo urlencode($realFile); ?>">
|
|
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getCsrfToken()); ?>">
|
|
<textarea name="content"><?php echo htmlspecialchars(file_get_contents($realFile)); ?></textarea><br>
|
|
<input type="hidden" name="action" value="data">
|
|
<input type="submit" value="Save" style="padding:10px 20px; margin-top:10px;">
|
|
</form>
|
|
<?php endif;
|
|
endif;
|
|
elseif ($action === "terminal"): ?>
|
|
<div id="terminal-tabs" style="margin-bottom:10px;">
|
|
<button onclick="showTab('wetty')">Wetty Terminal</button>
|
|
<button onclick="showTab('xterm')">Xterm Terminal</button>
|
|
</div>
|
|
<div id="wetty" class="terminal-tab" style="display:block;">
|
|
<h2>Wetty Terminal</h2>
|
|
<iframe src="http://<?php echo htmlspecialchars($_SERVER['HTTP_HOST']); ?>:3000" width="100%" height="500px" frameborder="0"></iframe>
|
|
</div>
|
|
<div id="xterm" class="terminal-tab" style="display:none;">
|
|
<h2>Xterm Terminal</h2>
|
|
<div id="xterm-container" style="width: 100%; height: 500px;"></div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</body>
|
|
</html>
|