749 lines
25 KiB
PHP
749 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* Git Platform API Handler Class
|
|
* Handles API interactions for GitHub, GitLab, Bitbucket, and Codeberg
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('WPINC')) {
|
|
die('Direct access not permitted.');
|
|
}
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class MCB_GitHub_API {
|
|
|
|
private $cache_duration = 3600; // 1 hour cache
|
|
|
|
// Platform API configurations
|
|
private $platforms = array(
|
|
'github' => array(
|
|
'name' => 'GitHub',
|
|
'api_base' => 'https://api.github.com',
|
|
'repo_pattern' => '/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_\.]+$/',
|
|
'web_base' => 'https://github.com'
|
|
),
|
|
'gitlab' => array(
|
|
'name' => 'GitLab',
|
|
'api_base' => 'https://gitlab.com/api/v4',
|
|
'repo_pattern' => '/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_\.]+$/',
|
|
'web_base' => 'https://gitlab.com'
|
|
),
|
|
'bitbucket' => array(
|
|
'name' => 'Bitbucket',
|
|
'api_base' => 'https://api.bitbucket.org/2.0',
|
|
'repo_pattern' => '/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_\.]+$/',
|
|
'web_base' => 'https://bitbucket.org'
|
|
),
|
|
'codeberg' => array(
|
|
'name' => 'Codeberg',
|
|
'api_base' => 'https://codeberg.org/api/v1',
|
|
'repo_pattern' => '/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_\.]+$/',
|
|
'web_base' => 'https://codeberg.org'
|
|
)
|
|
);
|
|
|
|
/**
|
|
* Detect platform from repository string
|
|
*/
|
|
private function detect_platform($repo_string) {
|
|
// Check for platform prefix (e.g., "gitlab:owner/repo")
|
|
if (strpos($repo_string, ':') !== false) {
|
|
list($platform, $repo) = explode(':', $repo_string, 2);
|
|
if (isset($this->platforms[$platform])) {
|
|
return array('platform' => $platform, 'repo' => $repo);
|
|
}
|
|
}
|
|
|
|
// Check for full URLs
|
|
if (strpos($repo_string, 'https://') === 0 || strpos($repo_string, 'http://') === 0) {
|
|
foreach ($this->platforms as $key => $config) {
|
|
if (strpos($repo_string, $config['web_base']) !== false) {
|
|
// Extract owner/repo from URL
|
|
$parsed = parse_url($repo_string);
|
|
$path = trim($parsed['path'], '/');
|
|
$parts = explode('/', $path);
|
|
if (count($parts) >= 2) {
|
|
return array(
|
|
'platform' => $key,
|
|
'repo' => $parts[0] . '/' . $parts[1]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default to GitHub for backward compatibility
|
|
return array('platform' => 'github', 'repo' => $repo_string);
|
|
}
|
|
|
|
/**
|
|
* Get repository files list
|
|
*/
|
|
public function get_repository_files($repo_string, $path = '') {
|
|
$platform_info = $this->detect_platform($repo_string);
|
|
$platform = $platform_info['platform'];
|
|
$repo = $platform_info['repo'];
|
|
|
|
// Validate repo format
|
|
if (!preg_match($this->platforms[$platform]['repo_pattern'], $repo)) {
|
|
return new WP_Error('invalid_repo', 'Invalid repository format. Use: owner/repository');
|
|
}
|
|
|
|
// Check cache first
|
|
$cache_key = 'mcb_repo_' . $platform . '_' . md5($repo . '_' . $path);
|
|
$cached_data = get_transient($cache_key);
|
|
|
|
if ($cached_data !== false) {
|
|
return $cached_data;
|
|
}
|
|
|
|
// Get files based on platform
|
|
switch ($platform) {
|
|
case 'github':
|
|
$files = $this->get_github_files($repo, $path);
|
|
break;
|
|
case 'gitlab':
|
|
$files = $this->get_gitlab_files($repo, $path);
|
|
break;
|
|
case 'bitbucket':
|
|
$files = $this->get_bitbucket_files($repo, $path);
|
|
break;
|
|
case 'codeberg':
|
|
$files = $this->get_codeberg_files($repo, $path);
|
|
break;
|
|
default:
|
|
return new WP_Error('unsupported_platform', 'Unsupported platform');
|
|
}
|
|
|
|
if (!is_wp_error($files)) {
|
|
// Cache the results
|
|
set_transient($cache_key, $files, $this->cache_duration);
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Get file content from repository
|
|
*/
|
|
public function get_file_content($repo_string, $file_path) {
|
|
$platform_info = $this->detect_platform($repo_string);
|
|
$platform = $platform_info['platform'];
|
|
$repo = $platform_info['repo'];
|
|
|
|
// Validate inputs
|
|
if (!preg_match($this->platforms[$platform]['repo_pattern'], $repo)) {
|
|
return new WP_Error('invalid_repo', 'Invalid repository format');
|
|
}
|
|
|
|
// Sanitize and validate file path
|
|
$file_path = trim($file_path, '/');
|
|
|
|
// Block path traversal attempts
|
|
if (strpos($file_path, '..') !== false ||
|
|
strpos($file_path, '//') !== false ||
|
|
strpos($file_path, '\\') !== false ||
|
|
preg_match('/[<>"|*?]/', $file_path)) {
|
|
return new WP_Error('invalid_path', 'Invalid file path');
|
|
}
|
|
|
|
// Check cache
|
|
$cache_key = 'mcb_file_' . $platform . '_' . md5($repo . $file_path);
|
|
$cached_content = get_transient($cache_key);
|
|
|
|
if ($cached_content !== false) {
|
|
return $cached_content;
|
|
}
|
|
|
|
// Get content based on platform
|
|
switch ($platform) {
|
|
case 'github':
|
|
$content = $this->get_github_file_content($repo, $file_path);
|
|
break;
|
|
case 'gitlab':
|
|
$content = $this->get_gitlab_file_content($repo, $file_path);
|
|
break;
|
|
case 'bitbucket':
|
|
$content = $this->get_bitbucket_file_content($repo, $file_path);
|
|
break;
|
|
case 'codeberg':
|
|
$content = $this->get_codeberg_file_content($repo, $file_path);
|
|
break;
|
|
default:
|
|
return new WP_Error('unsupported_platform', 'Unsupported platform');
|
|
}
|
|
|
|
if (!is_wp_error($content)) {
|
|
// Cache the content
|
|
set_transient($cache_key, $content, $this->cache_duration);
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* GitHub-specific file listing
|
|
*/
|
|
private function get_github_files($repo, $path = '') {
|
|
$url = $this->platforms['github']['api_base'] . '/repos/' . $repo . '/contents';
|
|
if (!empty($path)) {
|
|
$url .= '/' . ltrim($path, '/');
|
|
}
|
|
|
|
$response = $this->make_api_request($url, 'github');
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
return $this->parse_github_contents($response, $repo, $path);
|
|
}
|
|
|
|
/**
|
|
* GitLab-specific file listing
|
|
*/
|
|
private function get_gitlab_files($repo, $path = '') {
|
|
// GitLab uses project ID or URL-encoded path
|
|
$project_id = urlencode($repo);
|
|
$url = $this->platforms['gitlab']['api_base'] . '/projects/' . $project_id . '/repository/tree?recursive=true&per_page=100';
|
|
$response = $this->make_api_request($url, 'gitlab');
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
return $this->parse_gitlab_contents($response);
|
|
}
|
|
|
|
/**
|
|
* Bitbucket-specific file listing
|
|
*/
|
|
private function get_bitbucket_files($repo, $path = '') {
|
|
$url = $this->platforms['bitbucket']['api_base'] . '/repositories/' . $repo . '/src';
|
|
$response = $this->make_api_request($url, 'bitbucket');
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
return $this->parse_bitbucket_contents($response, $repo);
|
|
}
|
|
|
|
/**
|
|
* Codeberg-specific file listing (uses Gitea API)
|
|
*/
|
|
private function get_codeberg_files($repo, $path = '') {
|
|
$url = $this->platforms['codeberg']['api_base'] . '/repos/' . $repo . '/contents';
|
|
if (!empty($path)) {
|
|
$url .= '/' . ltrim($path, '/');
|
|
}
|
|
|
|
$response = $this->make_api_request($url, 'codeberg');
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
// Codeberg uses Gitea, similar to GitHub API
|
|
return $this->parse_github_contents($response, $repo, $path);
|
|
}
|
|
|
|
/**
|
|
* GitHub file content retrieval
|
|
*/
|
|
private function get_github_file_content($repo, $file_path) {
|
|
$url = $this->platforms['github']['api_base'] . '/repos/' . $repo . '/contents/' . $file_path;
|
|
$response = $this->make_api_request($url, 'github');
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if (!isset($data['content'])) {
|
|
return new WP_Error('no_content', 'File content not found');
|
|
}
|
|
|
|
return base64_decode($data['content']);
|
|
}
|
|
|
|
/**
|
|
* GitLab file content retrieval
|
|
*/
|
|
private function get_gitlab_file_content($repo, $file_path) {
|
|
$project_id = urlencode($repo);
|
|
$file_path_encoded = urlencode($file_path);
|
|
$url = $this->platforms['gitlab']['api_base'] . '/projects/' . $project_id . '/repository/files/' . $file_path_encoded . '/raw?ref=main';
|
|
|
|
// Try main branch first, then master
|
|
$response = $this->make_api_request($url, 'gitlab');
|
|
|
|
if (is_wp_error($response)) {
|
|
// Try master branch
|
|
$url = $this->platforms['gitlab']['api_base'] . '/projects/' . $project_id . '/repository/files/' . $file_path_encoded . '/raw?ref=master';
|
|
$response = $this->make_api_request($url, 'gitlab');
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Bitbucket file content retrieval
|
|
*/
|
|
private function get_bitbucket_file_content($repo, $file_path) {
|
|
// Get default branch first
|
|
$repo_url = $this->platforms['bitbucket']['api_base'] . '/repositories/' . $repo;
|
|
$repo_response = $this->make_api_request($repo_url, 'bitbucket');
|
|
|
|
if (is_wp_error($repo_response)) {
|
|
return $repo_response;
|
|
}
|
|
|
|
$repo_data = json_decode($repo_response, true);
|
|
$branch = isset($repo_data['mainbranch']['name']) ? $repo_data['mainbranch']['name'] : 'master';
|
|
|
|
$url = $this->platforms['bitbucket']['api_base'] . '/repositories/' . $repo . '/src/' . $branch . '/' . $file_path;
|
|
return $this->make_api_request($url, 'bitbucket');
|
|
}
|
|
|
|
/**
|
|
* Codeberg file content retrieval
|
|
*/
|
|
private function get_codeberg_file_content($repo, $file_path) {
|
|
// Codeberg uses Gitea API, similar to GitHub
|
|
$url = $this->platforms['codeberg']['api_base'] . '/repos/' . $repo . '/contents/' . $file_path;
|
|
$response = $this->make_api_request($url, 'codeberg');
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if (!isset($data['content'])) {
|
|
return new WP_Error('no_content', 'File content not found');
|
|
}
|
|
|
|
return base64_decode($data['content']);
|
|
}
|
|
|
|
/**
|
|
* Make HTTP request to Git platform API
|
|
*/
|
|
private function make_api_request($url, $platform) {
|
|
// SSRF Protection - validate URL
|
|
$parsed_url = parse_url($url);
|
|
|
|
// Only allow HTTPS protocol
|
|
if ($parsed_url['scheme'] !== 'https') {
|
|
return new WP_Error('invalid_protocol', 'Only HTTPS is allowed');
|
|
}
|
|
|
|
// Only allow known platform hosts
|
|
$allowed_hosts = array(
|
|
'api.github.com',
|
|
'gitlab.com',
|
|
'api.bitbucket.org',
|
|
'codeberg.org'
|
|
);
|
|
|
|
if (!in_array($parsed_url['host'], $allowed_hosts)) {
|
|
return new WP_Error('invalid_host', 'Invalid API host');
|
|
}
|
|
|
|
// Add request throttling to prevent rate limit issues
|
|
static $last_request_time = 0;
|
|
$min_interval = 0.1; // Minimum 100ms between requests
|
|
|
|
$current_time = microtime(true);
|
|
$time_since_last = $current_time - $last_request_time;
|
|
|
|
if ($time_since_last < $min_interval) {
|
|
usleep(($min_interval - $time_since_last) * 1000000);
|
|
}
|
|
|
|
$args = array(
|
|
'timeout' => 10, // Reduced timeout to prevent hanging
|
|
'redirection' => 3, // Limit redirects
|
|
'headers' => $this->get_platform_headers($platform)
|
|
);
|
|
|
|
// Add authentication if available
|
|
$token = $this->get_platform_token($platform);
|
|
if (!empty($token)) {
|
|
$args['headers']['Authorization'] = $this->get_auth_header($platform, $token);
|
|
}
|
|
|
|
$response = wp_remote_get($url, $args);
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$status_code = wp_remote_retrieve_response_code($response);
|
|
|
|
if ($status_code !== 200) {
|
|
return new WP_Error('api_error', 'API returned status: ' . $status_code);
|
|
}
|
|
|
|
$last_request_time = microtime(true);
|
|
|
|
return wp_remote_retrieve_body($response);
|
|
}
|
|
|
|
/**
|
|
* Get platform-specific headers
|
|
*/
|
|
private function get_platform_headers($platform) {
|
|
switch ($platform) {
|
|
case 'github':
|
|
return array(
|
|
'Accept' => 'application/vnd.github.v3+json',
|
|
'User-Agent' => 'WordPress/GitHub-Code-Viewer'
|
|
);
|
|
case 'gitlab':
|
|
return array(
|
|
'Accept' => 'application/json',
|
|
'User-Agent' => 'WordPress/GitHub-Code-Viewer'
|
|
);
|
|
case 'bitbucket':
|
|
return array(
|
|
'Accept' => 'application/json',
|
|
'User-Agent' => 'WordPress/GitHub-Code-Viewer'
|
|
);
|
|
case 'codeberg':
|
|
return array(
|
|
'Accept' => 'application/json',
|
|
'User-Agent' => 'WordPress/GitHub-Code-Viewer'
|
|
);
|
|
default:
|
|
return array(
|
|
'User-Agent' => 'WordPress/GitHub-Code-Viewer'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get platform token if configured
|
|
*/
|
|
private function get_platform_token($platform) {
|
|
// Use secure token manager if available
|
|
if (class_exists('MCB_Token_Manager')) {
|
|
return MCB_Token_Manager::get_token($platform);
|
|
}
|
|
// Fallback to direct option (for backwards compatibility)
|
|
return get_option('mcb_' . $platform . '_token', '');
|
|
}
|
|
|
|
/**
|
|
* Get authorization header format for platform
|
|
*/
|
|
private function get_auth_header($platform, $token) {
|
|
switch ($platform) {
|
|
case 'github':
|
|
return 'token ' . $token;
|
|
case 'gitlab':
|
|
return 'Bearer ' . $token;
|
|
case 'bitbucket':
|
|
return 'Bearer ' . $token;
|
|
case 'codeberg':
|
|
return 'token ' . $token;
|
|
default:
|
|
return 'Bearer ' . $token;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse GitHub/Codeberg repository contents
|
|
*/
|
|
private function parse_github_contents($response, $repo, $current_path = '') {
|
|
$items = json_decode($response, true);
|
|
$files = array();
|
|
|
|
if (!is_array($items)) {
|
|
return $files;
|
|
}
|
|
|
|
// Add parent directory navigation if not at root
|
|
if (!empty($current_path)) {
|
|
$parent_path = dirname($current_path);
|
|
if ($parent_path === '.') {
|
|
$parent_path = '';
|
|
}
|
|
|
|
$files[] = array(
|
|
'name' => '..',
|
|
'path' => $parent_path,
|
|
'size' => 0,
|
|
'type' => 'parent',
|
|
'is_folder' => true,
|
|
'url' => ''
|
|
);
|
|
}
|
|
|
|
$code_extensions = $this->get_code_extensions();
|
|
|
|
// First add all directories
|
|
foreach ($items as $item) {
|
|
if ($item['type'] === 'dir') {
|
|
$files[] = array(
|
|
'name' => $item['name'],
|
|
'path' => $item['path'],
|
|
'size' => 0,
|
|
'type' => 'folder',
|
|
'is_folder' => true,
|
|
'url' => $item['html_url']
|
|
);
|
|
}
|
|
}
|
|
|
|
// Then add all files
|
|
foreach ($items as $item) {
|
|
if ($item['type'] === 'file') {
|
|
$extension = strtolower(pathinfo($item['name'], PATHINFO_EXTENSION));
|
|
$name_lower = strtolower($item['name']);
|
|
|
|
// Include all files, not just code files, for better browsing
|
|
$files[] = array(
|
|
'name' => $item['name'],
|
|
'path' => $item['path'],
|
|
'size' => $item['size'],
|
|
'type' => $this->get_file_type($item['name']),
|
|
'is_folder' => false,
|
|
'url' => $item['html_url']
|
|
);
|
|
}
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Parse GitLab repository contents
|
|
*/
|
|
private function parse_gitlab_contents($response) {
|
|
$items = json_decode($response, true);
|
|
$files = array();
|
|
|
|
if (!is_array($items)) {
|
|
return $files;
|
|
}
|
|
|
|
$code_extensions = $this->get_code_extensions();
|
|
|
|
foreach ($items as $item) {
|
|
if ($item['type'] === 'blob') { // GitLab uses 'blob' for files
|
|
$extension = strtolower(pathinfo($item['name'], PATHINFO_EXTENSION));
|
|
$name_lower = strtolower($item['name']);
|
|
|
|
if (in_array($extension, $code_extensions) || $this->is_code_file($name_lower)) {
|
|
$files[] = array(
|
|
'name' => $item['name'],
|
|
'path' => $item['path'],
|
|
'size' => 0, // GitLab doesn't provide size in tree API
|
|
'type' => $this->get_file_type($item['name']),
|
|
'url' => '' // Will need to construct if needed
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Parse Bitbucket repository contents
|
|
*/
|
|
private function parse_bitbucket_contents($response, $repo) {
|
|
$data = json_decode($response, true);
|
|
$files = array();
|
|
|
|
if (!isset($data['values']) || !is_array($data['values'])) {
|
|
return $files;
|
|
}
|
|
|
|
$code_extensions = $this->get_code_extensions();
|
|
|
|
foreach ($data['values'] as $item) {
|
|
if ($item['type'] === 'commit_file') {
|
|
$extension = strtolower(pathinfo($item['path'], PATHINFO_EXTENSION));
|
|
$name_lower = strtolower(basename($item['path']));
|
|
|
|
if (in_array($extension, $code_extensions) || $this->is_code_file($name_lower)) {
|
|
$files[] = array(
|
|
'name' => basename($item['path']),
|
|
'path' => $item['path'],
|
|
'size' => isset($item['size']) ? $item['size'] : 0,
|
|
'type' => $this->get_file_type(basename($item['path'])),
|
|
'url' => 'https://bitbucket.org/' . $repo . '/src/master/' . $item['path']
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Get list of code file extensions
|
|
*/
|
|
private function get_code_extensions() {
|
|
return array(
|
|
'php', 'js', 'jsx', 'ts', 'tsx', 'py', 'rb', 'java', 'c', 'cpp', 'cc', 'cxx',
|
|
'h', 'hpp', 'cs', 'swift', 'kt', 'go', 'rs', 'scala', 'r', 'sql', 'sh', 'bash',
|
|
'yml', 'yaml', 'json', 'xml', 'html', 'css', 'scss', 'sass', 'less',
|
|
'md', 'markdown', 'txt', 'ini', 'conf', 'dockerfile', 'makefile',
|
|
'vue', 'svelte', 'elm', 'clj', 'ex', 'exs', 'erl', 'hrl', 'lua', 'pl', 'pm'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if filename indicates a code file
|
|
*/
|
|
private function is_code_file($filename) {
|
|
$code_files = array(
|
|
'dockerfile', 'makefile', '.gitignore', '.env', '.htaccess',
|
|
'gemfile', 'rakefile', 'gulpfile', 'gruntfile', 'webpack.config.js',
|
|
'package.json', 'composer.json', 'cargo.toml', 'go.mod'
|
|
);
|
|
|
|
return in_array($filename, $code_files);
|
|
}
|
|
|
|
/**
|
|
* Get file type based on extension
|
|
*/
|
|
private function get_file_type($filename) {
|
|
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
|
$name_lower = strtolower($filename);
|
|
|
|
$type_map = array(
|
|
'php' => 'php',
|
|
'js' => 'javascript',
|
|
'jsx' => 'jsx',
|
|
'ts' => 'typescript',
|
|
'tsx' => 'tsx',
|
|
'py' => 'python',
|
|
'rb' => 'ruby',
|
|
'java' => 'java',
|
|
'c' => 'c',
|
|
'cpp' => 'cpp',
|
|
'cc' => 'cpp',
|
|
'cxx' => 'cpp',
|
|
'h' => 'c',
|
|
'hpp' => 'cpp',
|
|
'cs' => 'csharp',
|
|
'swift' => 'swift',
|
|
'kt' => 'kotlin',
|
|
'go' => 'go',
|
|
'rs' => 'rust',
|
|
'scala' => 'scala',
|
|
'r' => 'r',
|
|
'sql' => 'sql',
|
|
'sh' => 'bash',
|
|
'bash' => 'bash',
|
|
'yml' => 'yaml',
|
|
'yaml' => 'yaml',
|
|
'json' => 'json',
|
|
'xml' => 'xml',
|
|
'html' => 'html',
|
|
'css' => 'css',
|
|
'scss' => 'scss',
|
|
'sass' => 'sass',
|
|
'less' => 'less',
|
|
'md' => 'markdown',
|
|
'markdown' => 'markdown'
|
|
);
|
|
|
|
// Check special file names
|
|
if ($name_lower === 'dockerfile') {
|
|
return 'docker';
|
|
}
|
|
if ($name_lower === 'makefile') {
|
|
return 'makefile';
|
|
}
|
|
|
|
return isset($type_map[$extension]) ? $type_map[$extension] : 'plain';
|
|
}
|
|
|
|
/**
|
|
* Validate if repository exists
|
|
*/
|
|
public function validate_repository_exists($repo_string) {
|
|
$platform_info = $this->detect_platform($repo_string);
|
|
$platform = $platform_info['platform'];
|
|
$repo = $platform_info['repo'];
|
|
|
|
// Validate format first
|
|
if (!preg_match($this->platforms[$platform]['repo_pattern'], $repo)) {
|
|
return new WP_Error('invalid_repo', 'Invalid repository format');
|
|
}
|
|
|
|
// Check cache first
|
|
$cache_key = 'mcb_repo_valid_' . $platform . '_' . md5($repo);
|
|
$cached_result = get_transient($cache_key);
|
|
|
|
if ($cached_result !== false) {
|
|
return $cached_result;
|
|
}
|
|
|
|
// Check with platform API
|
|
$exists = false;
|
|
switch ($platform) {
|
|
case 'github':
|
|
$url = $this->platforms['github']['api_base'] . '/repos/' . $repo;
|
|
break;
|
|
case 'gitlab':
|
|
$project_id = urlencode($repo);
|
|
$url = $this->platforms['gitlab']['api_base'] . '/projects/' . $project_id;
|
|
break;
|
|
case 'bitbucket':
|
|
$url = $this->platforms['bitbucket']['api_base'] . '/repositories/' . $repo;
|
|
break;
|
|
case 'codeberg':
|
|
$url = $this->platforms['codeberg']['api_base'] . '/repos/' . $repo;
|
|
break;
|
|
default:
|
|
return new WP_Error('unsupported_platform', 'Unsupported platform');
|
|
}
|
|
|
|
$response = $this->make_api_request($url, $platform);
|
|
|
|
if (!is_wp_error($response)) {
|
|
$data = json_decode($response, true);
|
|
if (isset($data['id']) || isset($data['uuid']) || isset($data['name'])) {
|
|
$exists = true;
|
|
}
|
|
}
|
|
|
|
if ($exists) {
|
|
set_transient($cache_key, true, 3600);
|
|
return true;
|
|
}
|
|
|
|
return new WP_Error('repo_not_found', 'Repository not found or is private');
|
|
}
|
|
|
|
/**
|
|
* Get platform display name
|
|
*/
|
|
public function get_platform_name($repo_string) {
|
|
$platform_info = $this->detect_platform($repo_string);
|
|
return $this->platforms[$platform_info['platform']]['name'];
|
|
}
|
|
|
|
/**
|
|
* Get repository web URL
|
|
*/
|
|
public function get_repository_url($repo_string) {
|
|
$platform_info = $this->detect_platform($repo_string);
|
|
$platform = $platform_info['platform'];
|
|
$repo = $platform_info['repo'];
|
|
|
|
return $this->platforms[$platform]['web_base'] . '/' . $repo;
|
|
}
|
|
}
|