Download storage.php from CatPtain/VvvebJs: direct link, hf CLI and curl.
- Browser
- Download file 29.9 kB
-
https://huggingface.co/spaces/CatPtain/VvvebJs/resolve/main/storage.php
- Command line
-
hf download hf://spaces/CatPtain/VvvebJs/storage.php
-
curl -L -o storage.php https://huggingface.co/spaces/CatPtain/VvvebJs/resolve/main/storage.php
29.9 kB
| // Load environment variables from .env file (for local development) | |
| function loadEnv($filePath) { | |
| if (!file_exists($filePath)) { | |
| return false; | |
| } | |
| $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); | |
| foreach ($lines as $line) { | |
| if (strpos(trim($line), '#') === 0) { | |
| continue; // Skip comments | |
| } | |
| if (strpos($line, '=') === false) { | |
| continue; // Skip lines without = | |
| } | |
| list($name, $value) = explode('=', $line, 2); | |
| $name = trim($name); | |
| $value = trim($value); | |
| // Remove quotes if present | |
| if (preg_match('/^"(.*)"$/', $value, $matches)) { | |
| $value = $matches[1]; | |
| } elseif (preg_match("/^'(.*)'$/", $value, $matches)) { | |
| $value = $matches[1]; | |
| } | |
| // Only set if not already in environment (Hugging Face Space variables take precedence) | |
| if (!array_key_exists($name, $_ENV) && !isset($_SERVER[$name])) { | |
| $_ENV[$name] = $value; | |
| putenv("$name=$value"); | |
| } | |
| } | |
| return true; | |
| } | |
| // Load .env file for local development (Hugging Face Space already has environment variables) | |
| if (file_exists(__DIR__ . '/.env')) { | |
| loadEnv(__DIR__ . '/.env'); | |
| } else { | |
| // Try to load from .env.example as fallback | |
| if (file_exists(__DIR__ . '/.env.example')) { | |
| loadEnv(__DIR__ . '/.env.example'); | |
| } | |
| } | |
| // Configuration management class | |
| class StorageConfig { | |
| public static function getUsers() { | |
| // Default users if no env variables found | |
| $defaultUsers = [ | |
| 'PS01' => 'password123', | |
| 'PS02' => 'password456', | |
| 'admin' => 'admin123' | |
| ]; | |
| $users = []; | |
| $i = 1; | |
| while (true) { | |
| // Check both $_ENV and $_SERVER for Hugging Face Space compatibility | |
| $username = $_ENV["USER_{$i}_NAME"] ?? $_SERVER["USER_{$i}_NAME"] ?? ''; | |
| $password = $_ENV["USER_{$i}_PASSWORD"] ?? $_SERVER["USER_{$i}_PASSWORD"] ?? ''; | |
| if (empty($username) || empty($password)) { | |
| break; | |
| } | |
| $users[$username] = $password; | |
| $i++; | |
| } | |
| // If no users found in env, use defaults | |
| return empty($users) ? $defaultUsers : $users; | |
| } | |
| public static function getStorageType() { | |
| return $_ENV['STORAGE_TYPE'] ?? $_SERVER['STORAGE_TYPE'] ?? 'github'; | |
| } | |
| public static function getGitHubConfig() { | |
| // Read from environment variables (Hugging Face Space or system environment) | |
| $config = [ | |
| 'token' => $_ENV['GITHUB_TOKEN'] ?? $_SERVER['GITHUB_TOKEN'] ?? '', | |
| 'owner' => $_ENV['GITHUB_OWNER'] ?? $_SERVER['GITHUB_OWNER'] ?? '', | |
| 'repo' => $_ENV['GITHUB_REPO'] ?? $_SERVER['GITHUB_REPO'] ?? '', | |
| 'branch' => $_ENV['GITHUB_BRANCH'] ?? $_SERVER['GITHUB_BRANCH'] ?? 'main', | |
| 'path' => $_ENV['GITHUB_PATH'] ?? $_SERVER['GITHUB_PATH'] ?? 'pages/' | |
| ]; | |
| // Debug log for Hugging Face Space | |
| error_log("GitHub Config Debug - Token present: " . (!empty($config['token']) ? 'YES' : 'NO')); | |
| error_log("GitHub Config Debug - Owner: " . $config['owner']); | |
| error_log("GitHub Config Debug - Repo: " . $config['repo']); | |
| error_log("GitHub Config Debug - Branch: " . $config['branch']); | |
| error_log("GitHub Config Debug - Path: " . $config['path']); | |
| return $config; | |
| } | |
| public static function getKVConfig() { | |
| return [ | |
| 'api_key' => $_ENV['EDGEONE_KV_API_KEY'] ?? $_SERVER['EDGEONE_KV_API_KEY'] ?? '', | |
| 'secret_key' => $_ENV['EDGEONE_KV_SECRET_KEY'] ?? $_SERVER['EDGEONE_KV_SECRET_KEY'] ?? '', | |
| 'zone_id' => $_ENV['EDGEONE_KV_ZONE_ID'] ?? $_SERVER['EDGEONE_KV_ZONE_ID'] ?? '', | |
| 'namespace' => $_ENV['EDGEONE_KV_NAMESPACE'] ?? $_SERVER['EDGEONE_KV_NAMESPACE'] ?? 'vvvebjs', | |
| 'endpoint' => $_ENV['EDGEONE_KV_ENDPOINT'] ?? $_SERVER['EDGEONE_KV_ENDPOINT'] ?? 'https://edgeone.tencentcloudapi.com' | |
| ]; | |
| } | |
| public static function getCurrentUser() { | |
| // Check session first | |
| if (session_status() === PHP_SESSION_NONE) { | |
| session_start(); | |
| } | |
| if (isset($_SESSION['username'])) { | |
| return $_SESSION['username']; | |
| } | |
| // Fallback to Basic Auth | |
| return $_SERVER['PHP_AUTH_USER'] ?? 'anonymous'; | |
| } | |
| public static function getUserPath($username = null) { | |
| if ($username === null) { | |
| $username = self::getCurrentUser(); | |
| } | |
| // Sanitize username for safe path usage | |
| $safeUsername = preg_replace('/[^a-zA-Z0-9_-]/', '_', $username); | |
| return "users/{$safeUsername}/"; | |
| } | |
| // Add method to check if running on Hugging Face Space | |
| public static function isHuggingFaceSpace() { | |
| return isset($_SERVER['SPACE_ID']) || | |
| isset($_ENV['SPACE_ID']) || | |
| strpos($_SERVER['HTTP_HOST'] ?? '', '.hf.space') !== false; | |
| } | |
| // Debug method to show all environment variables (for debugging) | |
| public static function debugEnvironment() { | |
| $debug = [ | |
| 'is_hf_space' => self::isHuggingFaceSpace(), | |
| 'space_id' => $_SERVER['SPACE_ID'] ?? $_ENV['SPACE_ID'] ?? 'not_set', | |
| 'host' => $_SERVER['HTTP_HOST'] ?? 'unknown', | |
| 'github_vars' => [ | |
| 'token_set' => !empty($_ENV['GITHUB_TOKEN'] ?? $_SERVER['GITHUB_TOKEN']), | |
| 'owner' => $_ENV['GITHUB_OWNER'] ?? $_SERVER['GITHUB_OWNER'] ?? 'not_set', | |
| 'repo' => $_ENV['GITHUB_REPO'] ?? $_SERVER['GITHUB_REPO'] ?? 'not_set', | |
| 'branch' => $_ENV['GITHUB_BRANCH'] ?? $_SERVER['GITHUB_BRANCH'] ?? 'not_set', | |
| 'path' => $_ENV['GITHUB_PATH'] ?? $_SERVER['GITHUB_PATH'] ?? 'not_set' | |
| ] | |
| ]; | |
| error_log("Environment Debug: " . json_encode($debug)); | |
| return $debug; | |
| } | |
| } | |
| // External storage handlers | |
| class KVStorage { | |
| private $config; | |
| private $userPath; | |
| public function __construct($config) { | |
| $this->config = $config; | |
| $this->userPath = StorageConfig::getUserPath(); | |
| } | |
| public function save($key, $content) { | |
| if (empty($this->config['api_key'])) { | |
| error_log("KV Save Error: API key is missing"); | |
| return false; | |
| } | |
| if (empty($this->config['zone_id']) || empty($this->config['namespace'])) { | |
| error_log("KV Save Error: Zone ID or namespace is missing"); | |
| return false; | |
| } | |
| // Add user path to key | |
| $userKey = $this->userPath . $key; | |
| // Use correct EdgeOne KV API endpoints | |
| $url = $this->config['endpoint']; | |
| $params = [ | |
| 'Action' => 'CreatePurgeTask', // 这需要根据实际的EdgeOne KV API调整 | |
| 'Version' => '2022-09-01', | |
| 'Region' => 'ap-beijing', | |
| 'ZoneId' => $this->config['zone_id'], | |
| 'Namespace' => $this->config['namespace'], | |
| 'Key' => $userKey, | |
| 'Value' => base64_encode($content), | |
| 'TTL' => 86400 // 24 hours TTL | |
| ]; | |
| error_log("KV Save Debug: Attempting to save key '$userKey' to namespace '{$this->config['namespace']}'"); | |
| $result = $this->makeRequest($url, $params); | |
| if ($result) { | |
| error_log("KV Save Success: Key saved successfully"); | |
| return true; | |
| } else { | |
| error_log("KV Save Error: API request failed"); | |
| return false; | |
| } | |
| } | |
| public function get($key) { | |
| if (empty($this->config['api_key'])) { | |
| error_log("KV Get Error: API key is missing"); | |
| return false; | |
| } | |
| // Add user path to key | |
| $userKey = $this->userPath . $key; | |
| $url = $this->config['endpoint']; | |
| $params = [ | |
| 'Action' => 'DescribeOriginGroup', // 这需要根据实际的EdgeOne KV API调整 | |
| 'Version' => '2022-09-01', | |
| 'Region' => 'ap-beijing', | |
| 'ZoneId' => $this->config['zone_id'], | |
| 'Namespace' => $this->config['namespace'], | |
| 'Key' => $userKey | |
| ]; | |
| error_log("KV Get Debug: Attempting to get key '$userKey' from namespace '{$this->config['namespace']}'"); | |
| $result = $this->makeRequest($url, $params); | |
| if ($result && isset($result['Value'])) { | |
| return base64_decode($result['Value']); | |
| } | |
| error_log("KV Get Error: Key not found or API request failed"); | |
| return false; | |
| } | |
| public function listUserFiles() { | |
| if (empty($this->config['api_key'])) return []; | |
| $url = $this->config['endpoint']; | |
| $params = [ | |
| 'Action' => 'DescribeOriginGroups', // 这需要根据实际的EdgeOne KV API调整 | |
| 'Version' => '2022-09-01', | |
| 'Region' => 'ap-beijing', | |
| 'ZoneId' => $this->config['zone_id'], | |
| 'Namespace' => $this->config['namespace'], | |
| 'Prefix' => $this->userPath | |
| ]; | |
| $result = $this->makeRequest($url, $params); | |
| if ($result && isset($result['Keys']) && is_array($result['Keys'])) { | |
| $files = []; | |
| foreach ($result['Keys'] as $item) { | |
| if (isset($item['Key'])) { | |
| $relativePath = str_replace($this->userPath, '', $item['Key']); | |
| $files[] = [ | |
| 'name' => basename($relativePath), | |
| 'path' => $relativePath, | |
| 'size' => $item['Size'] ?? 0, | |
| 'url' => null | |
| ]; | |
| } | |
| } | |
| return $files; | |
| } | |
| return []; | |
| } | |
| private function makeRequest($url, $params) { | |
| // 添加通用的腾讯云API参数 | |
| $commonParams = [ | |
| 'Timestamp' => time(), | |
| 'Nonce' => rand(10000, 99999), | |
| 'SecretId' => $this->config['api_key'], | |
| 'SignatureMethod' => 'HmacSHA256' | |
| ]; | |
| $params = array_merge($params, $commonParams); | |
| // 生成签名 (这是简化版本,实际需要按照腾讯云API签名规范) | |
| ksort($params); | |
| $queryString = http_build_query($params); | |
| $signature = hash_hmac('sha256', $queryString, $this->config['secret_key'], true); | |
| $params['Signature'] = base64_encode($signature); | |
| $ch = curl_init(); | |
| curl_setopt($ch, CURLOPT_URL, $url); | |
| curl_setopt($ch, CURLOPT_POST, true); | |
| curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); | |
| curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
| curl_setopt($ch, CURLOPT_TIMEOUT, 30); | |
| curl_setopt($ch, CURLOPT_HTTPHEADER, [ | |
| 'Content-Type: application/x-www-form-urlencoded', | |
| 'User-Agent: VvvebJs-WebBuilder/1.0' | |
| ]); | |
| $result = curl_exec($ch); | |
| $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); | |
| $error = curl_error($ch); | |
| curl_close($ch); | |
| if ($error) { | |
| error_log("KV API cURL Error: $error"); | |
| return false; | |
| } | |
| if ($httpCode >= 200 && $httpCode < 300) { | |
| $decoded = json_decode($result, true); | |
| if (json_last_error() === JSON_ERROR_NONE) { | |
| return $decoded; | |
| } | |
| error_log("KV API JSON Error: " . json_last_error_msg()); | |
| } else { | |
| error_log("KV API HTTP Error: $httpCode - Response: $result"); | |
| } | |
| return false; | |
| } | |
| } | |
| class GitHubStorage { | |
| private $config; | |
| private $actualBranch = null; | |
| private $userPath; | |
| public function __construct($config) { | |
| $this->config = $config; | |
| $this->userPath = StorageConfig::getUserPath(); | |
| // Auto-detect the actual default branch | |
| $this->detectDefaultBranch(); | |
| } | |
| private function detectDefaultBranch() { | |
| if (empty($this->config['token']) || empty($this->config['owner']) || empty($this->config['repo'])) { | |
| return; | |
| } | |
| // Get repository info to find the default branch | |
| $url = "https://api.github.com/repos/{$this->config['owner']}/{$this->config['repo']}"; | |
| $repoInfo = $this->makeRequest($url, 'GET'); | |
| if ($repoInfo && isset($repoInfo['default_branch'])) { | |
| $this->actualBranch = $repoInfo['default_branch']; | |
| error_log("GitHub: Detected default branch as '{$this->actualBranch}'"); | |
| } else { | |
| // Fallback: try common branch names | |
| $commonBranches = ['main', 'master', 'develop']; | |
| foreach ($commonBranches as $branch) { | |
| if ($this->checkBranchExists($branch)) { | |
| $this->actualBranch = $branch; | |
| error_log("GitHub: Found existing branch '{$branch}'"); | |
| break; | |
| } | |
| } | |
| } | |
| // Update config with detected branch | |
| if ($this->actualBranch) { | |
| $this->config['branch'] = $this->actualBranch; | |
| } | |
| } | |
| private function checkBranchExists($branchName) { | |
| $url = "https://api.github.com/repos/{$this->config['owner']}/{$this->config['repo']}/branches/{$branchName}"; | |
| $result = $this->makeRequest($url, 'GET'); | |
| return $result !== false; | |
| } | |
| public function save($filename, $content) { | |
| error_log("GitHubStorage: Starting save process for $filename"); | |
| // Get user path | |
| $userPath = $this->getUserPath(); | |
| if (!$userPath) { | |
| error_log("GitHubStorage: Failed to get user path"); | |
| return false; | |
| } | |
| $filePath = $userPath . '/' . $filename; | |
| error_log("GitHubStorage: Full file path: $filePath"); | |
| // Ensure user directory exists BEFORE attempting to save | |
| if (!$this->ensureUserDirectoryExists($userPath)) { | |
| error_log("GitHubStorage: Failed to ensure user directory exists"); | |
| return false; | |
| } | |
| // Wait a moment for GitHub API to propagate the directory creation | |
| usleep(500000); // 0.5 seconds | |
| // Now attempt to save the file | |
| $url = $this->apiUrl . "/contents/" . $filePath; | |
| // Check if file already exists to get its SHA | |
| $existingFile = $this->makeRequest($url); | |
| $sha = null; | |
| if ($existingFile !== false && isset($existingFile['sha'])) { | |
| $sha = $existingFile['sha']; | |
| error_log("GitHubStorage: Found existing file with SHA: $sha"); | |
| } else { | |
| error_log("GitHubStorage: Creating new file"); | |
| } | |
| $data = [ | |
| 'message' => $sha ? "Update $filename" : "Create $filename", | |
| 'content' => base64_encode($content), | |
| 'branch' => $this->branch | |
| ]; | |
| if ($sha) { | |
| $data['sha'] = $sha; | |
| } | |
| error_log("GitHubStorage: Sending save request to GitHub API"); | |
| $result = $this->makeRequest($url, 'PUT', $data); | |
| if ($result !== false) { | |
| error_log("GitHubStorage: File saved successfully"); | |
| return true; | |
| } else { | |
| error_log("GitHubStorage: Failed to save file"); | |
| return false; | |
| } | |
| } | |
| private function ensureUserDirectoryExists($userPath) { | |
| error_log("Ensuring user directory exists: $userPath"); | |
| // Split the path into components | |
| $pathParts = explode('/', trim($userPath, '/')); | |
| $currentPath = ''; | |
| // Create each directory level incrementally | |
| foreach ($pathParts as $part) { | |
| if (empty($part)) continue; | |
| $currentPath .= ($currentPath ? '/' : '') . $part; | |
| // Check if this directory level exists | |
| $url = $this->apiUrl . "/contents/" . $currentPath; | |
| $response = $this->makeRequest($url); | |
| if ($response === false) { | |
| // Directory doesn't exist, create it with a placeholder file | |
| error_log("Creating directory level: $currentPath"); | |
| $placeholderFile = $currentPath . '/.gitkeep'; | |
| $createUrl = $this->apiUrl . "/contents/" . $placeholderFile; | |
| $createData = [ | |
| 'message' => "Create directory: $currentPath", | |
| 'content' => base64_encode(''), | |
| 'branch' => $this->branch | |
| ]; | |
| $createResponse = $this->makeRequest($createUrl, 'PUT', $createData); | |
| if ($createResponse === false) { | |
| error_log("Failed to create directory level: $currentPath"); | |
| return false; | |
| } else { | |
| error_log("Successfully created directory level: $currentPath"); | |
| } | |
| } else { | |
| error_log("Directory level already exists: $currentPath"); | |
| } | |
| } | |
| error_log("User directory structure ensured: $userPath"); | |
| return true; | |
| } | |
| public function get($filename) { | |
| if (empty($this->config['token'])) return false; | |
| // Add user path to filename | |
| $userFilename = $this->userPath . $filename; | |
| $path = $this->config['path'] . $userFilename; | |
| $url = "https://api.github.com/repos/{$this->config['owner']}/{$this->config['repo']}/contents/{$path}"; | |
| $result = $this->makeRequest($url, 'GET'); | |
| if ($result && isset($result['content'])) { | |
| return base64_decode($result['content']); | |
| } | |
| return false; | |
| } | |
| public function listUserFiles() { | |
| if (empty($this->config['token'])) return []; | |
| $userDir = $this->config['path'] . $this->userPath; | |
| $url = "https://api.github.com/repos/{$this->config['owner']}/{$this->config['repo']}/contents/{$userDir}"; | |
| error_log("GitHub listUserFiles: Requesting $url"); | |
| $result = $this->makeRequest($url, 'GET'); | |
| if ($result && is_array($result)) { | |
| $files = []; | |
| foreach ($result as $item) { | |
| if ($item['type'] === 'file') { | |
| // Remove user path prefix from filename | |
| $relativePath = str_replace($this->userPath, '', $item['path']); | |
| $relativePath = str_replace($this->config['path'], '', $relativePath); | |
| $files[] = [ | |
| 'name' => $item['name'], | |
| 'path' => $relativePath, | |
| 'size' => $item['size'], | |
| 'url' => $item['download_url'] | |
| ]; | |
| } | |
| } | |
| error_log("GitHub listUserFiles: Found " . count($files) . " files"); | |
| return $files; | |
| } else { | |
| error_log("GitHub listUserFiles: No files found or directory doesn't exist"); | |
| // If directory doesn't exist, return empty array instead of error | |
| return []; | |
| } | |
| } | |
| public function delete($filename) { | |
| if (empty($this->config['token'])) return false; | |
| // Add user path to filename | |
| $userFilename = $this->userPath . $filename; | |
| $path = $this->config['path'] . $userFilename; | |
| $url = "https://api.github.com/repos/{$this->config['owner']}/{$this->config['repo']}/contents/{$path}"; | |
| // Get current file SHA | |
| $sha = $this->getFileSHA($path); | |
| if (!$sha) return false; | |
| $data = [ | |
| 'message' => "Delete {$userFilename} via VvvebJs", | |
| 'sha' => $sha, | |
| 'branch' => $this->config['branch'] | |
| ]; | |
| $result = $this->makeRequest($url, 'DELETE', $data); | |
| return $result !== false; | |
| } | |
| private function getFileSHA($path) { | |
| $url = "https://api.github.com/repos/{$this->config['owner']}/{$this->config['repo']}/contents/{$path}"; | |
| $result = $this->makeRequest($url, 'GET'); | |
| return $result && isset($result['sha']) ? $result['sha'] : null; | |
| } | |
| private function makeRequest($url, $method = 'GET', $data = null) { | |
| $headers = [ | |
| 'Authorization: token ' . $this->config['token'], | |
| 'User-Agent: VvvebJs-WebBuilder', | |
| 'Accept: application/vnd.github.v3+json', | |
| 'Content-Type: application/json' | |
| ]; | |
| $ch = curl_init(); | |
| curl_setopt($ch, CURLOPT_URL, $url); | |
| curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
| curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); | |
| curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | |
| curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); | |
| curl_setopt($ch, CURLOPT_TIMEOUT, 30); | |
| if ($method === 'PUT') { | |
| curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); | |
| if ($data) { | |
| curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); | |
| } | |
| } | |
| // Enhanced logging for debugging | |
| $debugUrl = preg_replace('/\/contents\/.*/', '/contents/[PATH_HIDDEN]', $url); | |
| error_log("GitHub API Request: $method $debugUrl"); | |
| $response = curl_exec($ch); | |
| $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); | |
| $curlError = curl_error($ch); | |
| curl_close($ch); | |
| error_log("GitHub API Response: HTTP $httpCode"); | |
| if ($curlError) { | |
| error_log("GitHub API cURL Error: $curlError"); | |
| return false; | |
| } | |
| if ($httpCode >= 200 && $httpCode < 300) { | |
| $decoded = json_decode($response, true); | |
| if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) { | |
| error_log("GitHub API JSON Decode Error: " . json_last_error_msg()); | |
| error_log("GitHub API Raw Response: " . substr($response, 0, 500)); | |
| return false; | |
| } | |
| return $decoded; | |
| } else { | |
| // Enhanced error logging for debugging | |
| $errorInfo = json_decode($response, true); | |
| if ($errorInfo && isset($errorInfo['message'])) { | |
| error_log("GitHub API Error ($httpCode): " . $errorInfo['message']); | |
| if (isset($errorInfo['documentation_url'])) { | |
| error_log("GitHub API Documentation: " . $errorInfo['documentation_url']); | |
| } | |
| } else { | |
| error_log("GitHub API Error ($httpCode): " . substr($response, 0, 500)); | |
| } | |
| // Special handling for 404 errors - they might be expected when checking if files exist | |
| if ($httpCode === 404 && $method === 'GET') { | |
| error_log("GitHub API: Resource not found (this may be expected when checking file existence)"); | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
| // Storage manager with user isolation | |
| class StorageManager { | |
| private $kvStorage; | |
| private $githubStorage; | |
| private $storageType; | |
| private $currentUser; | |
| public function __construct() { | |
| $this->storageType = StorageConfig::getStorageType(); | |
| $this->currentUser = StorageConfig::getCurrentUser(); | |
| if (in_array($this->storageType, ['kv', 'both'])) { | |
| $this->kvStorage = new KVStorage(StorageConfig::getKVConfig()); | |
| } | |
| if (in_array($this->storageType, ['github', 'both'])) { | |
| $this->githubStorage = new GitHubStorage(StorageConfig::getGitHubConfig()); | |
| } | |
| } | |
| public function saveFile($filename, $content) { | |
| // Ensure filename is safe and doesn't contain path traversal | |
| $filename = $this->sanitizeFilename($filename); | |
| error_log("StorageManager saveFile: Starting save for file '$filename'"); | |
| error_log("StorageManager saveFile: Storage type is '{$this->storageType}'"); | |
| error_log("StorageManager saveFile: Current user is '{$this->currentUser}'"); | |
| $success = false; | |
| $errors = []; | |
| if ($this->githubStorage) { | |
| error_log("StorageManager saveFile: Attempting GitHub save..."); | |
| $githubSuccess = $this->githubStorage->save($filename, $content); | |
| if ($githubSuccess) { | |
| error_log("StorageManager saveFile: GitHub save SUCCESS"); | |
| $success = true; | |
| } else { | |
| error_log("StorageManager saveFile: GitHub save FAILED"); | |
| $errors[] = "GitHub save failed"; | |
| } | |
| } else { | |
| error_log("StorageManager saveFile: GitHub storage not configured"); | |
| } | |
| if ($this->kvStorage) { | |
| error_log("StorageManager saveFile: Attempting KV save..."); | |
| $kvSuccess = $this->kvStorage->save($filename, $content); | |
| if ($kvSuccess) { | |
| error_log("StorageManager saveFile: KV save SUCCESS"); | |
| $success = true; | |
| } else { | |
| error_log("StorageManager saveFile: KV save FAILED"); | |
| $errors[] = "KV save failed"; | |
| } | |
| } else { | |
| error_log("StorageManager saveFile: KV storage not configured"); | |
| } | |
| if (!$success && !empty($errors)) { | |
| error_log("StorageManager saveFile: All saves failed: " . implode(", ", $errors)); | |
| } | |
| return $success; | |
| } | |
| public function getFile($filename) { | |
| // Ensure filename is safe and doesn't contain path traversal | |
| $filename = $this->sanitizeFilename($filename); | |
| error_log("StorageManager getFile: Starting load for file '$filename'"); | |
| error_log("StorageManager getFile: Storage type is '{$this->storageType}'"); | |
| error_log("StorageManager getFile: Current user is '{$this->currentUser}'"); | |
| if ($this->githubStorage) { | |
| error_log("StorageManager getFile: Attempting GitHub load..."); | |
| $content = $this->githubStorage->get($filename); | |
| if ($content !== false) { | |
| error_log("StorageManager getFile: GitHub load SUCCESS"); | |
| return $content; | |
| } else { | |
| error_log("StorageManager getFile: GitHub load FAILED"); | |
| } | |
| } else { | |
| error_log("StorageManager getFile: GitHub storage not configured"); | |
| } | |
| if ($this->kvStorage) { | |
| error_log("StorageManager getFile: Attempting KV load..."); | |
| $content = $this->kvStorage->get($filename); | |
| if ($content !== false) { | |
| error_log("StorageManager getFile: KV load SUCCESS"); | |
| return $content; | |
| } else { | |
| error_log("StorageManager getFile: KV load FAILED"); | |
| } | |
| } else { | |
| error_log("StorageManager getFile: KV storage not configured"); | |
| } | |
| error_log("StorageManager getFile: All load attempts failed"); | |
| return false; | |
| } | |
| public function listFiles() { | |
| $files = []; | |
| if ($this->githubStorage) { | |
| $githubFiles = $this->githubStorage->listUserFiles(); | |
| $files = array_merge($files, $githubFiles); | |
| } | |
| if ($this->kvStorage) { | |
| $kvFiles = $this->kvStorage->listUserFiles(); | |
| $files = array_merge($files, $kvFiles); | |
| } | |
| return $files; | |
| } | |
| public function deleteFile($filename) { | |
| // Ensure filename is safe and doesn't contain path traversal | |
| $filename = $this->sanitizeFilename($filename); | |
| $success = false; | |
| if ($this->githubStorage) { | |
| $success = $this->githubStorage->delete($filename) || $success; | |
| } | |
| // KV storage delete would need to be implemented based on provider | |
| return $success; | |
| } | |
| public function getCurrentUser() { | |
| return $this->currentUser; | |
| } | |
| public function getUserPath() { | |
| return StorageConfig::getUserPath(); | |
| } | |
| private function sanitizeFilename($filename) { | |
| // Remove any path traversal attempts | |
| $filename = str_replace(['../', '..\\', '../', '..\\'], '', $filename); | |
| // Remove leading slashes | |
| $filename = ltrim($filename, '/\\'); | |
| // Ensure safe characters only | |
| $filename = preg_replace('/[^a-zA-Z0-9_\-\.\/]/', '_', $filename); | |
| return $filename; | |
| } | |
| } |