<?php
declare(strict_types=1);

/*
 * Laravel Subdomain File Manager
 * Secure replacement for upll2.php
 *
 * IMPORTANT:
 * 1. Set FILE_ROOT to the REAL document root of the subdomain.
 * 2. Set a strong password in FM_PASSWORD_HASH.
 * 3. Keep this file outside the public web root when possible.
 * 4. This tool intentionally does NOT execute shell commands.
 */

error_reporting(E_ALL);
ini_set('display_errors', '0');

session_set_cookie_params([
    'httponly' => true,
    'secure'   => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
    'samesite'=> 'Strict',
]);
session_start();

/* =========================
   CONFIGURATION
   ========================= */

// CHANGE THIS to the Laravel subdomain document root.
const FILE_ROOT = '/home/u853860904/domains/';

/*
 * FIRST RUN:
 * Tidak membutuhkan SSH.
 * Jika password belum pernah dibuat, halaman setup akan muncul otomatis.
 * Setelah setup selesai, hash disimpan di file .fm-password.php di folder
 * yang sama dengan file manager.
 */
const PASSWORD_STORE = __DIR__ . '/.fm-password.php';

// Maximum upload size handled by this tool.
const MAX_UPLOAD_BYTES = 20 * 1024 * 1024;

// File extensions allowed for upload.
// Add/remove according to your Laravel build requirements.
const ALLOWED_UPLOAD_EXTENSIONS = [
    'php','php8','phtml','html','htm','css','js','json','xml','txt',
    'md','env','yml','yaml','ini','conf','log','sql','svg','webmanifest',
    'map','ico','jpg','jpeg','png','gif','webp','avif','woff','woff2',
    'ttf','eot','zip'
];

// Extensions that can be edited in the browser.
const EDITABLE_EXTENSIONS = [
    'php','php8','phtml','html','htm','css','js','json','xml','txt',
    'md','env','yml','yaml','ini','conf','log','sql','svg','webmanifest'
];

/* =========================
   PHP COMPATIBILITY
   ========================= */

if (version_compare(PHP_VERSION, '7.4.0', '<')) {
    http_response_code(500);
    exit(
        'PHP minimal yang didukung adalah PHP 7.4. ' .
        'Versi PHP server saat ini: ' . PHP_VERSION
    );
}

/* =========================
   SECURITY / HELPERS
   ========================= */

