Crie seu Servidor em casa com Armbian





O Armbian é uma estrutura de software voltada para a criação e otimização de sistemas operacionais Linux em computadores de placa única (SBCs), como Raspberry Pi, Orange Pi e TV Boxes. Ele não é uma distribuição independente, mas sim uma versão altamente otimizada baseada no Debian ou Ubuntu.
O seu principal objetivo é unificar o mundo dos processadores ARM.


Como esses chips são muito fragmentados, o Armbian fornece um kernel estável e otimizado para centenas de modelos de placas diferentes, garantindo que tudo funcione com o máximo de desempenho e eficiência energética.

Ele é muito utilizado por entusiastas e profissionais de TI para transformar placas baratas em servidores dedicados, sistemas de mídia ou minicomputadores.
Para ver o Armbian em funcionamento e aprender como utilizá-lo na prática em um dispositivo compacto.

php
<?php
/**
 * Simple File Manager PRO
 * Único arquivo PHP - Gerenciador de arquivos seguro e moderno.
 * 
 * @author    Melhorias aplicadas sobre código base
 * @version   2.0
 * @license   MIT
 */

// ==================== CONFIGURAÇÕES ====================
$config = [
    'use_auth'          => true,                     // Exige login
    'auth_users'        => [                         // Usuário => hash de senha (password_hash)
        'admin' => '$2y$10$N9qo8uLOickgx2ZMRZoMy.MrqT4JqXzPxQN5h6ZRJ4nQyJ9Z/3Mou', // senha: admin
    ],
    'readonly_users'    => [],                       // Usuários apenas leitura
    'root_path'         => $_SERVER['DOCUMENT_ROOT'], // Pasta raiz (recomendado usar um subdiretório)
    'max_upload_size'   => 50 * 1024 * 1024,         // 50MB
    'show_hidden'       => false,                    // Mostrar arquivos .*
    'allowed_extensions'=> [],                       // [] = todos; ex: ['jpg','png','txt']
    'allow_zip'         => true,                     // Habilitar compactar/extrair ZIP
    'max_search_depth'  => 3,                        // Profundidade máxima da busca recursiva
    'login_attempts'    => 5,                        // Tentativas de login antes de bloquear (15min)
];

// ==================== INICIALIZAÇÃO ====================
if (session_status() === PHP_SESSION_NONE) session_start();

// Funções de segurança e utilitários
function safe_path($path, $root) {
    $path = str_replace('\\', '/', $path);
    $path = preg_replace('/\.\.\/|\.\.\\\\/', '', $path);
    $full = realpath($root . '/' . ltrim($path, '/'));
    if ($full === false || strpos($full, realpath($root)) !== 0) {
        return '';
    }
    return ltrim(substr($full, strlen(realpath($root))), '/');
}

function generate_csrf_token() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function verify_csrf_token($token) {
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}

function format_size($size) {
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $i = 0;
    while ($size >= 1024 && $i < count($units)-1) {
        $size /= 1024;
        $i++;
    }
    return round($size, 2) . ' ' . $units[$i];
}

function get_file_icon($ext) {
    $icons = [
        'folder' => '📁', 'php' => '🐘', 'html' => '🌐', 'css' => '🎨', 'js' => '⚡',
        'jpg' => '🖼️', 'png' => '🖼️', 'gif' => '🖼️', 'svg' => '🖼️', 'pdf' => '📄',
        'txt' => '📃', 'md' => '📃', 'zip' => '📦', 'rar' => '📦', 'tar' => '📦',
        'mp3' => '🎵', 'mp4' => '🎬', 'sql' => '🗄️', 'json' => '📊', 'xml' => '📊',
    ];
    return $icons[$ext] ?? '📄';
}

function is_text_file($file) {
    $text_exts = ['php','html','htm','css','js','txt','md','json','xml','sql','csv','ini','conf','log','sh','py','rb'];
    return in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), $text_exts);
}

function is_image_file($file) {
    return in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), ['jpg','jpeg','png','gif','bmp','svg','webp']);
}

// Autenticação com bloqueio temporário
function is_logged_in() {
    global $config;
    if (!$config['use_auth']) return true;
    return !empty($_SESSION['logged_in']) && $_SESSION['logged_in'] === true;
}

