Files
ReachableCEO 91d52d2de5 feat(cloudron): add tirreno package artifacts
- Add CloudronStack/output/CloudronPackages-Artifacts/tirreno/ directory and its contents
- Includes package manifest, Dockerfile, source code, documentation, and build artifacts
- Add tirreno-1761840148.tar.gz as a build artifact
- Add tirreno-cloudron-package-1761841304.tar.gz as the Cloudron package
- Include all necessary files for the tirreno Cloudron package

This adds the complete tirreno Cloudron package artifacts to the repository.
2025-10-30 11:43:06 -05:00

253 lines
9.0 KiB
PHP

<?php
/**
* Tirreno ~ Open source user analytics
* Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
*
* Licensed under GNU Affero General Public License version 3 of the or any later version.
* For full copyright and license information, please see the LICENSE
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
* @link https://www.tirreno.com Tirreno(tm)
*/
namespace Controllers\Admin\Enrichment;
class Data extends \Controllers\Base {
public function enrichEntity(string $type, ?string $search, ?int $entityId, int $apiKey, ?string $enrichmentKey): array {
if ($enrichmentKey === null) {
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_KEY_DOES_NOT_EXIST];
}
set_error_handler([\Utils\ErrorHandler::class, 'exceptionErrorHandler']);
$search = $search !== null ? ['value' => $search] : null;
$result = $this->enrichEntityProcess($type, $search, $entityId, $apiKey, $enrichmentKey);
restore_error_handler();
return $result;
}
private function enrichEntityProcess(string $type, ?array $search, ?int $entityId, int $apiKey, ?string $enrichmentKey): array {
$processErrorMessage = ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_UNKNOWN_ERROR];
if ($type === 'device') {
if ($entityId !== null) {
$model = new \Models\Device();
$device = $model->getFullDeviceInfoById($entityId, $apiKey);
if ($device !== []) {
$entityId = $device['ua_id'];
$type = 'ua';
} else {
return $processErrorMessage;
}
} else {
return $processErrorMessage;
}
}
$model = new \Models\ApiKeys();
$attributes = $model->enrichableAttributes($apiKey);
if (!array_key_exists($type, $attributes)) {
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_ATTRIBUTE_IS_UNAVAILABLE];
}
$modelDb = null;
$modelResult = null;
$extraModel = null;
switch ($type) {
case 'ip':
$modelDb = new \Models\Ip();
$modelResult = new \Models\Enrichment\Ip();
$extraModel = new \Models\Enrichment\LocalhostIp();
break;
case 'email':
$modelDb = new \Models\Email();
$modelResult = new \Models\Enrichment\Email();
break;
case 'domain':
$modelDb = new \Models\Domain();
$modelResult = new \Models\Enrichment\DomainFound();
$extraModel = new \Models\Enrichment\DomainNotFound();
break;
case 'phone':
$modelDb = new \Models\Phone();
$modelResult = new \Models\Enrichment\PhoneValid();
$extraModel = new \Models\Enrichment\PhoneInvalid();
break;
case 'ua':
$modelDb = new \Models\Device();
$modelResult = new \Models\Enrichment\Device();
break;
}
if ($modelDb === null) {
return $processErrorMessage;
}
$value = $entityId !== null ? $modelDb->extractById($entityId, $apiKey) : $search;
if ($value === null || $value === []) {
return $processErrorMessage;
}
$apiError = null;
try {
[$statusCode, $response,] = $this->enrichEntityByValue($type, $value, $enrichmentKey);
$error = \Utils\ApiResponseFormats::getErrorResponseFormat();
$apiError = \Utils\ApiResponseFormats::matchResponse($response[$type] ?? [], $error) ? $response[$type]['error'] : null;
} catch (\ErrorException $e) {
return $processErrorMessage;
}
if ($statusCode === 403) {
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_KEY_OVERUSE];
}
if ($type === 'ip') {
// do not raise on bogon ip
if ($apiError === \Utils\Constants::get('ENRICHMENT_IP_IS_NOT_FOUND')) {
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_IP_NOT_FOUND];
} elseif ($apiError !== null && $apiError !== \Utils\Constants::get('ENRICHMENT_IP_IS_BOGON') || $statusCode !== 200 || $response[$type] === null) {
return $processErrorMessage;
}
} elseif ($apiError !== null || $statusCode !== 200 || $response[$type] === null) {
return $processErrorMessage;
}
try {
$modelResult->init($response[$type]);
} catch (\ErrorException $e) {
if ($extraModel === null) {
return $processErrorMessage;
}
try {
$extraModel->init($response[$type]);
$modelResult = $extraModel;
} catch (\ErrorException $e) {
return $processErrorMessage;
}
}
// change value in db only if $entityId was passed
if ($entityId !== null) {
try {
$modelResult->updateEntityInDb($entityId, $apiKey);
} catch (\ErrorException $e) {
return $processErrorMessage;
}
}
return [
$type => $response[$type],
'SUCCESS_MESSAGE' => $this->f3->get('AdminEnrichment_success_message'),
];
}
private function validateResponse(string $requestType, int $statusCode, ?array $result, string $errorMessage): bool|string|int {
if (!is_array($result)) {
return \Utils\ErrorCodes::ENRICHMENT_API_UNKNOWN_ERROR;
}
if ($statusCode === 200 && is_array($result) && is_array($result[$requestType])) {
return false;
}
$details = $result['detail'] ?? null;
if ($details) {
if (is_array($details)) {
$messages = array_map(function ($detail) {
if (isset($detail['msg']) && $detail['msg'] !== null && $detail['msg'] !== '') {
return $detail['msg'];
}
}, $details);
$messages = implode('; ', $messages);
} else {
$messages = $details;
}
}
if (strlen($errorMessage) > 0) {
\Utils\Logger::log('Enrichment API web error', $errorMessage);
}
if (!isset($messages) || strlen($messages) < 1) {
return \Utils\ErrorCodes::ENRICHMENT_API_UNKNOWN_ERROR;
}
return $messages;
}
private function enrichEntityByValue(string $type, array $value, string $enrichmentKey): array {
$apiUrl = \Utils\Variables::getEnrichtmentApi();
$postFields = [
$type => $value['value'],
];
$options = [
'method' => 'POST',
'header' => [
'Content-Type: application/json',
'Authorization: Bearer ' . $enrichmentKey,
'User-Agent: ' . $this->f3->get('USER_AGENT'),
],
'content' => \json_encode($postFields),
];
/** @var array{request: array<string>, body: string, headers: array<string>, engine: string, cached: bool, error: string} $result */
$result = \Web::instance()->request(
url: sprintf('%s/query', $apiUrl),
options: $options,
);
$matches = [];
preg_match('/^HTTP\/(\d+)(?:\.\d)? (\d{3})/', $result['headers'][0], $matches);
$jsonResponse = json_decode($result['body'], true);
$statusCode = (int) ($matches[2] ?? 0);
$errorMessages = $this->validateResponse($type, $statusCode, $jsonResponse, $result['error']);
return [$statusCode, $jsonResponse, $errorMessages];
}
public function getNotCheckedEntitiesCount(int $apiKey): array {
$model = new \Models\ApiKeys();
$models = $model->enrichableAttributes($apiKey);
$result = [];
foreach ($models as $type => $model) {
$result[$type] = (new $model())->countNotChecked($apiKey);
}
return $result;
}
public function getNotCheckedExists(int $apiKey): bool {
$model = new \Models\ApiKeys();
$models = $model->enrichableAttributes($apiKey);
foreach ($models as $model) {
if ((new $model())->notCheckedExists($apiKey)) {
return true;
}
}
return false;
}
public function getNotCheckedEntitiesByUserId(int $userId, int $apiKey): array {
$model = new \Models\ApiKeys();
$models = $model->enrichableAttributes($apiKey);
$result = [];
foreach ($models as $type => $model) {
$result[$type] = (new $model())->notCheckedForUserId($userId, $apiKey);
}
return $result;
}
}