* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class ElsaServer { private $serverUrl; private $token; public $scopes; /** * Constructor. * Inicializa las variables de configuración. */ public function __construct() { $this->serverUrl = get_config('local_elsa', 'server_url'); $this->token = get_config('local_elsa', 'token'); } /** * Envía datos al servidor. * * @param array $data Datos a enviar. * @param string $action Acción a realizar. * @param string $type Tipo de solicitud (post o put). * @return mixed Respuesta del servidor. */ public function sendToServer($data = null, $action = null, $type = "post", $collectionName = null, $retries = 2) { try { $cleanUrl = preg_replace('#([^:])//+#', '\\1/', $this->serverUrl . $action); if ($type == "delete" && $collectionName) { $cleanUrl .= "/" . urlencode($collectionName); } $headers = [ 'Accept: application/json', 'Authorization: Bearer ' . $this->token ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $cleanUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($type)); if ($type == "post" || $type == "put") { if($action == '/api/v1/ai/upload'){ $multipartData = []; if (isset($data['files']) && !empty($data['files'])) { $file = array_shift($data['files']); unset($data['files']); $temp_file = tempnam(sys_get_temp_dir(), 'upload_'); file_put_contents($temp_file, $file['contents']); $multipartData['files'] = new \CURLFile($temp_file, $file['type'], $file['filename']); } $multipartData['upload_input'] = json_encode($data); curl_setopt($ch, CURLOPT_POSTFIELDS, $multipartData); curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($headers, ['Content-Type: multipart/form-data'])); } else { curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($headers, ['Content-Type: application/json'])); } curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($type)); } $response = curl_exec($ch); $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (curl_errno($ch)) { throw new Exception(curl_error($ch)); } curl_close($ch); if ($httpcode >= 200 && $httpcode < 300) { $decodedResponse = json_decode($response, true); if (json_last_error() === JSON_ERROR_NONE) { return json_decode($response); } else { return $response; } } elseif ($httpcode == 429 || $httpcode == 413) { return json_decode($response)->detail; } else { throw new Exception("HTTP Error: " . $httpcode); } } catch (Exception $e) { if ($retries > 0) { error_log('Exception during server request: ' . $e->getMessage()); return $this->sendToServer($data, $action, $type, $collectionName, $retries - 1); } else { error_log('Exception during server request: ' . $e->getMessage()); return null; } } } /** * Procesa errores y obtiene detalles del mensaje de error. * * @param Exception $e Excepción capturada. * @return string Detalle del error. */ public function processError($e){ $pattern = '/"detail":"([^"]*)"/'; if (preg_match($pattern, $e->getMessage(), $matches)) { $error_detail = $matches[1]; return $error_detail; } else { return get_string('errorServiceLLM', 'local_elsa'); } } /** * Decodifica una cadena base64url. * * @param string $data Datos codificados. * @return string Datos decodificados. */ public function base64url_decode($data) { try { $padding = 4 - (strlen($data) % 4); if ($padding < 4) { $data .= str_repeat('=', $padding); } return base64_decode(strtr($data, '-_', '+/')); } catch (Exception $e) { error_log('Excepción al base64 el JWT: ', $e->getMessage(), "\n"); } return null; } /** * Codifica una cadena a base64url. * * @param string $data Datos a codificar. * @return string Datos codificados. */ public function base64url_encode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } /** * Genera una URL para un iframe de Metabase. * * @param array $payload Datos a incluir en el payload. * @return string URL generada. */ public function getUrlMetabase($payload, $institution){ $header = json_encode([ 'alg' => 'HS256', 'typ' => 'JWT' ]); $base64UrlHeader = $this->base64url_encode($header); $base64UrlPayload = $this->base64url_encode($payload); $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $institution["report_token"], true); $base64UrlSignature = $this->base64url_encode($signature); $jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; return $institution["report_url"] . "/embed/dashboard/" . $jwt . "#theme=transparent&bordered=false&titled=false"; } /** * Valida el token verificando su expiración. * * @return institucion si el token es válido, null si no lo es. */ public function getValidateToken(): ?array{ return array('name' => "UPS"); if (session_status() == PHP_SESSION_NONE) { session_start(); } if (isset($_SESSION['institution_token']) && isset($_SESSION['token_end_date']) && isset($_SESSION['last_updated']) && $_SESSION['token_end_date'] > time() && (time() - $_SESSION['last_updated'] < 1080)) { return array( 'name' => $_SESSION['institution_token']['name'], 'description' => $_SESSION['institution_token']['description'], 'city' => $_SESSION['institution_token']['city'], 'contact_name' => $_SESSION['institution_token']['contact_name'], 'contact_email' => $_SESSION['institution_token']['contact_email'], 'contact_mobile' => $_SESSION['institution_token']['contact_mobile'], 'query_limit' => $_SESSION['institution_token']['query_limit'], 'storage_limit' => $_SESSION['institution_token']['storage_limit'], 'token_scopes' => json_decode($_SESSION['institution_token']['token_scopes']), 'report_url' => $_SESSION['institution_token']['report_url'], 'report_token' => $_SESSION['institution_token']['report_token'], 'domain' => $_SESSION['institution_token']['domain'], 'id' => $_SESSION['institution_token']['id'] ); } $institution = $this->sendToServer(null, '/api/v1/auth/instance', 'get'); if ($institution != null && strtotime($institution->token_end_date) > time()) { $_SESSION['institution_token'] = array( 'name' => $institution->name, 'description' => $institution->description, 'city' => $institution->city, 'contact_name' => $institution->contact_name, 'contact_email' => $institution->contact_email, 'contact_mobile' => $institution->contact_mobile, 'query_limit' => $institution->query_limit, 'storage_limit' => $institution->storage_limit, 'token_scopes' => json_encode($institution->token_scopes), 'report_url' =>$institution->report_url, 'report_token' => $institution->report_token, 'domain' => $institution->domain, 'id' => $institution->id, 'token_end_date' => $institution->token_end_date ); $_SESSION['last_updated'] = time(); $_SESSION['token_end_date'] = $institution->token_end_date; return $_SESSION['institution_token']; } return null; } /** * Obtiene el nombre del curso. * * @return string Nombre del curso. */ public function getNameCourse() { global $COURSE; $institution = $this->getValidateToken(); if($institution != null) return $institution['id'] . "_" . $COURSE->id; return $COURSE->id . "_Course_" . substr($COURSE->name, 0, 1); } public function sendMetrics($metrics){ if($this->getValidateToken() != null){ return $this->sendToServer($metrics, '/api/v1/metrics', 'post'); }else { error_log("Error al enviar métricas invalido token "); } return null; } }