feat: ssh auth, protocol management, and cleanup

This commit is contained in:
infosave2007
2026-01-23 17:55:40 +03:00
parent 60fc55fd47
commit bbab877eac
70 changed files with 16225 additions and 986 deletions
+314
View File
@@ -0,0 +1,314 @@
<?php
class OpenRouterService {
private $apiKey;
private $apiUrl = 'https://openrouter.ai/api/v1';
private $timeout = 60; // 60 seconds timeout for AI generation
public function __construct() {
$this->apiKey = $_ENV['OPENROUTER_API_KEY'] ?? null;
if (!$this->apiKey) {
throw new Exception('OpenRouter API key not configured');
}
}
/**
* Generate installation script using OpenRouter API
*/
public function generateScript(string $prompt, string $model = 'openai/gpt-3.5-turbo'): array {
try {
$messages = [
[
'role' => 'system',
'content' => 'You are a helpful assistant that creates bash installation scripts for VPN protocols. Always respond with valid JSON containing the script, suggestions, ubuntu compatibility, and estimated installation time.'
],
[
'role' => 'user',
'content' => $prompt
]
];
$response = $this->makeAPICall('/chat/completions', [
'model' => $model,
'messages' => $messages,
'temperature' => 0.3, // Lower temperature for more consistent results
'max_tokens' => 4000, // Sufficient for detailed scripts
'response_format' => ['type' => 'json_object']
]);
if (!isset($response['choices'][0]['message']['content'])) {
throw new Exception('Invalid response from OpenRouter API');
}
$content = $response['choices'][0]['message']['content'];
$parsed = json_decode($content, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// If JSON parsing fails, try to extract script from plain text
return $this->parsePlainTextResponse($content);
}
return $this->validateAndEnhanceResponse($parsed);
} catch (Exception $e) {
error_log("Error in OpenRouterService::generateScript: " . $e->getMessage());
throw new Exception('Failed to generate script: ' . $e->getMessage());
}
}
/**
* Get available AI models from OpenRouter
*/
public function getAvailableModels(): array {
try {
$response = $this->makeAPICall('/models', [], 'GET');
if (!isset($response['data'])) {
throw new Exception('Invalid response from OpenRouter API');
}
// Filter models suitable for code generation
$codeModels = array_filter($response['data'], function($model) {
$codeModelIds = [
'openai/gpt-3.5-turbo',
'openai/gpt-4',
'openai/gpt-4-turbo',
'anthropic/claude-3-haiku',
'anthropic/claude-3-sonnet',
'anthropic/claude-3-opus',
'google/gemini-pro',
'meta-llama/llama-2-70b-chat',
'meta-llama/llama-3-70b-instruct'
];
return in_array($model['id'], $codeModelIds) && $model['top_provider'] === true;
});
return array_values(array_map(function($model) {
return [
'id' => $model['id'],
'name' => $model['name'] ?? $model['id'],
'description' => $model['description'] ?? '',
'pricing' => $model['pricing'] ?? null
];
}, $codeModels));
} catch (Exception $e) {
error_log("Error in OpenRouterService::getAvailableModels: " . $e->getMessage());
// Return default models if API call fails
return $this->getDefaultModels();
}
}
/**
* Make API call to OpenRouter
*/
private function makeAPICall(string $endpoint, array $data = [], string $method = 'POST'): array {
$ch = curl_init();
$url = $this->apiUrl . $endpoint;
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json',
'HTTP-Referer: ' . ($_ENV['APP_URL'] ?? 'https://localhost'),
'X-Title: Amnezia VPN Panel'
],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new Exception('CURL error: ' . $error);
}
if ($httpCode >= 400) {
$errorData = json_decode($response, true);
$errorMessage = $errorData['error']['message'] ?? "HTTP $httpCode error";
throw new Exception($errorMessage);
}
$decoded = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Invalid JSON response from OpenRouter API');
}
return $decoded;
}
/**
* Parse plain text response when JSON parsing fails
*/
private function parsePlainTextResponse(string $content): array {
// Try to extract bash script from plain text
if (preg_match('/```bash\n(.*?)\n```/s', $content, $matches)) {
$script = trim($matches[1]);
} elseif (preg_match('/```(.*?)```/s', $content, $matches)) {
$script = trim($matches[1]);
} else {
// If no code blocks found, treat the entire content as script
$script = trim($content);
}
// Add bash shebang if not present
if (!str_starts_with($script, '#!')) {
$script = "#!/bin/bash\n\n" . $script;
}
return [
'script' => $script,
'suggestions' => [
'Check the script for syntax errors',
'Test the script in a safe environment',
'Review security implications'
],
'ubuntu_compatible' => true,
'estimated_time' => '5 minutes'
];
}
/**
* Validate and enhance AI response
*/
private function validateAndEnhanceResponse(array $response): array {
$defaults = [
'script' => '#!/bin/bash\n# Default installation script\necho "Installation script placeholder"',
'suggestions' => [],
'ubuntu_compatible' => true,
'estimated_time' => '5 minutes'
];
// Ensure all required fields are present
foreach ($defaults as $key => $defaultValue) {
if (!isset($response[$key])) {
$response[$key] = $defaultValue;
}
}
// Validate script format
if (!str_starts_with(trim($response['script']), '#!')) {
$response['script'] = "#!/bin/bash\n\n" . $response['script'];
}
// Ensure suggestions is an array
if (!is_array($response['suggestions'])) {
$response['suggestions'] = [];
}
// Add default suggestions if none provided
if (empty($response['suggestions'])) {
$response['suggestions'] = [
'Review the generated script for security implications',
'Test the script in a development environment first',
'Ensure all dependencies are available on your system',
'Backup your system before running the script'
];
}
// Validate ubuntu_compatible is boolean
if (!is_bool($response['ubuntu_compatible'])) {
$response['ubuntu_compatible'] = true;
}
return $response;
}
/**
* Get default models when API is unavailable
*/
private function getDefaultModels(): array {
return [
[
'id' => 'openai/gpt-3.5-turbo',
'name' => 'GPT-3.5 Turbo',
'description' => 'Fast and cost-effective model for general purpose tasks',
'pricing' => ['prompt' => '0.001', 'completion' => '0.002']
],
[
'id' => 'openai/gpt-4',
'name' => 'GPT-4',
'description' => 'Most capable model for complex tasks',
'pricing' => ['prompt' => '0.03', 'completion' => '0.06']
],
[
'id' => 'anthropic/claude-3-haiku',
'name' => 'Claude 3 Haiku',
'description' => 'Fast and cost-effective model from Anthropic',
'pricing' => ['prompt' => '0.00025', 'completion' => '0.00125']
],
[
'id' => 'anthropic/claude-3-sonnet',
'name' => 'Claude 3 Sonnet',
'description' => 'Balanced performance and cost from Anthropic',
'pricing' => ['prompt' => '0.003', 'completion' => '0.015']
]
];
}
public function testModelAvailability(string $modelId): array {
if (!$this->apiKey) {
return [
'success' => false,
'http_code' => 401,
'message' => 'OpenRouter API key not configured'
];
}
$payload = [
'model' => $modelId,
'messages' => [
['role' => 'user', 'content' => 'Reply with: OK']
],
'max_tokens' => 5,
'temperature' => 0
];
$ch = curl_init($this->apiUrl . '/chat/completions');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->apiKey,
'HTTP-Referer: https://amnez.ia',
'X-Title: Amnezia VPN Panel'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
return [
'success' => false,
'http_code' => null,
'message' => 'Network error: ' . $curlError
];
}
$json = json_decode($response, true);
$ok = $httpCode === 200 && isset($json['choices'][0]['message']['content']);
return [
'success' => $ok,
'http_code' => $httpCode,
'message' => $ok ? 'Model is available' : ($json['error']['message'] ?? 'Model test failed')
];
}
}