function login($user, $pass) {
    global $config;
    if (isset($_SESSION['login_attempts']) && $_SESSION['login_attempts'] >= $config['login_attempts']) {
        if (time() - $_SESSION['last_attempt'] < 900) return false; // 15 min
        $_SESSION['login_attempts'] = 0;
    }
    if (isset($config['auth_users'][$user]) && password_verify($pass, $config['auth_users'][$user])) {
        $_SESSION['logged_in'] = true;
        $_SESSION['username'] = $user;
        $_SESSION['login_attempts'] = 0;
        return true;
    }
    $_SESSION['login_attempts'] = ($_SESSION['login_attempts'] ?? 0) + 1;
    $_SESSION['last_attempt'] = time();
    return false;
}

function logout() {
    unset($_SESSION['logged_in'], $_SESSION['username'], $_SESSION['csrf_token']);
    session_destroy();
}

function is_readonly() {
    global $config;
    if (!is_logged_in()) return true;
    return in_array($_SESSION['username'], $config['readonly_users']);
}

// Processar login/logout
if (isset($_GET['logout'])) {
    logout();
    header('Location: ' . $_SERVER['PHP_SELF']);
    exit;
}
if (isset($_POST['login'])) {
    if (login($_POST['username'], $_POST['password'])) {
        header('Location: ' . $_SERVER['PHP_SELF']);
        exit;
    }
    $login_error = 'Usuário ou senha incorretos!';
}

// ==================== PROCESSAMENTO DE AÇÕES ====================
$current_path = isset($_GET['p']) ? safe_path($_GET['p'], $config['root_path']) : '';
$full_path = $config['root_path'] . '/' . $current_path;
$full_path = realpath($full_path) ?: $config['root_path'];
$message = '';
$error = '';