function h(string $value): string {
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function rootPath(): string {
    $root = realpath(FILE_ROOT);
    if ($root === false || !is_dir($root)) {
        http_response_code(500);
        exit('FILE_ROOT tidak ditemukan. Periksa konfigurasi.');
    }
    return rtrim($root, DIRECTORY_SEPARATOR);
}

function safePath(string $relative): string {
    $root = rootPath();

    $relative = str_replace("\0", '', $relative);
    $relative = str_replace('\\', '/', $relative);
    $relative = trim($relative, '/');

    if ($relative === '') {
        return $root;
    }

    $candidate = $root . DIRECTORY_SEPARATOR . $relative;

    /*
     * realpath() works for existing files/directories.
     * For creation, validate the real parent directory separately.
     */
    $real = realpath($candidate);

    if ($real !== false) {
        $real = rtrim($real, DIRECTORY_SEPARATOR);
        if ($real === $root || strpos($real, $root . DIRECTORY_SEPARATOR) === 0) {
            return $real;
        }
    }

    $parent = realpath(dirname($candidate));
    if ($parent !== false &&
        ($parent === $root || str_starts_with($parent, $root . DIRECTORY_SEPARATOR))) {
        return $candidate;
    }

    throw new RuntimeException('Path di luar FILE_ROOT.');
}

function relativePath(string $absolute): string {
    $root = rootPath();
    $real = realpath($absolute);

    if ($real === false) {
        throw new RuntimeException('Path tidak ditemukan.');
    }

    if ($real === $root) {
        return '';
    }

    if (!strpos($real, $root . DIRECTORY_SEPARATOR) === 0) {
        throw new RuntimeException('Path tidak valid.');
    }

    return ltrim(substr($real, strlen($root)), DIRECTORY_SEPARATOR);
}

function currentDir(): string {
    $rel = $_GET['dir'] ?? '';
    return safePath((string)$rel);
}

function csrfToken(): string {
    if (empty($_SESSION['csrf'])) {
        $_SESSION['csrf'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf'];
}

function verifyCsrf(): void {
    $token = $_POST['csrf'] ?? '';
    if (!is_string($token) || !hash_equals($_SESSION['csrf'] ?? '', $token)) {
        http_response_code(403);
        exit('CSRF token tidak valid.');
    }
}

function requireLogin(): void {
    if (empty($_SESSION['fm_authenticated'])) {
        header('Location: ?login=1');
        exit;
    }
}

function extensionOf(string $name): string {
    return strtolower(pathinfo($name, PATHINFO_EXTENSION));
}

function validName(string $name): bool {
    if ($name === '' || $name === '.' || $name === '..') {
        return false;
    }

    if (strpos($name, "\0") !== false || strpos($name, '/') !== false || strpos($name, '\\') !== false) {
        return false;
    }

    return preg_match('/^[A-Za-z0-9._@+=,\- ]+$/', $name) === 1;
}

function editable(string $name): bool {
    return in_array(extensionOf($name), EDITABLE_EXTENSIONS, true);
}

function uploadAllowed(string $name): bool {
    return in_array(extensionOf($name), ALLOWED_UPLOAD_EXTENSIONS, true);
}

function flash(string $message, string $type = 'ok'): void {
    $_SESSION['flash'] = ['message' => $message, 'type' => $type];
}

function redirectDir(string $dir) {
    $query = $dir === '' ? '' : '?dir=' . rawurlencode($dir);
    header('Location: ' . strtok($_SERVER['REQUEST_URI'] ?? '', '?') . $query);
    exit;
}

/* =========================
   PASSWORD STORE / LOGIN
   ========================= */

function passwordConfigured(): bool {
    if (!is_file(PASSWORD_STORE) || !is_readable(PASSWORD_STORE)) {
        return false;
    }

    $raw = (string)@file_get_contents(PASSWORD_STORE);
    $lines = preg_split('/\\R/', $raw);
    $hash = '';

    foreach ($lines as $line) {
        $line = trim($line);
        if ($line !== '' && $line[0] !== '<' && $line[0] !== '*' && $line[0] !== '/' && $line !== 'exit;') {
            $hash = $line;
        }
    }

    return $hash !== '' && password_get_info($hash)['algo'] !== 0;
}

function storedPasswordHash(): string {
    if (!passwordConfigured()) {
        return '';
    }

    $raw = (string)@file_get_contents(PASSWORD_STORE);
    $lines = preg_split('/\\R/', $raw);

    foreach ($lines as $line) {
        $line = trim($line);
        if ($line !== '' && $line[0] !== '<' && $line[0] !== '*' && $line[0] !== '/' && $line !== 'exit;') {
            return $line;
        }
    }

    return '';
}

function savePasswordHash(string $hash): bool {
    /*
     * Store only the hash, not the plaintext password.
     * The file is also protected at the PHP level by an exit statement.
     */
    $content = "<?php\nexit;\n";
    $content .= "/* Password hash is intentionally stored outside executable logic. */\n";
    $content .= $hash . "\n";

    return @file_put_contents(PASSWORD_STORE, $content, LOCK_EX) !== false
        && @chmod(PASSWORD_STORE, 0600);
}

if (isset($_GET['logout'])) {
    $_SESSION = [];
    if (ini_get('session.use_cookies')) {
        $params = session_get_cookie_params();
        setcookie(
            session_name(),
            '',
            time() - 42000,
            $params['path'],
            $params['domain'],
            (bool)$params['secure'],
            (bool)$params['httponly']
        );
    }
    session_destroy();
    header('Location: ?login=1');
    exit;
}

/* ===== FIRST-RUN PASSWORD SETUP ===== */

if (!passwordConfigured()) {
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'setup') {
        verifyCsrf();

        $password  = (string)($_POST['password'] ?? '');
        $password2 = (string)($_POST['password2'] ?? '');

        if (strlen($password) < 12) {
            flash('Password minimal 12 karakter.', 'error');
        } elseif (!hash_equals($password, $password2)) {
            flash('Konfirmasi password tidak sama.', 'error');
        } else {
            $hash = password_hash($password, PASSWORD_DEFAULT);

            if ($hash === false || !savePasswordHash($hash)) {
                flash(
                    'Password gagal disimpan. Pastikan folder ini writable oleh PHP.',
                    'error'
                );
            } else {
                session_regenerate_id(true);
                $_SESSION['fm_authenticated'] = true;
                $_SESSION['csrf'] = bin2hex(random_bytes(32));
                redirectDir('');
            }
        }
    }

    $flash = $_SESSION['flash'] ?? null;
    unset($_SESSION['flash']);
    ?>
    <!doctype html>
    <html lang="id">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>Setup - Laravel File Manager</title>
        <style>
            body{background:#0b0d10;color:#e8edf2;font-family:system-ui,sans-serif;max-width:460px;margin:10vh auto;padding:20px}
            .box{background:#15191e;border:1px solid #303740;border-radius:12px;padding:24px}
            input,button{box-sizing:border-box;width:100%;padding:11px;margin-top:8px;border-radius:7px;border:1px solid #3a434d;background:#0d1014;color:#fff}
            button{cursor:pointer}
            .error{color:#ff7b7b;margin-bottom:10px}
            small{color:#8d98a4}
        </style>
    </head>
    <body>
    <div class="box">
        <h2>Setup File Manager</h2>
        <p>Ini adalah konfigurasi pertama. Buat password administrator tanpa SSH.</p>

        <?php if ($flash): ?>
            <div class="<?=h($flash['type'])?>"><?=h($flash['message'])?></div>
        <?php endif; ?>

        <form method="post" autocomplete="off">
            <input type="hidden" name="action" value="setup">
            <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">

            <label>Password baru</label>
            <input type="password" name="password" minlength="12" required autocomplete="new-password">

            <label>Ulangi password</label>
            <input type="password" name="password2" minlength="12" required autocomplete="new-password">

            <button type="submit">Buat Password</button>
        </form>

        <p><small>Password plaintext tidak disimpan. Hanya password hash yang disimpan.</small></p>
    </div>
    </body>
    </html>
    <?php
    exit;
}

/* ===== NORMAL LOGIN ===== */

if (isset($_POST['action']) && $_POST['action'] === 'login') {
    verifyCsrf();

    $password = (string)($_POST['password'] ?? '');
    $hash = storedPasswordHash();

    if ($hash !== '' && password_verify($password, $hash)) {
        session_regenerate_id(true);
        $_SESSION['fm_authenticated'] = true;
        $_SESSION['csrf'] = bin2hex(random_bytes(32));

        /*
         * Transparently upgrade old hashes when PHP's preferred algorithm changes.
         */
        if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
            @savePasswordHash(password_hash($password, PASSWORD_DEFAULT));
        }

        redirectDir('');
    }

    usleep(350000);
    flash('Password salah.', 'error');

    header('Location: ?login=1');
    exit;
}

if (isset($_GET['login']) && empty($_SESSION['fm_authenticated'])) {
    $flash = $_SESSION['flash'] ?? null;
    unset($_SESSION['flash']);
    ?>
    <!doctype html>
    <html lang="id">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>Login - Laravel File Manager</title>
        <style>
            body{background:#0b0d10;color:#e8edf2;font-family:system-ui,sans-serif;max-width:420px;margin:12vh auto;padding:20px}
            .box{background:#15191e;border:1px solid #303740;border-radius:12px;padding:24px}
            input,button{box-sizing:border-box;width:100%;padding:11px;margin-top:8px;border-radius:7px;border:1px solid #3a434d;background:#0d1014;color:#fff}
            button{cursor:pointer}
            .error{color:#ff7b7b;margin-bottom:10px}
        </style>
    </head>
    <body>
    <div class="box">
        <h2>Laravel File Manager</h2>

        <?php if ($flash): ?>
            <div class="<?=h($flash['type'])?>"><?=h($flash['message'])?></div>
        <?php endif; ?>

        <form method="post" autocomplete="off">
            <input type="hidden" name="action" value="login">
            <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">
            <label>Password</label>
            <input type="password" name="password" required autofocus autocomplete="current-password">
            <button type="submit">Login</button>
        </form>
    </div>
    </body>
    </html>
    <?php
    exit;
}

requireLogin();

/* =========================
   ACTIONS
   ========================= */

$dir = currentDir();
$dirRel = relativePath($dir);
$action = (string)($_POST['action'] ?? $_GET['action'] ?? '');

try {
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        verifyCsrf();
    }

    switch ($action) {
        case 'upload':
            if (!isset($_FILES['file']) || !is_array($_FILES['file'])) {
                throw new RuntimeException('File upload tidak ditemukan.');
            }

            $f = $_FILES['file'];

            if (($f['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
                throw new RuntimeException('Upload gagal. Kode: ' . (int)$f['error']);
            }

            if (!is_uploaded_file($f['tmp_name'])) {
                throw new RuntimeException('Sumber upload tidak valid.');
            }

            $name = basename((string)$f['name']);

            if (!validName($name)) {
                throw new RuntimeException('Nama file tidak valid.');
            }

            if ($name === basename(PASSWORD_STORE)) {
                throw new RuntimeException('File sistem password tidak boleh diubah melalui file manager.');
            }

            if (!uploadAllowed($name)) {
                throw new RuntimeException('Extension file tidak diizinkan.');
            }

            if ((int)$f['size'] > MAX_UPLOAD_BYTES) {
                throw new RuntimeException('File terlalu besar.');
            }

            $target = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $name);

            if (file_exists($target)) {
                throw new RuntimeException('File sudah ada. Rename file terlebih dahulu.');
            }

            if (!move_uploaded_file($f['tmp_name'], $target)) {
                throw new RuntimeException('Gagal memindahkan file upload.');
            }

            flash('Upload berhasil: ' . $name);
            redirectDir($dirRel);

        case 'delete':
            $name = basename((string)($_POST['name'] ?? ''));

            if (!validName($name)) {
                throw new RuntimeException('Nama file/folder tidak valid.');
            }

            $target = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $name);

            if (!file_exists($target)) {
                throw new RuntimeException('File/folder tidak ditemukan.');
            }

            if (is_dir($target)) {
                if (count(scandir($target)) > 2) {
                    throw new RuntimeException('Folder tidak kosong.');
                }
                if (!rmdir($target)) {
                    throw new RuntimeException('Folder gagal dihapus.');
                }
            } else {
                if (!unlink($target)) {
                    throw new RuntimeException('File gagal dihapus.');
                }
            }

            flash('Dihapus: ' . $name);
            redirectDir($dirRel);

        case 'rename':
            $old = basename((string)($_POST['oldname'] ?? ''));
            $new = basename((string)($_POST['newname'] ?? ''));

            if (!validName($old) || !validName($new)) {
                throw new RuntimeException('Nama tidak valid.');
            }

            $source = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $old);
            $target = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $new);

            if (!file_exists($source)) {
                throw new RuntimeException('File/folder asal tidak ditemukan.');
            }

            if (file_exists($target)) {
                throw new RuntimeException('Nama tujuan sudah digunakan.');
            }

            if (!rename($source, $target)) {
                throw new RuntimeException('Rename gagal.');
            }

            flash('Rename berhasil.');
            redirectDir($dirRel);

        case 'mkdir':
            $name = basename((string)($_POST['name'] ?? ''));

            if (!validName($name)) {
                throw new RuntimeException('Nama folder tidak valid.');
            }

            $target = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $name);

            if (file_exists($target)) {
                throw new RuntimeException('Folder sudah ada.');
            }

            if (!mkdir($target, 0755)) {
                throw new RuntimeException('Folder gagal dibuat.');
            }

            flash('Folder dibuat: ' . $name);
            redirectDir($dirRel);

        case 'newfile':
            $name = basename((string)($_POST['name'] ?? ''));

            if (!validName($name)) {
                throw new RuntimeException('Nama file tidak valid.');
            }

            if ($name === basename(PASSWORD_STORE)) {
                throw new RuntimeException('File sistem password tidak boleh dibuat.');
            }

            if (!uploadAllowed($name)) {
                throw new RuntimeException('Extension file tidak diizinkan.');
            }

            $target = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $name);

            if (file_exists($target)) {
                throw new RuntimeException('File sudah ada.');
            }

            if (file_put_contents($target, '') === false) {
                throw new RuntimeException('File gagal dibuat.');
            }

            flash('File dibuat: ' . $name);
            redirectDir($dirRel);

        case 'save':
            $name = basename((string)($_POST['filename'] ?? ''));

            if (!validName($name) || !editable($name) || $name === basename(PASSWORD_STORE)) {
                throw new RuntimeException('File tidak boleh diedit melalui browser.');
            }

            $target = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $name);

            if (!is_file($target)) {
                throw new RuntimeException('File tidak ditemukan.');
            }

            $content = (string)($_POST['content'] ?? '');

            if (strlen($content) > MAX_UPLOAD_BYTES) {
                throw new RuntimeException('Isi file terlalu besar.');
            }

            if (file_put_contents($target, $content, LOCK_EX) === false) {
                throw new RuntimeException('File gagal disimpan.');
            }

            flash('File disimpan: ' . $name);
            redirectDir($dirRel);
    }
} catch (Throwable $e) {
    flash($e->getMessage(), 'error');
    redirectDir($dirRel);
}


/* =========================
   EDIT FORM
   ========================= */

if ($action === 'edit' && isset($_GET['item'])) {
    $name = basename((string)$_GET['item']);

    if (!validName($name) || !editable($name)) {
        http_response_code(400);
        exit('File tidak boleh diedit.');
    }

    $file = safePath(($dirRel !== '' ? $dirRel . '/' : '') . $name);

    if (!is_file($file)) {
        http_response_code(404);
        exit('File tidak ditemukan.');
    }

    $content = file_get_contents($file);
    if ($content === false) {
        http_response_code(500);
        exit('File tidak dapat dibaca.');
    }

    $flash = $_SESSION['flash'] ?? null;
    unset($_SESSION['flash']);
    ?>
    <!doctype html>
    <html lang="id">
    <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Edit <?=h($name)?></title>
    <style>
    body{margin:0;background:#0b0d10;color:#e8edf2;font-family:system-ui,sans-serif}
    .wrap{max-width:1200px;margin:25px auto;padding:0 16px}
    textarea{width:100%;min-height:75vh;box-sizing:border-box;background:#090b0e;color:#dce6ef;border:1px solid #414b56;border-radius:8px;padding:12px;font:13px/1.5 monospace}
    button,a{font:inherit}
    button{background:#20262d;color:#fff;border:1px solid #414b56;border-radius:7px;padding:9px 13px;cursor:pointer}
    a{color:#8bc7ff;text-decoration:none}
    .bar{background:#15191e;border:1px solid #303740;border-radius:10px;padding:12px;margin:12px 0}
    .ok{color:#7ee787}.error{color:#ff7b7b}
    </style>
    </head>
    <body>
    <div class="wrap">
        <p><a href="?dir=<?=rawurlencode($dirRel)?>">← Kembali</a></p>
        <h2>Edit: <?=h($name)?></h2>

        <?php if ($flash): ?>
            <div class="bar <?=h($flash['type'])?>"><?=h($flash['message'])?></div>
        <?php endif; ?>

        <form method="post">
            <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">
            <input type="hidden" name="action" value="save">
            <input type="hidden" name="filename" value="<?=h($name)?>">
            <textarea name="content" spellcheck="false"><?=h($content)?></textarea>
            <p><button type="submit">Simpan</button></p>
        </form>
    </div>
    </body>
    </html>
    <?php
    exit;
}

/* =========================
   DISPLAY
   =========================
*/


$flash = $_SESSION['flash'] ?? null;
unset($_SESSION['flash']);

$items = scandir($dir);
$items = array_values(array_filter($items, static fn($v) => $v !== '.' && $v !== '..'));
sort($items, SORT_NATURAL | SORT_FLAG_CASE);

function sizeText(int $size): string {
    $units = ['B','KB','MB','GB'];
    $i = 0;
    $n = $size;
    while ($n >= 1024 && $i < count($units)-1) {
        $n /= 1024;
        $i++;
    }
    return number_format($n, 2) . ' ' . $units[$i];
}
?>
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Laravel File Manager</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#0b0d10;color:#e8edf2;font-family:system-ui,-apple-system,sans-serif}
.wrap{max-width:1100px;margin:30px auto;padding:0 16px}
.top{display:flex;justify-content:space-between;gap:15px;align-items:center;flex-wrap:wrap}
h2{margin:0}
a{color:#8bc7ff;text-decoration:none}
a:hover{text-decoration:underline}
.bar,.card{background:#15191e;border:1px solid #303740;border-radius:10px;padding:12px;margin-top:14px}
.path{font-family:monospace;word-break:break-all}
form.inline{display:inline}
button,input,textarea{font:inherit}
button{background:#20262d;color:#fff;border:1px solid #414b56;border-radius:7px;padding:7px 10px;cursor:pointer}
button:hover{background:#2a323b}
input{background:#0d1014;color:#fff;border:1px solid #414b56;border-radius:7px;padding:8px}
textarea{width:100%;min-height:520px;background:#090b0e;color:#dce6ef;border:1px solid #414b56;border-radius:8px;padding:12px;font:13px/1.5 monospace}
.item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:center;padding:10px 4px;border-bottom:1px solid #252b32}
.item:last-child{border-bottom:0}
.name{min-width:0;overflow-wrap:anywhere}
.meta{color:#8d98a4;font-size:12px}
.actions{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
.ok{color:#7ee787}.error{color:#ff7b7b}
.danger{border-color:#8d3b3b}
small{color:#8d98a4}
@media(max-width:650px){
    .item{grid-template-columns:1fr}
    .actions{justify-content:flex-start}
}
</style>
</head>
<body>
<div class="wrap">

<div class="top">
    <h2>Laravel File Manager</h2>
    <a href="?logout=1">Logout</a>
</div>

<?php if ($flash): ?>
<div class="bar <?=h($flash['type'])?>"><?=h($flash['message'])?></div>
<?php endif; ?>

<div class="bar">
    <div class="path">
        Root: <strong><?=h(FILE_ROOT)?></strong><br>
        Current: <strong><?=h($dirRel === '' ? '/' : '/' . $dirRel)?></strong>
    </div>
</div>

<div class="card">
    <?php if ($dirRel !== ''): ?>
        <a href="?dir=<?=rawurlencode(dirname($dirRel) === '.' ? '' : dirname($dirRel))?>">← Folder sebelumnya</a>
    <?php endif; ?>

    <hr>

    <form method="post" enctype="multipart/form-data">
        <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">
        <input type="hidden" name="action" value="upload">
        <input type="file" name="file" required>
        <button type="submit">Upload</button>
    </form>

    <br>

    <form method="post" class="inline">
        <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">
        <input type="hidden" name="action" value="mkdir">
        <input name="name" placeholder="Nama folder" required>
        <button type="submit">+ Folder</button>
    </form>

    <form method="post" class="inline">
        <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">
        <input type="hidden" name="action" value="newfile">
        <input name="name" placeholder="Nama file" required>
        <button type="submit">+ File</button>
    </form>
</div>

<div class="card">
<?php foreach ($items as $item):
    $path = $dir . DIRECTORY_SEPARATOR . $item;
    $isDir = is_dir($path);
    $rel = ($dirRel !== '' ? $dirRel . '/' : '') . $item;
?>
<div class="item">
    <div class="name">
        <?php if ($isDir): ?>
            📁 <a href="?dir=<?=rawurlencode($rel)?>"><?=h($item)?></a>
        <?php else: ?>
            📄 <?=h($item)?>
            <div class="meta"><?=sizeText((int)filesize($path))?></div>
        <?php endif; ?>
    </div>

    <div class="actions">
        <?php if (!$isDir && editable($item)): ?>
            <a href="?action=edit&dir=<?=rawurlencode($dirRel)?>&item=<?=rawurlencode($item)?>">
                <button type="button">Edit</button>
            </a>
        <?php endif; ?>

        <form method="post" class="inline">
            <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">
            <input type="hidden" name="action" value="rename">
            <input type="hidden" name="oldname" value="<?=h($item)?>">
            <input type="text" name="newname" value="<?=h($item)?>" required>
            <button type="submit">Rename</button>
        </form>

        <form method="post" class="inline"
              onsubmit="return confirm('Hapus <?=h($item)?>?')">
            <input type="hidden" name="csrf" value="<?=h(csrfToken())?>">
            <input type="hidden" name="action" value="delete">
            <input type="hidden" name="name" value="<?=h($item)?>">
            <button class="danger" type="submit">Delete</button>
        </form>
    </div>
</div>
<?php endforeach; ?>

<?php if (!$items): ?>
    <small>Folder kosong.</small>
<?php endif; ?>
</div>

</div>
</body>
</html>
