isLoggedIn()) { return true; } // Enhanced auth error logging error_log("VvvebJs Auth Failed - Session logged in: " . ($userManager->isLoggedIn() ? 'yes' : 'no')); error_log("VvvebJs Auth Failed - Session data: " . json_encode($_SESSION ?? [])); // For AJAX requests, return JSON error instead of redirect if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { header('Content-Type: application/json'); http_response_code(401); echo json_encode(['success' => false, 'message' => 'Authentication required', 'redirect' => 'index.html']); exit; } // No authentication found - redirect to index.html which is the login page header('Location: index.html'); exit; } // Check authentication $action = $_GET['action'] ?? $_POST['action'] ?? ''; // Enhanced debug logging for action detection error_log("VvvebJs Action Debug - GET action: " . ($_GET['action'] ?? 'none')); error_log("VvvebJs Action Debug - POST action: " . ($_POST['action'] ?? 'none')); error_log("VvvebJs Action Debug - Final action: '$action'"); // Special handling for auth check if ($action === 'checkAuth') { $userManager = new UserManager(); if ($userManager->isLoggedIn()) { http_response_code(200); echo json_encode(['authenticated' => true, 'user' => $userManager->getCurrentUser()]); } else { http_response_code(401); echo json_encode(['authenticated' => false]); } exit; } checkAuth(); // Initialize storage manager $storageManager = new StorageManager(); define('MAX_FILE_LIMIT', 1024 * 1024 * 2);//2 Megabytes max html file size define('ALLOW_PHP', false);//check if saved html contains php tag and don't save if not allowed define('ALLOWED_OEMBED_DOMAINS', [ 'https://www.youtube.com/', 'https://www.vimeo.com/', 'https://www.x.com/', 'https://x.com/', 'https://publish.twitter.com/', 'https://www.twitter.com/', 'https://www.reddit.com/', ]);//load urls only from allowed websites for oembed function sanitizeFileName($file, $allowedExtension = 'html') { $basename = basename($file); $disallow = ['.htaccess', 'passwd']; if (in_array($basename, $disallow)) { showError('Filename not allowed!'); return ''; } //sanitize, remove double dot .. and remove get parameters if any $file = preg_replace('@\?.*$@' , '', preg_replace('@\.{2,}@' , '', preg_replace('@[^\/\\a-zA-Z0-9\-\._]@', '', $file))); if (!$file) { return ''; } // For loadFile action, return the sanitized filename as-is for storage manager // Only add __DIR__ for local file operations $action = $_GET['action'] ?? $_POST['action'] ?? ''; if ($action === 'loadFile' || $action === 'listFiles') { // For external storage operations, just sanitize and add extension if ($allowedExtension) { $file = preg_replace('/\.[^.]+$/', '', $file) . ".$allowedExtension"; } return $file; } // For local file operations, add the full path $file = __DIR__ . DIRECTORY_SEPARATOR . $file; //allow only .html extension if ($allowedExtension) { $file = preg_replace('/\.[^.]+$/', '', $file) . ".$allowedExtension"; } return $file; } function showError($error) { // Enhanced error response for AJAX requests if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { header('Content-Type: application/json'); http_response_code(500); echo json_encode(['success' => false, 'message' => $error]); exit; } header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500); die($error); } function validOembedUrl($url) { foreach (ALLOWED_OEMBED_DOMAINS as $domain) { if (strpos($url, $domain) === 0) { return true; } } return false; } $html = ''; $file = ''; if (isset($_POST['startTemplateUrl']) && !empty($_POST['startTemplateUrl'])) { $startTemplateUrl = sanitizeFileName($_POST['startTemplateUrl']); $html = ''; if ($startTemplateUrl) { $html = file_get_contents($startTemplateUrl); } } else if (isset($_POST['html'])){ $html = substr($_POST['html'], 0, MAX_FILE_LIMIT); if (!ALLOW_PHP) { //if (strpos($html, '@', $html)) { showError('PHP not allowed!'); } } } if (isset($_POST['file'])) { $file = sanitizeFileName($_POST['file']); } if ($action && $action !== '') { //file manager actions, delete and rename switch ($action) { case 'listFiles': // List files for current user with enhanced error handling try { $files = $storageManager->listFiles(); // Enhanced debugging $github = StorageConfig::getGitHubConfig(); $debug = [ 'hasToken' => !empty($github['token']), 'owner' => $github['owner'], 'repo' => $github['repo'], 'branch' => $github['branch'], 'path' => $github['path'], 'user' => $storageManager->getCurrentUser(), 'userPath' => $storageManager->getUserPath() ]; error_log("GitHub List Files Debug: " . json_encode($debug)); error_log("Files found: " . count($files)); header('Content-Type: application/json'); echo json_encode([ 'success' => true, 'files' => $files, 'user' => $storageManager->getCurrentUser(), 'userPath' => $storageManager->getUserPath(), 'debug' => $debug ]); } catch (Exception $e) { error_log("GitHub List Files Error: " . $e->getMessage()); header('Content-Type: application/json'); echo json_encode([ 'success' => false, 'message' => 'Error loading files: ' . $e->getMessage(), 'user' => $storageManager->getCurrentUser(), 'files' => [] ]); } exit; case 'loadFile': // Load a specific file for current user $filename = sanitizeFileName($_GET['file'] ?? ''); error_log("VvvebJs LoadFile Debug - Original filename: " . ($_GET['file'] ?? '')); error_log("VvvebJs LoadFile Debug - Sanitized filename: " . $filename); if ($filename) { try { $content = $storageManager->getFile($filename); error_log("VvvebJs LoadFile Debug - Content length: " . ($content !== false ? strlen($content) : 'false')); if ($content !== false) { header('Content-Type: application/json'); echo json_encode([ 'success' => true, 'content' => $content, 'filename' => $filename ]); } else { // Enhanced error information $github = StorageConfig::getGitHubConfig(); $debugInfo = [ 'filename' => $filename, 'user' => $storageManager->getCurrentUser(), 'userPath' => $storageManager->getUserPath(), 'fullPath' => $github['path'] . $storageManager->getUserPath() . $filename, 'hasToken' => !empty($github['token']), 'repo' => $github['owner'] . '/' . $github['repo'] ]; error_log("VvvebJs LoadFile Error - Debug info: " . json_encode($debugInfo)); header('Content-Type: application/json'); echo json_encode([ 'success' => false, 'message' => 'File not found or access denied', 'debug' => $debugInfo ]); } } catch (Exception $e) { error_log("VvvebJs LoadFile Exception: " . $e->getMessage()); header('Content-Type: application/json'); echo json_encode([ 'success' => false, 'message' => 'Error loading file: ' . $e->getMessage(), 'filename' => $filename ]); } } else { header('Content-Type: application/json'); echo json_encode([ 'success' => false, 'message' => 'Invalid filename' ]); } exit; case 'checkAuth': // Check if user is authenticated header('Content-Type: application/json'); echo json_encode([ 'success' => true, 'user' => $storageManager->getCurrentUser(), 'authenticated' => true ]); exit; case 'rename': $newfile = sanitizeFileName($_POST['newfile']); if ($file && $newfile) { // For user isolation, we need to handle this through storage manager $content = $storageManager->getFile($file); if ($content !== false) { if ($storageManager->saveFile($newfile, $content)) { $storageManager->deleteFile($file); echo "File '$file' renamed to '$newfile'"; } else { showError("Error renaming file '$file' to '$newfile'"); } } else { showError("File '$file' not found"); } } break; case 'delete': if ($file) { if ($storageManager->deleteFile($file)) { echo "File '$file' deleted"; } else { showError("Error deleting file '$file'"); } } break; case 'saveReusable': //block or section $type = $_POST['type'] ?? false; $name = $_POST['name'] ?? false; $html = $_POST['html'] ?? false; if ($type && $name && $html) { $file = sanitizeFileName("$type/$name"); if ($file) { $dir = dirname($file); if (!is_dir($dir)) { echo "$dir folder does not exist\n"; if (mkdir($dir, 0777, true)) { echo "$dir folder was created\n"; } else { showError("Error creating folder '$dir'\n"); } } if (file_put_contents($file, $html)) { echo "File saved '$file'"; } else { showError("Error saving file '$file'\nPossible causes are missing write permission or incorrect file path!"); } } else { showError('Invalid filename!'); } } else { showError("Missing reusable element data!\n"); } break; case 'oembedProxy': $url = $_GET['url'] ?? ''; if (validOembedUrl($url)) { $options = array( 'http'=>array( 'method'=>"GET", 'header'=> 'User-Agent: ' . $_SERVER['HTTP_USER_AGENT'] . "\r\n" ) ); $context = stream_context_create($options); header('Content-Type: application/json'); echo file_get_contents($url, false, $context ); } else { showError('Invalid url!'); } break; } } else { // No action specified - this should be a save request error_log("VvvebJs: No action specified, treating as save request"); error_log("VvvebJs Save Request Debug - POST data keys: " . implode(', ', array_keys($_POST))); //save page if ($html) { if ($file) { // Extract relative filename for external storage $relativePath = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $file); $relativePath = str_replace('\\', '/', $relativePath); // Enhanced debug logging error_log("VvvebJs Save Debug - File: $relativePath"); error_log("VvvebJs Save Debug - HTML length: " . strlen($html)); error_log("VvvebJs Save Debug - Storage type: " . StorageConfig::getStorageType()); error_log("VvvebJs Save Debug - Current user: " . $storageManager->getCurrentUser()); // Try external storage first $externalSuccess = false; $externalError = ''; try { $externalSuccess = $storageManager->saveFile($relativePath, $html); if (!$externalSuccess) { $externalError = 'Storage manager returned false'; } } catch (Exception $e) { $externalError = $e->getMessage(); error_log("VvvebJs Save Error: " . $externalError); } // Also save locally as backup/cache $dir = dirname($file); $localSuccess = false; if (!is_dir($dir)) { if (mkdir($dir, 0777, true)) { error_log("VvvebJs: Created directory $dir"); } } if (is_dir($dir)) { $localSuccess = file_put_contents($file, $html); } // Enhanced JSON response for AJAX requests if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { header('Content-Type: application/json'); if ($externalSuccess || $localSuccess) { echo json_encode([ 'success' => true, 'message' => 'File saved successfully', 'file' => $relativePath, 'external_storage' => $externalSuccess, 'local_cache' => $localSuccess !== false, 'user' => $storageManager->getCurrentUser() ]); } else { $github = StorageConfig::getGitHubConfig(); echo json_encode([ 'success' => false, 'message' => 'Failed to save file', 'external_error' => $externalError, 'debug_info' => [ 'has_token' => !empty($github['token']), 'owner' => $github['owner'], 'repo' => $github['repo'], 'target_file' => $relativePath, 'user' => $storageManager->getCurrentUser() ] ]); } exit; } // Traditional text response for non-AJAX requests if ($externalSuccess || $localSuccess) { $message = "File saved '$file'"; if ($externalSuccess) { $message .= " āœ… (external storage SUCCESS)"; } else { $message .= " āŒ (external storage FAILED: $externalError)"; } if ($localSuccess) { $message .= " āœ… (local cache SUCCESS)"; } else { $message .= " āŒ (local cache FAILED)"; } // Additional GitHub debug info if (!$externalSuccess) { $github = StorageConfig::getGitHubConfig(); $message .= "\n\nšŸ” Debug Info:"; $message .= "\n- GitHub Token: " . (empty($github['token']) ? 'āŒ Missing' : 'āœ… Present'); $message .= "\n- GitHub Owner: " . ($github['owner'] ?: 'āŒ Missing'); $message .= "\n- GitHub Repo: " . ($github['repo'] ?: 'āŒ Missing'); $message .= "\n- GitHub Path: " . ($github['path'] ?: 'root'); $message .= "\n- Target file: " . $relativePath; } echo $message; } else { showError("Error saving file '$file'\nExternal: $externalError\nLocal: File write failed\nPossible causes are missing write permission or incorrect file path!"); } } else { // For AJAX requests, return JSON error if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { header('Content-Type: application/json'); echo json_encode(['success' => false, 'message' => 'Filename is empty!']); exit; } showError('Filename is empty!'); } } else { // For AJAX requests, return JSON error if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { header('Content-Type: application/json'); echo json_encode(['success' => false, 'message' => 'Html content is empty!']); exit; } showError('Html content is empty!'); } }