// CSRF check para ações POST
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && !verify_csrf_token($_POST['csrf_token'] ?? '')) {
    $error = 'Token de segurança inválido. Recarregue a página e tente novamente.';
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && is_logged_in() && !is_readonly()) {
    // Upload múltiplo
    if (isset($_FILES['uploads']) && $_POST['action'] === 'upload') {
        foreach ($_FILES['uploads']['error'] as $key => $err) {
            if ($err === UPLOAD_ERR_OK) {
                $name = basename($_FILES['uploads']['name'][$key]);
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                if (!empty($config['allowed_extensions']) && !in_array($ext, $config['allowed_extensions'])) {
                    $error .= "Extensão não permitida: $name<br>";
                    continue;
                }
                $target = $full_path . '/' . $name;
                if (move_uploaded_file($_FILES['uploads']['tmp_name'][$key], $target)) {
                    $message .= "Arquivo $name enviado com sucesso.<br>";
                } else {
                    $error .= "Falha ao enviar $name.<br>";
                }
            }
        }
    }
    // Criar pasta
    elseif (isset($_POST['new_folder']) && $_POST['action'] === 'mkdir') {
        $folder = safe_path($_POST['folder_name'], $config['root_path']);
        if ($folder && !file_exists($full_path . '/' . $folder)) {
            mkdir($full_path . '/' . $folder, 0755, true) ? $message = 'Pasta criada.' : $error = 'Erro ao criar pasta.';
        } else $error = 'Nome inválido ou já existe.';
    }
    // Criar arquivo
    elseif (isset($_POST['new_file']) && $_POST['action'] === 'touch') {
        $file = safe_path($_POST['file_name'], $config['root_path']);
        if ($file && !file_exists($full_path . '/' . $file)) {
            file_put_contents($full_path . '/' . $file, '') !== false ? $message = 'Arquivo criado.' : $error = 'Erro ao criar arquivo.';
        } else $error = 'Nome inválido ou já existe.';
    }
    // Renomear
    elseif (isset($_POST['old_name'], $_POST['new_name']) && $_POST['action'] === 'rename') {
        $old = $full_path . '/' . basename($_POST['old_name']);
        $new = $full_path . '/' . safe_path($_POST['new_name'], $config['root_path']);
        rename($old, $new) ? $message = 'Renomeado.' : $error = 'Erro ao renomear.';
    }
    // Copiar (suporta destino com caminho relativo)
    elseif (isset($_POST['source'], $_POST['dest']) && $_POST['action'] === 'copy') {
        $src = $full_path . '/' . basename($_POST['source']);
        $dest_full = $config['root_path'] . '/' . safe_path($_POST['dest'], $config['root_path']);
        if (is_dir($dest_full)) $dest_full .= '/' . basename($_POST['source']);
        copy($src, $dest_full) ? $message = 'Copiado.' : $error = 'Erro ao copiar.';
    }
    // Mover
    elseif (isset($_POST['source'], $_POST['dest']) && $_POST['action'] === 'move') {
        $src = $full_path . '/' . basename($_POST['source']);
        $dest_full = $config['root_path'] . '/' . safe_path($_POST['dest'], $config['root_path']);
        if (is_dir($dest_full)) $dest_full .= '/' . basename($_POST['source']);
        rename($src, $dest_full) ? $message = 'Movido.' : $error = 'Erro ao mover.';
    }
    // Compactar ZIP
    elseif (isset($_POST['zip_items']) && $_POST['action'] === 'zip' && $config['allow_zip'] && class_exists('ZipArchive')) {
        $zip = new ZipArchive();
        $zipname = 'backup_' . date('Ymd_His') . '.zip';
        $zippath = $full_path . '/' . $zipname;
        if ($zip->open($zippath, ZipArchive::CREATE) === true) {
            foreach ($_POST['items'] as $item) {
                $itemPath = $full_path . '/' . basename($item);
                if (is_file($itemPath)) $zip->addFile($itemPath, basename($item));
                elseif (is_dir($itemPath)) {
                    $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($itemPath, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
                    foreach ($files as $file) {
                        $zip->addFile($file->getPathname(), substr($file->getPathname(), strlen($full_path)+1));
                    }
                }
            }
            $zip->close();
            $message = "Arquivo ZIP criado: <a href='?action=download&p=".urlencode($current_path)."&item=".urlencode($zipname)."'>$zipname</a>";
        } else $error = 'Erro ao criar ZIP.';
    }
    // Extrair ZIP
    elseif (isset($_POST['extract_zip']) && $_POST['action'] === 'unzip' && $config['allow_zip'] && class_exists('ZipArchive')) {
        $zipfile = $full_path . '/' . basename($_POST['extract_zip']);
        $zip = new ZipArchive();
        if ($zip->open($zipfile) === true) {
            $zip->extractTo($full_path);
            $zip->close();
            $message = 'Arquivo extraído.';
        } else $error = 'Erro ao extrair ZIP.';
    }
    // Deletar
    elseif (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['item'])) {
        $item = $full_path . '/' . basename($_GET['item']);
        if (is_dir($item)) rmdir($item) ? $message = 'Pasta deletada.' : $error = 'Pasta não vazia.';
        else unlink($item) ? $message = 'Arquivo deletado.' : $error = 'Erro ao deletar.';
        header('Location: ?p=' . urlencode($current_path));
        exit;
    }
    // Salvar edição
    elseif (isset($_POST['save_file'], $_POST['file_path'], $_POST['content']) && $_POST['action'] === 'save') {
        $file = safe_path($_POST['file_path'], $config['root_path']);
        $full_file = $config['root_path'] . '/' . $file;
        if (file_put_contents($full_file, $_POST['content']) !== false) {
            $message = 'Arquivo salvo.';
            $edit_file = $file;
            $edit_content = $_POST['content'];
        } else $error = 'Erro ao salvar.';
    }
}

// Ações GET (download, view, edit, delete)
$action = $_GET['action'] ?? '';
if ($action === 'download' && isset($_GET['item']) && is_logged_in()) {
    $file = $full_path . '/' . basename($_GET['item']);
    if (is_file($file)) {
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . basename($file) . '"');
        header('Content-Length: ' . filesize($file));
        readfile($file);
        exit;
    }
}
if ($action === 'view' && isset($_GET['item']) && is_logged_in()) {
    $file = $full_path . '/' . basename($_GET['item']);
    if (is_file($file) && is_image_file($file)) {
        header('Content-Type: ' . mime_content_type($file));
        readfile($file);
        exit;
    }
}
if ($action === 'edit' && isset($_GET['item']) && is_logged_in()) {
    $file = $full_path . '/' . basename($_GET['item']);
    if (is_file($file) && is_text_file($file)) {
        $edit_file = $current_path . '/' . basename($_GET['item']);
        $edit_content = file_get_contents($file);
    }
}
if ($action === 'delete' && isset($_GET['item']) && is_logged_in() && !is_readonly()) {
    // Já tratado via POST, mas manter GET com confirmação
    echo "<script>if(confirm('Deletar permanentemente?')) location.href='?action=delete_confirm&p=".urlencode($current_path)."&item=".urlencode($_GET['item'])."'; else history.back();</script>";
    exit;
}

