files.js 5.08 KB
const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');
const { isAuthenticated } = require('../middleware/auth');
const router = express.Router();

const PXE_ROOT = process.env.PXE_ROOT || '/var/www/html';
const LIVE_DIR = path.join(PXE_ROOT, 'debian12/live');
const BOOT_FILE = path.join(PXE_ROOT, 'boot.ipxe');

// Hapus semua file di direktori yang cocok dengan pattern prefix
// Contoh: hapus vmlinuz-6.1.0-anything, initrd.img-anything, dll
function deleteMatchingFiles(dir, prefix) {
  try {
    const allFiles = fs.readdirSync(dir);
    allFiles.forEach(f => {
      // Cocokkan file yang diawali prefix (case-insensitive)
      if (f.toLowerCase().startsWith(prefix.toLowerCase())) {
        const filePath = path.join(dir, f);
        fs.unlinkSync(filePath);
        console.log(`Dihapus: ${filePath}`);
      }
    });
  } catch(e) {
    console.error('Gagal hapus file lama:', e.message);
  }
}

// Halaman editor boot.ipxe
router.get('/boot-editor', isAuthenticated, (req, res) => {
  let content = '';
  let fileExists = false;
  try {
    content = fs.readFileSync(BOOT_FILE, 'utf8');
    fileExists = true;
  } catch(e) {
    content = '# File boot.ipxe tidak ditemukan atau tidak dapat dibaca\n# Path: ' + BOOT_FILE;
  }
  res.render('boot-editor', { content, fileExists, bootFile: BOOT_FILE, title: 'Boot Editor' });
});

// Simpan boot.ipxe
router.post('/boot-editor/save', isAuthenticated, (req, res) => {
  const { content } = req.body;
  try {
    if (fs.existsSync(BOOT_FILE)) {
      fs.copyFileSync(BOOT_FILE, BOOT_FILE + '.bak');
    }
    fs.writeFileSync(BOOT_FILE, content, 'utf8');
    req.flash('success', 'File boot.ipxe berhasil disimpan! Backup dibuat di boot.ipxe.bak');
  } catch(e) {
    req.flash('error', 'Gagal menyimpan: ' + e.message);
  }
  res.redirect('/files/boot-editor');
});

// Halaman upload file live
router.get('/upload', isAuthenticated, (req, res) => {
  // Cari file aktual di LIVE_DIR (bisa vmlinuz-6.1.0 dll)
  function findFile(dir, prefix) {
    try {
      const files = fs.readdirSync(dir);
      // Cari exact match dulu
      if (files.includes(prefix)) {
        const p = path.join(dir, prefix);
        const stat = fs.statSync(p);
        return { exists: true, actualName: prefix, size: stat.size, mtime: stat.mtime };
      }
      // Cari yang berawalan prefix
      const match = files.find(f => f.toLowerCase().startsWith(prefix.toLowerCase()));
      if (match) {
        const p = path.join(dir, match);
        const stat = fs.statSync(p);
        return { exists: true, actualName: match, size: stat.size, mtime: stat.mtime };
      }
    } catch(e) {}
    return { exists: false, actualName: null, size: 0, mtime: null };
  }

  const files = [
    { name: 'vmlinuz',             ...findFile(LIVE_DIR, 'vmlinuz') },
    { name: 'initrd.img',          ...findFile(LIVE_DIR, 'initrd') },
    { name: 'filesystem.squashfs', ...findFile(LIVE_DIR, 'filesystem') },
  ];

  res.render('upload', { files, liveDir: LIVE_DIR, title: 'Upload File' });
});

// Proses upload — hapus file lama dengan prefix apapun, simpan dengan nama standar
router.post('/upload', isAuthenticated, (req, res) => {
  // Map fieldname → { targetDir, deletePrefix, finalName }
  const fileConfig = {
    vmlinuz:  { dir: LIVE_DIR,  prefix: 'vmlinuz',     finalName: 'vmlinuz' },
    initrd:   { dir: LIVE_DIR,  prefix: 'initrd',      finalName: 'initrd.img' },
    squashfs: { dir: LIVE_DIR,  prefix: 'filesystem',  finalName: 'filesystem.squashfs' },
    boot_ipxe:{ dir: PXE_ROOT,  prefix: 'boot',        finalName: 'boot.ipxe' },
  };

  const upload = multer({
    storage: multer.diskStorage({
      destination: (req, file, cb) => {
        const cfg = fileConfig[file.fieldname];
        fs.mkdirSync(cfg.dir, { recursive: true });

        // Hapus semua file lama dengan prefix yang sama sebelum upload
        deleteMatchingFiles(cfg.dir, cfg.prefix);

        cb(null, cfg.dir);
      },
      filename: (req, file, cb) => {
        const cfg = fileConfig[file.fieldname];
        cb(null, cfg ? cfg.finalName : file.originalname);
      }
    }),
    limits: { fileSize: 4 * 1024 * 1024 * 1024 } // 4GB max untuk squashfs
  }).fields([
    { name: 'vmlinuz',   maxCount: 1 },
    { name: 'initrd',    maxCount: 1 },
    { name: 'squashfs',  maxCount: 1 },
    { name: 'boot_ipxe', maxCount: 1 },
  ]);

  upload(req, res, (err) => {
    if (err) {
      req.flash('error', 'Upload gagal: ' + err.message);
      return res.redirect('/files/upload');
    }

    const uploaded = req.files || {};
    const keys = Object.keys(uploaded);

    if (keys.length === 0) {
      req.flash('error', 'Tidak ada file yang dipilih untuk diupload');
      return res.redirect('/files/upload');
    }

    // Buat pesan sukses yang informatif
    const messages = keys.map(field => {
      const cfg = fileConfig[field];
      const original = uploaded[field][0].originalname;
      return `${original}${cfg.finalName}`;
    });

    req.flash('success', `Berhasil upload & rename: ${messages.join(' | ')}`);
    res.redirect('/files/upload');
  });
});

module.exports = router;