.sh"} optionally with a "warning" // {"ok":false,"error":"Invalid path"|"Not a directory: …"|"Invalid script path" // |"Not found: …"|"Could not resolve path" // |"Refusing to import from inside the Varaverk repo …" // |"Already in Custom Scripts."|"A script named \"…\" already exists …" // |"Copy failed"|"Copy verification failed — source left untouched" // |"Invalid request"} // // DEPENDS ON // include/config.php SCRIPTS_DIR, CUSTOM_SCRIPTS_DIR // api/script.php manages the scripts once they are here // ═══════════════════════════════════════════════════════════════════════════════════════════════ header('Content-Type: application/json'); header('Cache-Control: no-store, no-cache'); require_once dirname(__DIR__) . '/include/config.php'; // ── browse (GET): list subdirectories and .sh files at $path, rooted at / ───── if ($_SERVER['REQUEST_METHOD'] === 'GET' && ($_GET['action'] ?? '') === 'browse') { $path = trim($_GET['path'] ?? '/'); if (!preg_match('#^/[^\0]*$#', $path) || str_contains($path, '..')) { echo json_encode(['ok' => false, 'error' => 'Invalid path']); exit; } $clean = rtrim($path, '/') ?: '/'; if (!is_dir($clean)) { echo json_encode(['ok' => false, 'error' => 'Not a directory: ' . $clean]); exit; } $dirOut = shell_exec('find ' . escapeshellarg($clean) . ' -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort | head -300') ?: ''; $dirs = array_values(array_filter(array_map('trim', explode("\n", $dirOut)))); $fileOut = shell_exec('find ' . escapeshellarg($clean) . ' -maxdepth 1 -mindepth 1 -type f -iname "*.sh" 2>/dev/null | sort | head -300') ?: ''; $files = array_values(array_filter(array_map('trim', explode("\n", $fileOut)))); $parent = ($clean !== '/') ? (dirname($clean) ?: '/') : null; echo json_encode(['ok' => true, 'path' => $clean, 'dirs' => $dirs, 'files' => $files, 'parent' => $parent]); exit; } // ── import (POST): move the chosen .sh file into CUSTOM_SCRIPTS_DIR ─────────── if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'import') { $src = trim($_POST['path'] ?? ''); if (!preg_match('#^/[^\0]*\.sh$#i', $src) || str_contains($src, '..')) { echo json_encode(['ok' => false, 'error' => 'Invalid script path']); exit; } if (!is_file($src)) { echo json_encode(['ok' => false, 'error' => 'Not found: ' . $src]); exit; } $srcReal = realpath($src); if ($srcReal === false) { echo json_encode(['ok' => false, 'error' => 'Could not resolve path']); exit; } // Refuse to move a file out of the git-tracked repo — that would delete a // tracked file out from under git without a commit recording it. $repoReal = realpath(SCRIPTS_DIR); if ($repoReal && str_starts_with($srcReal, $repoReal . '/')) { echo json_encode(['ok' => false, 'error' => 'Refusing to import from inside the Varaverk repo — that would delete a git-tracked file.']); exit; } // Already there — nothing to do. $customReal = realpath(CUSTOM_SCRIPTS_DIR) ?: CUSTOM_SCRIPTS_DIR; if (str_starts_with($srcReal, rtrim($customReal, '/') . '/')) { echo json_encode(['ok' => false, 'error' => 'Already in Custom Scripts.']); exit; } if (!is_dir(CUSTOM_SCRIPTS_DIR)) mkdir(CUSTOM_SCRIPTS_DIR, 0755, true); $name = basename($srcReal); $dest = CUSTOM_SCRIPTS_DIR . '/' . $name; if (file_exists($dest)) { echo json_encode(['ok' => false, 'error' => "A script named \"$name\" already exists in Custom Scripts."]); exit; } // Copy across filesystems, verify, THEN delete the source — never remove the // only copy on a failed or partial copy. if (!copy($srcReal, $dest)) { @unlink($dest); echo json_encode(['ok' => false, 'error' => 'Copy failed']); exit; } if (filesize($srcReal) !== filesize($dest) || hash_file('sha256', $srcReal) !== hash_file('sha256', $dest)) { @unlink($dest); echo json_encode(['ok' => false, 'error' => 'Copy verification failed — source left untouched']); exit; } chmod($dest, 0755); if (!@unlink($srcReal)) { // Copied and verified but couldn't remove the original (permissions, read-only // mount). The script is usable from its new home either way — surface a warning // rather than failing the import outright. echo json_encode([ 'ok' => true, 'id' => 'Custom/' . $name, 'warning' => 'Imported, but could not delete the original at ' . $srcReal . ' — remove it manually.', ]); exit; } echo json_encode(['ok' => true, 'id' => 'Custom/' . $name]); exit; } echo json_encode(['ok' => false, 'error' => 'Invalid request']);