// Busca
$search_results = [];
if (isset($_GET['search']) && !empty($_GET['q'])) {
    $searchTerm = $_GET['q'];
    $depth = 0;
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config['root_path'], RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
    foreach ($iterator as $file) {
        if ($iterator->getDepth() > $config['max_search_depth']) continue;
        if (strpos($file->getFilename(), $searchTerm) !== false || strpos($file->getPathname(), $searchTerm) !== false) {
            $search_results[] = [
                'name' => $file->getFilename(),
                'path' => substr($file->getPathname(), strlen($config['root_path'])+1),
                'is_dir' => $file->isDir(),
                'size' => $file->isDir() ? '-' : format_size($file->getSize()),
            ];
        }
        if (count($search_results) > 200) break;
    }
}

// Listagem de arquivos/pastas (com ordenação)
$items = [];
if (is_dir($full_path)) {
    $dh = opendir($full_path);
    while (($file = readdir($dh)) !== false) {
        if ($file === '.' || $file === '..') continue;
        if (!$config['show_hidden'] && $file[0] === '.') continue;
        $item_path = $full_path . '/' . $file;
        $is_dir = is_dir($item_path);
        $ext = $is_dir ? 'folder' : strtolower(pathinfo($file, PATHINFO_EXTENSION));
        if (!empty($config['allowed_extensions']) && !$is_dir && !in_array($ext, $config['allowed_extensions'])) continue;
        $items[] = [
            'name' => $file,
            'is_dir' => $is_dir,
            'size' => $is_dir ? '-' : format_size(filesize($item_path)),
            'modified' => date('d/m/Y H:i', filemtime($item_path)),
            'perms' => substr(sprintf('%o', fileperms($item_path)), -4),
            'icon' => get_file_icon($ext),
        ];
    }
    closedir($dh);
    $order = $_GET['order'] ?? 'name';
    $dir = $_GET['dir'] ?? 'asc';
    usort($items, function($a, $b) use ($order, $dir) {
        if ($order === 'size') $cmp = ($a['size'] === '-' ? -1 : ($b['size'] === '-' ? 1 : (float)$a['size'] <=> (float)$b['size']));
        elseif ($order === 'modified') $cmp = strtotime($a['modified']) <=> strtotime($b['modified']);
        else $cmp = strcasecmp($a['name'], $b['name']);
        return $dir === 'asc' ? $cmp : -$cmp;
    });
}

// Breadcrumb
$breadcrumbs = [];
if ($current_path) {
    $parts = explode('/', $current_path);
    $path_build = '';
    foreach ($parts as $part) {
        if ($part) {
            $path_build .= ($path_build ? '/' : '') . $part;
            $breadcrumbs[] = ['name' => $part, 'path' => $path_build];
        }
    }
}

