|
|
<?php |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
require_once __DIR__ . '/storage.php'; |
|
|
require_once __DIR__ . '/user-manager.php'; |
|
|
|
|
|
|
|
|
error_log("VvvebJs Save Request - Method: " . ($_SERVER['REQUEST_METHOD'] ?? 'unknown')); |
|
|
error_log("VvvebJs Save Request - Action: " . ($_GET['action'] ?? $_POST['action'] ?? 'none')); |
|
|
error_log("VvvebJs Save Request - Has HTML: " . (isset($_POST['html']) ? 'yes' : 'no')); |
|
|
error_log("VvvebJs Save Request - Has File: " . (isset($_POST['file']) ? 'yes' : 'no')); |
|
|
|
|
|
|
|
|
function checkAuth() { |
|
|
$userManager = new UserManager(); |
|
|
|
|
|
|
|
|
if ($userManager->isLoggedIn()) { |
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
error_log("VvvebJs Auth Failed - Session logged in: " . ($userManager->isLoggedIn() ? 'yes' : 'no')); |
|
|
error_log("VvvebJs Auth Failed - Session data: " . json_encode($_SESSION ?? [])); |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
header('Location: index.html'); |
|
|
exit; |
|
|
} |
|
|
|
|
|
|
|
|
$action = $_GET['action'] ?? $_POST['action'] ?? ''; |
|
|
|
|
|
|
|
|
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'"); |
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
$storageManager = new StorageManager(); |
|
|
|
|
|
define('MAX_FILE_LIMIT', 1024 * 1024 * 2); |
|
|
define('ALLOW_PHP', false); |
|
|
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/', |
|
|
]); |
|
|
|
|
|
function sanitizeFileName($file, $allowedExtension = 'html') { |
|
|
$basename = basename($file); |
|
|
$disallow = ['.htaccess', 'passwd']; |
|
|
if (in_array($basename, $disallow)) { |
|
|
showError('Filename not allowed!'); |
|
|
return ''; |
|
|
} |
|
|
|
|
|
|
|
|
$file = preg_replace('@\?.*$@' , '', preg_replace('@\.{2,}@' , '', preg_replace('@[^\/\\a-zA-Z0-9\-\._]@', '', $file))); |
|
|
|
|
|
if (!$file) { |
|
|
return ''; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$action = $_GET['action'] ?? $_POST['action'] ?? ''; |
|
|
if ($action === 'loadFile' || $action === 'listFiles') { |
|
|
|
|
|
if ($allowedExtension) { |
|
|
$file = preg_replace('/\.[^.]+$/', '', $file) . ".$allowedExtension"; |
|
|
} |
|
|
return $file; |
|
|
} |
|
|
|
|
|
|
|
|
$file = __DIR__ . DIRECTORY_SEPARATOR . $file; |
|
|
|
|
|
|
|
|
if ($allowedExtension) { |
|
|
$file = preg_replace('/\.[^.]+$/', '', $file) . ".$allowedExtension"; |
|
|
} |
|
|
return $file; |
|
|
} |
|
|
|
|
|
function showError($error) { |
|
|
|
|
|
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 (preg_match('@<\?php|<\? |<\?=|<\s*script\s*language\s*=\s*"\s*php\s*"\s*>@', $html)) { |
|
|
showError('PHP not allowed!'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (isset($_POST['file'])) { |
|
|
$file = sanitizeFileName($_POST['file']); |
|
|
} |
|
|
|
|
|
if ($action && $action !== '') { |
|
|
|
|
|
switch ($action) { |
|
|
case 'listFiles': |
|
|
|
|
|
try { |
|
|
$files = $storageManager->listFiles(); |
|
|
|
|
|
|
|
|
$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': |
|
|
|
|
|
$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 { |
|
|
|
|
|
$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': |
|
|
|
|
|
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) { |
|
|
|
|
|
$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': |
|
|
|
|
|
$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 { |
|
|
|
|
|
error_log("VvvebJs: No action specified, treating as save request"); |
|
|
error_log("VvvebJs Save Request Debug - POST data keys: " . implode(', ', array_keys($_POST))); |
|
|
|
|
|
|
|
|
if ($html) { |
|
|
if ($file) { |
|
|
|
|
|
$relativePath = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $file); |
|
|
$relativePath = str_replace('\\', '/', $relativePath); |
|
|
|
|
|
|
|
|
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()); |
|
|
|
|
|
|
|
|
$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); |
|
|
} |
|
|
|
|
|
|
|
|
$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); |
|
|
} |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
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)"; |
|
|
} |
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
|
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 { |
|
|
|
|
|
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!'); |
|
|
} |
|
|
} |
|
|
|