Compare commits

..

4 Commits

Author SHA256 Message Date
0352fffabe reset lock icon and status upon admin check failed 2025-10-22 14:23:31 +01:00
5227d38315 cleaned up unused code 2025-10-22 14:21:47 +01:00
7a632e7a77 Added Shell Page 2025-10-22 14:21:09 +01:00
717940d5c6 Updated 2025-10-22 14:20:51 +01:00
7 changed files with 278 additions and 20 deletions

2
.gitignore vendored
View File

@@ -5,4 +5,4 @@ src/img/**
**.log **.log
src/pages/infophp.php src/pages/infophp.php
src/pages/gethash.php src/pages/gethash.php
vars.php

109
src/css/shell.css Normal file
View File

@@ -0,0 +1,109 @@
/* css/shell.css — Themed terminal styling (dark/light aware) */
.shell-page {
max-width: 900px;
margin: 2rem auto;
padding: 1.5rem;
background: var(--card);
border-radius: 12px;
box-shadow: 0 4px 12px var(--shadow);
color: var(--text);
font-family: "JetBrains Mono", monospace;
transition: background 0.3s, color 0.3s;
}
.shell-page h2 {
text-align: center;
margin-bottom: 1rem;
font-weight: 500;
color: var(--text);
}
.shell-output {
background: #171717;
color: #01b501;
padding: 1rem;
height: 600px;
overflow-y: auto;
border-radius: 10px;
border: 1px solid #333;
box-shadow: inset 0 0 6px rgba(0,0,0,0.4);
font-size: 0.9rem;
margin-bottom: 1rem;
white-space: pre-wrap;
word-break: break-word;
}
body.light-mode .shell-output {
background: #f9f9f9;
color: #004400;
border: 1px solid #ddd;
box-shadow: inset 0 0 4px rgba(0,0,0,0.1);
}
.shell-form {
display: flex;
align-items: center;
gap: 0.6rem;
background: var(--bg);
padding: 0.5rem;
border-radius: 10px;
box-shadow: inset 0 0 4px rgba(0,0,0,0.2);
}
.shell-form .prompt {
font-weight: bold;
color: var(--accent);
}
.shell-input {
flex: 1;
padding: 0.6rem;
border-radius: 8px;
border: 1px solid rgba(255,255,255,0.08);
background: var(--card);
color: var(--text);
font-family: inherit;
font-size: 0.95rem;
outline: none;
transition: border-color 0.2s;
}
body.light-mode .shell-input {
border: 1px solid rgba(0,0,0,0.1);
background: #fff;
}
.shell-input:focus {
border-color: var(--accent);
}
.btn-run {
background: var(--accent);
color: #fff;
border: none;
padding: 0.6rem 1.2rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s, transform 0.1s;
}
.btn-run:hover {
background: var(--accentHover);
}
.btn-run:active {
transform: scale(0.96);
}
pre.line {
margin: 0;
padding: 0.2rem 0;
font-family: inherit;
}
pre.line.cmd { color: var(--accent); }
pre.line.err { color: #f33; }
pre.line.out { color: #01b501; }
body.light-mode pre.line.out { color: #007700; }

View File

@@ -0,0 +1,46 @@
<?php
// includes/shell_exec.php — Executes shell commands securely (admin only)
require_once __DIR__ . '/config.php';
session_start();
header('Content-Type: application/json');
if (empty($_SESSION['is_admin'])) {
http_response_code(403);
echo json_encode(['error' => 'Not authorized']);
exit;
}
$cmd = trim($_POST['command'] ?? '');
if ($cmd === '') {
echo json_encode(['output' => '']);
exit;
}
// Restrict dangerous commands for safety
$blacklist = ['rm', 'shutdown', 'reboot', 'passwd', 'dd', ':(){'];
foreach ($blacklist as $bad) {
if (stripos($cmd, $bad) !== false) {
echo json_encode(['output' => "⚠️ Command '$bad' not allowed"]);
exit;
}
}
// Execute safely (captures both stdout and stderr)
$descriptor = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
];
$process = proc_open($cmd, $descriptor, $pipes, '/home');
if (is_resource($process)) {
$output = stream_get_contents($pipes[1]);
$error = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$status = proc_close($process);
echo json_encode([
'output' => trim($output . "\n" . $error),
'status' => $status
]);
} else {
echo json_encode(['output' => 'Failed to execute command']);
}

View File

@@ -11,8 +11,9 @@ $pages_dir = __DIR__ . '/pages';
$nav_order = $CONFIG['nav_order'] ?? []; $nav_order = $CONFIG['nav_order'] ?? [];
$nav_admin = $CONFIG['nav_admin'] ?? []; $nav_admin = $CONFIG['nav_admin'] ?? [];
$nav_hidden = $CONFIG['nav_hidden'] ?? []; $nav_hidden = $CONFIG['nav_hidden'] ?? [];
$nav_items = [];
$nav_bar = []; $nav_bar = [];
$nav_bar_admin = [];
if (is_dir($pages_dir)) { if (is_dir($pages_dir)) {
foreach (glob($pages_dir . '/*.php') as $file) { foreach (glob($pages_dir . '/*.php') as $file) {
$base = strtolower(basename($file, '.php')); $base = strtolower(basename($file, '.php'));
@@ -29,23 +30,6 @@ if (is_dir($pages_dir)) {
} }
} }
} }
// Categorize pages
foreach ($nav_items as $base => $label) {
$lcBase = lcfirst($base);
if (in_array($lcBase, array_map('strtolower',$nav_hidden))) {
continue; // skip hidden
}
if (in_array($lcBase, $nav_admin)) {
$nav_bar_admin[] = $label;
} elseif (in_array($lcBase, $nav_order)) {
$nav_bar[] = $label; // ordered pages
} else {
$nav_bar[] = $label; // unordered normal pages
}
}
// Optional: order nav_bar according to $nav_order // Optional: order nav_bar according to $nav_order
$ordered = []; $ordered = [];
foreach ($nav_order as $o) { foreach ($nav_order as $o) {
@@ -57,7 +41,7 @@ foreach ($nav_order as $o) {
} }
} }
$nav_bar = array_merge($ordered, $nav_bar); $nav_bar = array_merge($ordered, $nav_bar);
unset($nav_items,$nav_order,$nav_admin,$nav_hidden) unset($nav_order,$nav_admin,$nav_hidden);
?> ?>
<!doctype html> <!doctype html>