// Espaço em disco
$disk_free = disk_free_space($config['root_path']);
$disk_total = disk_total_space($config['root_path']);
$disk_used_percent = round((1 - $disk_free / $disk_total) * 100);
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple File Manager PRO</title>
    <style>
        * { margin:0; padding:0; box-sizing:border-box; }
        body { font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; background: var(--bg); color: var(--text); transition: background 0.2s; }
        :root { --bg:#0f172a; --surface:#1e293b; --border:#334155; --text:#e2e8f0; --text-muted:#94a3b8; --primary:#3b82f6; --primary-hover:#2563eb; --success:#10b981; --danger:#ef4444; --warning:#f59e0b; }
        .light { --bg:#f1f5f9; --surface:#ffffff; --border:#cbd5e1; --text:#0f172a; --text-muted:#475569; --primary:#3b82f6; --primary-hover:#2563eb; }
        .container { max-width:1400px; margin:0 auto; padding:20px; }
        .header { background: var(--surface); border-bottom:1px solid var(--border); padding:15px 20px; display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px; }
        .header h1 { font-size:1.5rem; color:var(--primary); display:flex; align-items:center; gap:10px; }
        .btn { padding:8px 16px; border:none; border-radius:6px; cursor:pointer; font-size:0.9rem; transition:0.2s; display:inline-flex; align-items:center; gap:6px; text-decoration:none; }
        .btn-primary { background:var(--primary); color:white; } .btn-primary:hover { background:var(--primary-hover); }
        .btn-success { background:var(--success); color:white; } .btn-success:hover { background:#059669; }
        .btn-danger { background:var(--danger); color:white; } .btn-danger:hover { background:#dc2626; }
        .btn-warning { background:var(--warning); color:white; } .btn-warning:hover { background:#d97706; }
        .btn-secondary { background:var(--text-muted); color:white; } .btn-secondary:hover { background:#475569; }
        .alert { padding:12px 16px; border-radius:6px; margin:15px 0; }
        .alert-success { background:#064e3b; color:#6ee7b7; border:1px solid #059669; }
        .alert-error { background:#450a0a; color:#fca5a5; border:1px solid #dc2626; }
        .breadcrumb { background:var(--surface); padding:10px 20px; border-bottom:1px solid var(--border); display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
        .breadcrumb a { color:var(--primary); text-decoration:none; } .breadcrumb a:hover { text-decoration:underline; }
        .toolbar { background:var(--surface); padding:15px 20px; border-bottom:1px solid var(--border); display:flex; gap:10px; flex-wrap:wrap; align-items:center; }
        .toolbar input, .toolbar select { background:var(--bg); border:1px solid var(--border); color:var(--text); padding:8px 12px; border-radius:6px; }
        .file-table { width:100%; border-collapse:collapse; background:var(--surface); border-radius:8px; overflow:auto; }
        .file-table th, .file-table td { padding:12px 16px; text-align:left; border-bottom:1px solid var(--border); }
        .file-table th { background:var(--bg); color:var(--text-muted); font-weight:600; cursor:pointer; user-select:none; }
        .file-table tr:hover { background:rgba(59,130,246,0.1); }
        .file-name { display:flex; align-items:center; gap:10px; color:var(--text); text-decoration:none; }
        .file-actions { display:flex; gap:5px; flex-wrap:wrap; }
        .modal-overlay { display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:1000; justify-content:center; align-items:center; }
        .modal-overlay.active { display:flex; }
        .modal { background:var(--surface); border-radius:12px; padding:25px; width:90%; max-width:500px; border:1px solid var(--border); }
        .editor-textarea { width:100%; min-height:500px; background:var(--bg); color:var(--text); border:1px solid var(--border); padding:16px; font-family:monospace; font-size:0.9rem; line-height:1.6; resize:vertical; }
        @media (max-width:768px) { .file-table th:nth-child(3), .file-table td:nth-child(3) { display:none; } }
        .theme-toggle { cursor:pointer; background:var(--surface); border:1px solid var(--border); border-radius:6px; padding:6px 10px; }
        .search-box { display:flex; gap:6px; margin-left:auto; }
        .disk-usage { font-size:0.8rem; color:var(--text-muted); }
    </style>
</head>
<body class="<?php echo isset($_COOKIE['theme']) && $_COOKIE['theme'] === 'light' ? 'light' : ''; ?>">
<script>
    function toggleTheme() {
        document.body.classList.toggle('light');
        const isLight = document.body.classList.contains('light');
        document.cookie = "theme=" + (isLight ? 'light' : 'dark') + ";path=/;max-age=31536000";
    }
</script>

<?php if (!is_logged_in() && $config['use_auth']): ?>
<div class="login-container" style="display:flex; justify-content:center; align-items:center; min-height:100vh;">
    <div class="modal" style="max-width:400px;">
        <h2 style="text-align:center; margin-bottom:20px;">📁 Simple File Manager</h2>
        <?php if (isset($login_error)) echo "<div class='alert alert-error'>$login_error</div>"; ?>
        <form method="POST">
            <div class="form-group" style="margin-bottom:15px;"><label>Usuário</label><input type="text" name="username" required autofocus style="width:100%; padding:10px;"></div>
            <div class="form-group" style="margin-bottom:20px;"><label>Senha</label><input type="password" name="password" required style="width:100%; padding:10px;"></div>
            <button type="submit" name="login" class="btn btn-primary" style="width:100%;">Entrar</button>
        </form>
        <p style="text-align:center; margin-top:20px; color:var(--text-muted);">Padrão: admin / admin</p>
    </div>
</div>
<?php else: ?>
<div class="header">
    <h1>📁 Simple File Manager PRO</h1>
    <div style="display:flex; gap:10px; align-items:center;">
        <span class="theme-toggle" onclick="toggleTheme()">🌓 Tema</span>
        <span>👤 <?= htmlspecialchars($_SESSION['username'] ?? 'guest') ?> <?= is_readonly() ? '(leitura)' : '' ?></span>
        <a href="?logout=1" class="btn btn-danger btn-sm">Sair</a>
    </div>
</div>

<div class="breadcrumb">
    <a href="?">🏠 Home</a>
    <?php foreach ($breadcrumbs as $crumb): ?> <span>/</span> <a href="?p=<?= urlencode($crumb['path']) ?>"><?= htmlspecialchars($crumb['name']) ?></a> <?php endforeach; ?>
</div>

<?php if ($message): ?><div class="container"><div class="alert alert-success"><?= $message ?></div></div><?php endif; ?>
<?php if ($error): ?><div class="container"><div class="alert alert-error"><?= $error ?></div></div><?php endif; ?>

<!-- Barra de ferramentas -->
<div class="toolbar">
    <?php if (!is_readonly()): ?>
    <form method="POST" enctype="multipart/form-data" style="display:flex; gap:6px;">
        <input type="file" name="uploads[]" multiple>
        <input type="hidden" name="action" value="upload">
        <input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
        <button type="submit" class="btn btn-success">📤 Upload</button>
    </form>
    <button onclick="showModal('folderModal')" class="btn btn-primary">📁 Nova Pasta</button>
    <button onclick="showModal('fileModal')" class="btn btn-primary">📄 Novo Arquivo</button>
    <?php endif; ?>
    
    <form class="search-box" method="GET">
        <input type="text" name="q" placeholder="Buscar..." value="<?= htmlspecialchars($_GET['q'] ?? '') ?>">
        <button type="submit" name="search" class="btn btn-secondary">🔍</button>
    </form>
    <div class="disk-usage">💾 Disco: <?= $disk_used_percent ?>% usado (<?= format_size($disk_total - $disk_free) ?> / <?= format_size($disk_total) ?>)</div>
</div>

<div class="container">
    <?php if (isset($_GET['search'])): ?>
        <h2>Resultados para "<?= htmlspecialchars($_GET['q']) ?>"</h2>
        <?php if (empty($search_results)): ?><p>Nenhum resultado.</p><?php else: ?>
        <table class="file-table"><thead><tr><th>Nome</th><th>Caminho</th><th>Tamanho</th></tr></thead><tbody>
        <?php foreach ($search_results as $res): ?>
        <tr><td><a href="?p=<?= urlencode(dirname($res['path'])) ?>" class="file-name"><?= $res['is_dir'] ? '📁' : '📄' ?> <?= htmlspecialchars($res['name']) ?></a></td>
        <td><?= htmlspecialchars($res['path']) ?></td><td><?= $res['size'] ?></td></tr>
        <?php endforeach; ?>
        </tbody></table>
        <?php endif; ?>
    <?php elseif (isset($edit_file)): ?>
        <!-- Editor de texto -->
        <div style="margin-bottom:15px;"><a href="?p=<?= urlencode($current_path) ?>" class="btn btn-secondary">⬅️ Voltar</a></div>
        <div class="editor-container">
            <h3>✏️ Editando: <?= htmlspecialchars(basename($edit_file)) ?></h3>
            <form method="POST">
                <input type="hidden" name="action" value="save">
                <input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
                <input type="hidden" name="file_path" value="<?= htmlspecialchars($edit_file) ?>">
                <textarea name="content" class="editor-textarea" spellcheck="false"><?= htmlspecialchars($edit_content) ?></textarea>
                <div class="editor-footer" style="padding:15px; text-align:right;">
                    <button type="submit" name="save_file" class="btn btn-success">💾 Salvar (Ctrl+S)</button>
                </div>
            </form>
        </div>
    <?php else: ?>
        <!-- Listagem de arquivos -->
        <?php if (empty($items) && $current_path === ''): ?><div class="empty-state" style="text-align:center; padding:60px;">📂 Nenhum arquivo encontrado. Faça upload!</div>
        <?php else: ?>
        <table class="file-table">
            <thead><tr>
                <th><a href="?p=<?= urlencode($current_path) ?>&order=name&dir=<?= ($_GET['order']=='name' && $_GET['dir']!='desc') ? 'desc' : 'asc' ?>">Nome</a></th>
                <th><a href="?p=<?= urlencode($current_path) ?>&order=size&dir=<?= ($_GET['order']=='size' && $_GET['dir']!='desc') ? 'desc' : 'asc' ?>">Tamanho</a></th>
                <th><a href="?p=<?= urlencode($current_path) ?>&order=modified&dir=<?= ($_GET['order']=='modified' && $_GET['dir']!='desc') ? 'desc' : 'asc' ?>">Modificado</a></th>
                <th>Ações</th>
            </tr></thead>
            <tbody>
                <?php if ($current_path): ?>
                <tr><td colspan="4"><a href="?p=<?= urlencode(dirname($current_path) === '.' ? '' : dirname($current_path)) ?>" class="file-name">⬆️ ..</a></td></tr>
                <?php endif; ?>
                <?php foreach ($items as $item): ?>
                <tr>
                    <td><?php if ($item['is_dir']): ?><a href="?p=<?= urlencode(($current_path ? $current_path . '/' : '') . $item['name']) ?>" class="file-name"><span class="file-icon"><?= $item['icon'] ?></span> <?= htmlspecialchars($item['name']) ?></a>
                        <?php else: ?><span class="file-name"><span class="file-icon"><?= $item['icon'] ?></span> <?= htmlspecialchars($item['name']) ?></span><?php endif; ?></td>
                    <td><?= $item['size'] ?></td>
                    <td><?= $item['modified'] ?></td>
                    <td class="file-actions">
                        <?php if (!$item['is_dir']): ?>
                            <a href="?action=download&p=<?= urlencode($current_path) ?>&item=<?= urlencode($item['name']) ?>" class="btn btn-secondary btn-sm">⬇️</a>
                            <?php if (is_text_file($full_path . '/' . $item['name'])): ?>
                                <a href="?action=edit&p=<?= urlencode($current_path) ?>&item=<?= urlencode($item['name']) ?>" class="btn btn-warning btn-sm">✏️</a>
                            <?php endif; ?>
                            <?php if (is_image_file($full_path . '/' . $item['name'])): ?>
                                <a href="?action=view&p=<?= urlencode($current_path) ?>&item=<?= urlencode($item['name']) ?>" target="_blank" class="btn btn-secondary btn-sm">👁️</a>
                            <?php endif; ?>
                        <?php endif; ?>
                        <?php if (!is_readonly()): ?>
                            <button onclick="renameItem('<?= addslashes($item['name']) ?>')" class="btn btn-secondary btn-sm">📝</button>
                            <button onclick="copyItem('<?= addslashes($item['name']) ?>')" class="btn btn-secondary btn-sm">📋</button>
                            <?php if ($config['allow_zip'] && class_exists('ZipArchive') && $item['is_dir']): ?>
                                <button onclick="zipItems(['<?= addslashes($item['name']) ?>'])" class="btn btn-secondary btn-sm">🗜️</button>
                            <?php endif; ?>
                            <a href="?action=delete&p=<?= urlencode($current_path) ?>&item=<?= urlencode($item['name']) ?>" onclick="return confirm('Deletar <?= addslashes($item['name']) ?>?')" class="btn btn-danger btn-sm">🗑️</a>
                        <?php endif; ?>
                    </td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
        <?php endif; ?>
    <?php endif; ?>
</div>

<!-- Modais -->
<div id="folderModal" class="modal-overlay"><div class="modal"><h3>📁 Nova Pasta</h3><form method="POST"><input type="hidden" name="action" value="mkdir"><input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>"><input type="text" name="folder_name" placeholder="nome" required><div class="modal-actions" style="margin-top:20px;"><button type="button" onclick="hideModal('folderModal')" class="btn btn-secondary">Cancelar</button><button type="submit" name="new_folder" class="btn btn-primary">Criar</button></div></form></div></div>
<div id="fileModal" class="modal-overlay"><div class="modal"><h3>📄 Novo Arquivo</h3><form method="POST"><input type="hidden" name="action" value="touch"><input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>"><input type="text" name="file_name" placeholder="arquivo.txt" required><div class="modal-actions" style="margin-top:20px;"><button type="button" onclick="hideModal('fileModal')" class="btn btn-secondary">Cancelar</button><button type="submit" name="new_file" class="btn btn-primary">Criar</button></div></form></div></div>
<div id="renameModal" class="modal-overlay"><div class="modal"><h3>📝 Renomear</h3><form method="POST"><input type="hidden" name="action" value="rename"><input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>"><input type="hidden" name="old_name" id="renameOld"><input type="text" name="new_name" id="renameNew" required><div class="modal-actions"><button type="button" onclick="hideModal('renameModal')" class="btn btn-secondary">Cancelar</button><button type="submit" class="btn btn-primary">Renomear</button></div></form></div></div>
<div id="copyModal" class="modal-overlay"><div class="modal"><h3>📋 Copiar/Mover</h3><form method="POST"><input type="hidden" name="action" id="copyAction" value="copy"><input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>"><input type="hidden" name="source" id="copySource"><input type="text" name="dest" id="copyDest" placeholder="destino (caminho relativo)" required><div class="modal-actions"><button type="button" onclick="hideModal('copyModal')" class="btn btn-secondary">Cancelar</button><button type="submit" id="copySubmit" class="btn btn-primary">Copiar</button><button type="button" onclick="document.getElementById('copyAction').value='move'; document.getElementById('copySubmit').innerText='Mover'" class="btn btn-warning">Mover</button></div></form></div></div>

<script>
function showModal(id) { document.getElementById(id).classList.add('active'); }
function hideModal(id) { document.getElementById(id).classList.remove('active'); }
function renameItem(name) { document.getElementById('renameOld').value = name; document.getElementById('renameNew').value = name; showModal('renameModal'); }
function copyItem(name) { document.getElementById('copySource').value = name; document.getElementById('copyDest').value = name; document.getElementById('copyAction').value = 'copy'; document.getElementById('copySubmit').innerText = 'Copiar'; showModal('copyModal'); }
function zipItems(items) { if(confirm('Compactar item(s) selecionado?')) { var form = document.createElement('form'); form.method = 'POST'; form.innerHTML = '<input type="hidden" name="action" value="zip"><input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>"><input type="hidden" name="items[]" value="'+items[0]+'">'; document.body.appendChild(form); form.submit(); } }
document.querySelectorAll('.modal-overlay').forEach(m => m.addEventListener('click', e => { if(e.target === m) hideModal(m.id); }));
document.addEventListener('keydown', e => { if(e.ctrlKey && e.key === 's') { let btn = document.querySelector('button[name="save_file"]'); if(btn) { e.preventDefault(); btn.click(); } } });
</script>
<?php endif; ?>
</body>
</html>


⚙️ Como usar

  1. Salve o código acima em /var/www/html/filemanager.php.

  2. Ajuste as permissões: sudo chown www-data:www-data /var/www/html/filemanager.php && sudo chmod 644 /var/www/html/filemanager.php

  3. Acesse http://seu-servidor/filemanager.php e faça login com:

    • Usuário: admin

    • Senha: admin

  4. Recomendação de segurança: Altere a senha imediatamente usando password_hash('sua_nova_senha', PASSWORD_DEFAULT) e substitua o hash no array $config['auth_users'].

🔒 Notas de segurança importantes

  • Este gerenciador dá acesso completo ao sistema de arquivos dentro da root_path. Para produção, restrinja root_path a uma pasta específica (ex: /var/www/html/files).

  • Sempre utilize HTTPS para evitar interceptação de credenciais.

  • Considere adicionar autenticação por IP ou .htaccess adicional.

  • Mantenha o PHP atualizado e desabilite funções perigosas se não forem necessárias (exec, shell_exec).

Reactions

Postar um comentário

0 Comentários