433 lines
14 KiB
PHP
Executable File
433 lines
14 KiB
PHP
Executable File
<?php
|
|
error_reporting(0);
|
|
/**
|
|
* 🚪 API GATEWAY - Point d'entrée unique
|
|
define("60", 60);
|
|
* Version: 1.0 - Février 2026
|
|
*/
|
|
|
|
header('Content-Type: application/json');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
|
|
|
require_once __DIR__ . '/config.php';
|
|
require_once __DIR__ . '/db-connect.php';
|
|
|
|
class ApiGateway {
|
|
private $db;
|
|
private $rateLimit = [];
|
|
private $modules = [
|
|
'mind' => 'Weval Mind Core',
|
|
'hamid' => 'HAMID IA Engine',
|
|
'brain' => 'Brain Engine',
|
|
'perception' => 'Perception Module',
|
|
'diagnostic' => 'Diagnostic Module',
|
|
'planning' => 'Planning Module',
|
|
'execution' => 'Execution Module',
|
|
'learning' => 'Learning Module',
|
|
'surgeon' => 'Auto-Surgeon',
|
|
'reputation' => 'Reputation Monitor',
|
|
'alert' => 'Alert System'
|
|
];
|
|
|
|
public function __construct() {
|
|
$this->db = getDB();
|
|
$this->initRateLimiting();
|
|
}
|
|
|
|
/**
|
|
* Initialize rate limiting
|
|
*/
|
|
private function initRateLimiting() {
|
|
$clientIp = $this->getClientIp();
|
|
$minute = date('Y-m-d H:i');
|
|
|
|
// Simple in-memory rate limiting (for demo)
|
|
// In production, use Redis or database
|
|
$this->rateLimit[$clientIp][$minute] =
|
|
($this->rateLimit[$clientIp][$minute] ?? 0) + 1;
|
|
}
|
|
|
|
/**
|
|
* Check rate limiting
|
|
*/
|
|
private function checkRateLimit($clientIp) {
|
|
$minute = date('Y-m-d H:i');
|
|
$requests = $this->rateLimit[$clientIp][$minute] ?? 0;
|
|
|
|
if ($requests > 60) {
|
|
logMessage('WARNING', 'API Gateway', 'Rate limit exceeded', [
|
|
'ip' => $clientIp,
|
|
'requests' => $requests
|
|
]);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get client IP
|
|
*/
|
|
private function getClientIp() {
|
|
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ??
|
|
$_SERVER['HTTP_CLIENT_IP'] ??
|
|
$_SERVER['REMOTE_ADDR'] ??
|
|
'0.0.0.0';
|
|
return $ip;
|
|
}
|
|
|
|
/**
|
|
* Authenticate request
|
|
*/
|
|
private function authenticate() {
|
|
// Check for API key in headers
|
|
$apiKey = $_SERVER['HTTP_X_API_KEY'] ??
|
|
$_GET['api_key'] ??
|
|
$_POST['api_key'] ??
|
|
null;
|
|
|
|
// Check for session (if coming from Arsenal frontend)
|
|
$sessionId = $_COOKIE['arsenal_session'] ?? null;
|
|
|
|
// For now, allow all requests (in production, implement proper auth)
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Route request to appropriate module
|
|
*/
|
|
private function routeRequest($module, $action, $data) {
|
|
switch ($module) {
|
|
case 'mind':
|
|
return $this->routeToMind($action, $data);
|
|
case 'hamid':
|
|
return $this->routeToHamid($action, $data);
|
|
case 'status':
|
|
return $this->getSystemStatus();
|
|
case 'health':
|
|
return $this->healthCheck();
|
|
default:
|
|
return [
|
|
'error' => 'Module not found',
|
|
'available_modules' => array_keys($this->modules)
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Route to Weval Mind
|
|
*/
|
|
private function routeToMind($action, $data) {
|
|
// For now, redirect to existing simple API
|
|
// Later, this will call the full Weval Mind Core
|
|
|
|
$validActions = ['autonomous_cycle', 'status', 'emergency', 'diagnose', 'report'];
|
|
|
|
if (!in_array($action, $validActions)) {
|
|
return [
|
|
'error' => 'Invalid action for mind module',
|
|
'valid_actions' => $validActions
|
|
];
|
|
}
|
|
|
|
// Call the simple API we already have
|
|
if ($action === 'autonomous_cycle') {
|
|
$result = $this->callSimpleApi('weval-mind-core-simple.php', [
|
|
'action' => 'autonomous_cycle'
|
|
]);
|
|
|
|
// Log the cycle
|
|
if (isset($result['cycle_id'])) {
|
|
$this->logCycle($result);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
return [
|
|
'module' => 'mind',
|
|
'action' => $action,
|
|
'status' => 'processing',
|
|
'message' => 'Action queued for execution'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Route to HAMID IA
|
|
*/
|
|
private function routeToHamid($action, $data) {
|
|
$validActions = ['chat', 'providers', 'stats', 'kb_search', 'kb_add'];
|
|
|
|
if (!in_array($action, $validActions)) {
|
|
return [
|
|
'error' => 'Invalid action for hamid module',
|
|
'valid_actions' => $validActions
|
|
];
|
|
}
|
|
|
|
if ($action === 'chat') {
|
|
if (empty($data['message'])) {
|
|
return ['error' => 'Message is required for chat action'];
|
|
}
|
|
|
|
// For now, use the simple interface
|
|
// Later, this will call the full HAMID Engine
|
|
return [
|
|
'response' => "HAMID IA Engine is under construction. Your message: " .
|
|
htmlspecialchars(substr($data['message'], 0, 100)),
|
|
'provider' => 'gateway',
|
|
'action' => 'chat',
|
|
'note' => 'Full HAMID Engine coming soon'
|
|
];
|
|
}
|
|
|
|
return [
|
|
'module' => 'hamid',
|
|
'action' => $action,
|
|
'status' => 'available_soon',
|
|
'message' => 'HAMID Engine module in development'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get system status
|
|
*/
|
|
private function getSystemStatus() {
|
|
try {
|
|
$dbHealth = $this->db->healthCheck();
|
|
|
|
// Check if services are running
|
|
$services = [
|
|
'apache' => $this->checkService('apache2'),
|
|
'postgresql' => $this->checkService('postgresql'),
|
|
'pmta' => $this->checkService('pmta')
|
|
];
|
|
|
|
// Get system metrics
|
|
$load = sys_getloadavg();
|
|
$memory = shell_exec("free -m | awk 'NR==2 {printf \"%.1f\", $3/$2*100}'");
|
|
$disk = shell_exec("df / | awk 'NR==2 {print $5}' | sed 's/%//'");
|
|
|
|
return [
|
|
'status' => 'operational',
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'services' => $services,
|
|
'metrics' => [
|
|
'cpu_load' => [
|
|
'1min' => $load[0] ?? 0,
|
|
'5min' => $load[1] ?? 0,
|
|
'15min' => $load[2] ?? 0
|
|
],
|
|
'memory_usage' => floatval($memory) ?: 0,
|
|
'disk_usage' => floatval($disk) ?: 0
|
|
],
|
|
'database' => $dbHealth,
|
|
'modules' => $this->modules,
|
|
'environment' => ENVIRONMENT
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
return [
|
|
'status' => 'degraded',
|
|
'error' => $e->getMessage(),
|
|
'timestamp' => date('Y-m-d H:i:s')
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Health check
|
|
*/
|
|
private function healthCheck() {
|
|
$checks = [];
|
|
|
|
// Check database
|
|
try {
|
|
$dbCheck = $this->db->healthCheck();
|
|
$checks['database'] = [
|
|
'status' => $dbCheck['status'],
|
|
'query_count' => $dbCheck['query_count']
|
|
];
|
|
} catch (Exception $e) {
|
|
$checks['database'] = [
|
|
'status' => 'unhealthy',
|
|
'error' => $e->getMessage()
|
|
];
|
|
}
|
|
|
|
// Check services
|
|
$services = ['apache2', 'postgresql', 'pmta'];
|
|
foreach ($services as $service) {
|
|
$checks['service_' . $service] = [
|
|
'status' => $this->checkService($service) ? 'running' : 'stopped'
|
|
];
|
|
}
|
|
|
|
// Check disk space
|
|
$disk = shell_exec("df / | awk 'NR==2 {print $5}' | sed 's/%//'");
|
|
$checks['disk_space'] = [
|
|
'usage_percent' => floatval($disk) ?: 0,
|
|
'status' => (floatval($disk) > 85) ? 'warning' : 'healthy'
|
|
];
|
|
|
|
// Determine overall status
|
|
$overall = 'healthy';
|
|
foreach ($checks as $check) {
|
|
if (isset($check['status']) && $check['status'] === 'unhealthy') {
|
|
$overall = 'unhealthy';
|
|
break;
|
|
}
|
|
if (isset($check['status']) && $check['status'] === 'warning') {
|
|
$overall = 'warning';
|
|
}
|
|
}
|
|
|
|
return [
|
|
'status' => $overall,
|
|
'checks' => $checks,
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'uptime' => shell_exec("uptime -p 2>/dev/null || echo 'unknown'")
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Call simple API
|
|
*/
|
|
private function callSimpleApi($endpoint, $params = []) {
|
|
$url = "http://localhost:5890/api/$endpoint";
|
|
|
|
if (!empty($params)) {
|
|
$url .= '?' . http_build_query($params);
|
|
}
|
|
|
|
$response = @file_get_contents($url);
|
|
if ($response === false) {
|
|
return ['error' => 'Failed to call API endpoint: ' . $endpoint];
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
return $data ?: ['error' => 'Invalid JSON response'];
|
|
}
|
|
|
|
/**
|
|
* Check if service is running
|
|
*/
|
|
private function checkService($service) {
|
|
$status = @shell_exec("systemctl is-active $service 2>/dev/null");
|
|
return trim($status) === 'active';
|
|
}
|
|
|
|
/**
|
|
* Log cycle to database
|
|
*/
|
|
private function logCycle($cycleData) {
|
|
try {
|
|
$cycleId = $this->db->insert('mind_cycles', [
|
|
'cycle_uuid' => $cycleData['cycle_id'] ?? 'CYCLE-' . uniqid(),
|
|
'trigger_type' => 'api_gateway',
|
|
'started_at' => $cycleData['timestamp'] ?? date('Y-m-d H:i:s'),
|
|
'ended_at' => date('Y-m-d H:i:s'),
|
|
'status' => 'completed',
|
|
'health_score' => 95, // Default for now
|
|
'issues_found' => 0,
|
|
'actions_taken' => 0,
|
|
'modules_run' => ['perception'],
|
|
'result_json' => json_encode($cycleData)
|
|
]);
|
|
|
|
logMessage('INFO', 'API Gateway', 'Cycle logged to database', [
|
|
'cycle_id' => $cycleId,
|
|
'uuid' => $cycleData['cycle_id'] ?? 'unknown'
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
logMessage('ERROR', 'API Gateway', 'Failed to log cycle: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process incoming request
|
|
*/
|
|
public function processRequest() {
|
|
try {
|
|
// Get request data
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
$clientIp = $this->getClientIp();
|
|
|
|
// Handle preflight requests
|
|
if ($method === 'OPTIONS') {
|
|
http_response_code(200);
|
|
exit;
|
|
}
|
|
|
|
// Rate limiting
|
|
if (!$this->checkRateLimit($clientIp)) {
|
|
http_response_code(429);
|
|
echo json_encode([
|
|
'error' => 'Rate limit exceeded',
|
|
'limit' => 60 . ' requests per minute',
|
|
'retry_after' => 60
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Authentication
|
|
if (!$this->authenticate()) {
|
|
http_response_code(401);
|
|
echo json_encode(['error' => 'Authentication required']);
|
|
exit;
|
|
}
|
|
|
|
// Get module and action
|
|
$module = $_GET['module'] ?? $_POST['module'] ?? 'status';
|
|
$action = $_GET['action'] ?? $_POST['action'] ?? 'status';
|
|
|
|
// Get request data
|
|
$input = file_get_contents('php://input');
|
|
$data = json_decode($input, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
$data = $_POST ?: [];
|
|
}
|
|
|
|
// Log the request
|
|
logMessage('INFO', 'API Gateway', 'Request received', [
|
|
'module' => $module,
|
|
'action' => $action,
|
|
'ip' => $clientIp,
|
|
'method' => $method
|
|
]);
|
|
|
|
// Route the request
|
|
$result = $this->routeRequest($module, $action, $data);
|
|
|
|
// Add metadata
|
|
$result['gateway'] = [
|
|
'version' => '1.0',
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'request_id' => 'REQ-' . uniqid()
|
|
];
|
|
|
|
// Return response
|
|
http_response_code(200);
|
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
|
|
} catch (Exception $e) {
|
|
logMessage('ERROR', 'API Gateway', 'Processing error: ' . $e->getMessage());
|
|
|
|
http_response_code(500);
|
|
echo json_encode([
|
|
'error' => 'Internal server error',
|
|
'message' => $e->getMessage(),
|
|
'timestamp' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle request
|
|
$gateway = new ApiGateway();
|
|
$gateway->processRequest();
|
|
?>
|