View File

@@ -43,6 +43,9 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} catch (err) { } catch (err) {
console.warn('Admin check failed', err); console.warn('Admin check failed', err);
lockBtn.textContent = '🔒';
lockBtn.classList.add('locked');
lockBtn.title = 'Admin Login';
} }
} }

90
src/js/shell.js Normal file
View File

@@ -0,0 +1,90 @@
// js/shell.js — SPA-safe interactive shell with history + Ctrl-L clear
(() => {
if (window.shellInitialized) return;
window.shellInitialized = true;
document.addEventListener('page-loaded', (e) => {
if (e.detail.page !== 'shell') return;
console.log('[shell.js] Initializing shell...');
const form = document.getElementById('shellForm');
const input = document.getElementById('shellInput');
const output = document.getElementById('shellOutput');
const runBtn = document.getElementById('runBtn');
if (!form || !input || !output || !runBtn) return;
// ----- History -----
const history = [];
let histIndex = -1;
function appendLine(text, type = 'out') {
const line = document.createElement('pre');
line.className = `line ${type}`;
line.textContent = text;
output.appendChild(line);
output.scrollTop = output.scrollHeight;
}
form.addEventListener('submit', async (e) => {
e.preventDefault();
e.stopPropagation();
const cmd = input.value.trim();
if (!cmd) return;
history.push(cmd);
histIndex = history.length;
appendLine(`$ ${cmd}`, 'cmd');
input.value = '';
runBtn.disabled = true;
try {
const res = await fetch('includes/shell_exec.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ command: cmd })
});
const data = await res.json();
if (data.error) appendLine(data.error, 'err');
if (data.output) appendLine(data.output, 'out');
} catch (err) {
appendLine(`Error: ${err.message}`, 'err');
} finally {
runBtn.disabled = false;
}
});
// ----- Keyboard handlers -----
input.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
if (histIndex > 0) {
histIndex--;
input.value = history[histIndex];
setTimeout(() => input.setSelectionRange(input.value.length, input.value.length), 0);
}
e.preventDefault();
} else if (e.key === 'ArrowDown') {
if (histIndex < history.length - 1) {
histIndex++;
input.value = history[histIndex];
} else {
histIndex = history.length;
input.value = '';
}
e.preventDefault();
} else if (e.key.toLowerCase() === 'l' && e.ctrlKey) {
// Ctrl+L clear screen
output.innerHTML = '';
e.preventDefault();
}
});
console.log('[shell.js] Shell initialized.');
console.log('💡 Usage');
console.log('Shortcut Action');
console.log('↑ / ↓ Browse previous commands');
console.log('Ctrl + L Clear the screen');
console.log('Enter Execute command');
});
})();

View File

@@ -0,0 +1,26 @@
<?php
// pages/shell.php — Admin shell console
require_once __DIR__ . '/../includes/config.php';
session_start();
if (empty($_SESSION['is_admin'])) {
echo "<section class='center'><p class='error'>Access denied. Admin login required.</p></section>";
exit;
}
?>
<section class="shell-page">
<h2>Interactive Shell</h2>
<div id="shellOutput" class="shell-output"></div>
<form id="shellForm" class="shell-form">
<span class="prompt">$</span>
<input id="shellInput" class="shell-input" type="text" placeholder="Enter command..." autocomplete="off" />
<button id="runBtn" class="btn-run" type="submit">Run</button>
</form>
<script src="js/shell.js" defer></script>
<link rel="stylesheet" href="css/shell.css">
</section>
<link rel="stylesheet" href="css/shell.css">
<script src="js/shell.js" defer></script>