|
|
<?php
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
if (strpos($line, '=') === false) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
list($name, $value) = explode('=', $line, 2);
|
|
|
$name = trim($name);
|
|
|
$value = trim($value);
|
|
|
|
|
|
|
|
|
if (preg_match('/^"(.*)"$/', $value, $matches)) {
|
|
|
$value = $matches[1];
|
|
|
} elseif (preg_match("/^'(.*)'$/", $value, $matches)) {
|
|
|
$value = $matches[1];
|
|
|
}
|
|
|
|
|
|
|
|
|
if (!array_key_exists($name, $_ENV) && !isset($_SERVER[$name])) {
|
|
|
$_ENV[$name] = $value;
|
|
|
putenv("$name=$value");
|
|
|
}
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (file_exists(__DIR__ . '/.env')) {
|
|
|
loadEnv(__DIR__ . '/.env');
|
|
|
} else {
|
|
|
|
|
|
if (file_exists(__DIR__ . '/.env.example')) {
|
|
|
loadEnv(__DIR__ . '/.env.example');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
class StorageConfig {
|
|
|
public static function getUsers() {
|
|
|
|
|
|
$defaultUsers = [
|
|
|
'PS01' => 'password123',
|
|
|
'PS02' => 'password456',
|
|
|
'admin' => 'admin123'
|
|
|
];
|
|
|
|
|
|
$users = [];
|
|
|
$i = 1;
|
|
|
while (true) {
|
|
|
|
|
|
$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++;
|
|
|
}
|
|
|
|
|
|
|
|
|
return empty($users) ? $defaultUsers : $users;
|
|
|
}
|
|
|
|
|
|
public static function getStorageType() {
|
|
|
return $_ENV['STORAGE_TYPE'] ?? $_SERVER['STORAGE_TYPE'] ?? 'github';
|
|
|
}
|
|
|
|
|
|
public static function getGitHubConfig() {
|
|
|
|
|
|
$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/'
|
|
|
];
|
|
|
|
|
|
|
|
|
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() {
|
|
|
|
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
|
session_start();
|
|
|
}
|
|
|
|
|
|
if (isset($_SESSION['username'])) {
|
|
|
return $_SESSION['username'];
|
|
|
}
|
|
|
|
|
|
|
|
|
return $_SERVER['PHP_AUTH_USER'] ?? 'anonymous';
|
|
|
}
|
|
|
|
|
|
public static function getUserPath($username = null) {
|
|
|
if ($username === null) {
|
|
|
$username = self::getCurrentUser();
|
|
|
}
|
|
|
|
|
|
$safeUsername = preg_replace('/[^a-zA-Z0-9_-]/', '_', $username);
|
|
|
return "users/{$safeUsername}/";
|
|
|
}
|
|
|
|
|
|
|
|
|
public static function isHuggingFaceSpace() {
|
|
|
return isset($_SERVER['SPACE_ID']) ||
|
|
|
isset($_ENV['SPACE_ID']) ||
|
|
|
strpos($_SERVER['HTTP_HOST'] ?? '', '.hf.space') !== false;
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
$userKey = $this->userPath . $key;
|
|
|
|
|
|
|
|
|
$url = $this->config['endpoint'];
|
|
|
$params = [
|
|
|
'Action' => 'CreatePurgeTask',
|
|
|
'Version' => '2022-09-01',
|
|
|
'Region' => 'ap-beijing',
|
|
|
'ZoneId' => $this->config['zone_id'],
|
|
|
'Namespace' => $this->config['namespace'],
|
|
|
'Key' => $userKey,
|
|
|
'Value' => base64_encode($content),
|
|
|
'TTL' => 86400
|
|
|
];
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
$userKey = $this->userPath . $key;
|
|
|
|
|
|
$url = $this->config['endpoint'];
|
|
|
$params = [
|
|
|
'Action' => 'DescribeOriginGroup',
|
|
|
'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',
|
|
|
'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) {
|
|
|
|
|
|
$commonParams = [
|
|
|
'Timestamp' => time(),
|
|
|
'Nonce' => rand(10000, 99999),
|
|
|
'SecretId' => $this->config['api_key'],
|
|
|
'SignatureMethod' => 'HmacSHA256'
|
|
|
];
|
|
|
|
|
|
$params = array_merge($params, $commonParams);
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
$this->detectDefaultBranch();
|
|
|
}
|
|
|
|
|
|
private function detectDefaultBranch() {
|
|
|
if (empty($this->config['token']) || empty($this->config['owner']) || empty($this->config['repo'])) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
|
|
|
$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 {
|
|
|
|
|
|
$commonBranches = ['main', 'master', 'develop'];
|
|
|
foreach ($commonBranches as $branch) {
|
|
|
if ($this->checkBranchExists($branch)) {
|
|
|
$this->actualBranch = $branch;
|
|
|
error_log("GitHub: Found existing branch '{$branch}'");
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
|
$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");
|
|
|
|
|
|
|
|
|
if (!$this->ensureUserDirectoryExists($userPath)) {
|
|
|
error_log("GitHubStorage: Failed to ensure user directory exists");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
|
|
|
usleep(500000);
|
|
|
|
|
|
|
|
|
$url = $this->apiUrl . "/contents/" . $filePath;
|
|
|
|
|
|
|
|
|
$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");
|
|
|
|
|
|
|
|
|
$pathParts = explode('/', trim($userPath, '/'));
|
|
|
$currentPath = '';
|
|
|
|
|
|
|
|
|
foreach ($pathParts as $part) {
|
|
|
if (empty($part)) continue;
|
|
|
|
|
|
$currentPath .= ($currentPath ? '/' : '') . $part;
|
|
|
|
|
|
|
|
|
$url = $this->apiUrl . "/contents/" . $currentPath;
|
|
|
$response = $this->makeRequest($url);
|
|
|
|
|
|
if ($response === false) {
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
$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') {
|
|
|
|
|
|
$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");
|
|
|
|
|
|
return [];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public function delete($filename) {
|
|
|
if (empty($this->config['token'])) return false;
|
|
|
|
|
|
|
|
|
$userFilename = $this->userPath . $filename;
|
|
|
$path = $this->config['path'] . $userFilename;
|
|
|
$url = "https://api.github.com/repos/{$this->config['owner']}/{$this->config['repo']}/contents/{$path}";
|
|
|
|
|
|
|
|
|
$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));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
$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 {
|
|
|
|
|
|
$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));
|
|
|
}
|
|
|
|
|
|
|
|
|
if ($httpCode === 404 && $method === 'GET') {
|
|
|
error_log("GitHub API: Resource not found (this may be expected when checking file existence)");
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
|
$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) {
|
|
|
|
|
|
$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) {
|
|
|
|
|
|
$filename = $this->sanitizeFilename($filename);
|
|
|
|
|
|
$success = false;
|
|
|
|
|
|
if ($this->githubStorage) {
|
|
|
$success = $this->githubStorage->delete($filename) || $success;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $success;
|
|
|
}
|
|
|
|
|
|
public function getCurrentUser() {
|
|
|
return $this->currentUser;
|
|
|
}
|
|
|
|
|
|
public function getUserPath() {
|
|
|
return StorageConfig::getUserPath();
|
|
|
}
|
|
|
|
|
|
private function sanitizeFilename($filename) {
|
|
|
|
|
|
$filename = str_replace(['../', '..\\', '../', '..\\'], '', $filename);
|
|
|
|
|
|
$filename = ltrim($filename, '/\\');
|
|
|
|
|
|
$filename = preg_replace('/[^a-zA-Z0-9_\-\.\/]/', '_', $filename);
|
|
|
|
|
|
return $filename;
|
|
|
}
|
|
|
} |