Files
cattr/app/Http/Controllers/Api/AboutController.php
Noor E Ilahi 7ccf44f7da first commit
2026-01-09 12:54:53 +05:30

302 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Console\Commands\RotateScreenshots;
use App\Helpers\ModuleHelper;
use App\Helpers\ReportHelper;
use App\Helpers\StorageCleaner;
use App\Helpers\Version;
use Artisan;
use Cache;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Controller;
use JsonException;
use Settings;
class AboutController extends Controller
{
private Client $client;
public function __construct()
{
$this->client = new Client([
'base_uri' => config('app.stats_collector_url') . '/v2/',
'headers' => ['x-cattr-instance' => Settings::scope('core')->get('instance')],
]);
}
/**
* @api {get} /about/reports Get Available Report Formats
* @apiDescription Retrieves the list of available report formats and their corresponding MIME types.
*
* @apiVersion 4.0.0
* @apiName GetReportFormats
* @apiGroup About
*
* @apiSuccess {Object} types List of available report formats and their MIME types.
* @apiSuccess {String} types.csv MIME type for CSV format.
* @apiSuccess {String} types.xlsx MIME type for XLSX format.
* @apiSuccess {String} types.pdf MIME type for PDF format.
* @apiSuccess {String} types.xls MIME type for XLS format.
* @apiSuccess {String} types.ods MIME type for ODS format.
* @apiSuccess {String} types.html MIME type for HTML format.
*
* @apiSuccessExample {json} Success Response:
* HTTP/1.1 200 OK
* {
* "types": {
* "csv": "text/csv",
* "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
* "pdf": "application/pdf",
* "xls": "application/vnd.ms-excel",
* "ods": "application/vnd.oasis.opendocument.spreadsheet",
* "html": "text/html"
* }
* }
*
* @apiUse 400Error
* @apiUse ValidationError
* @apiUse UnauthorizedError
* @apiUse ForbiddenError
*/
public function reports(): JsonResponse
{
return responder()->success([
'types' => ReportHelper::getAvailableReportFormats(),
])->respond();
}
/**
* Returns information about this instance.
* @throws JsonException
*/
/**
* @api {get} /about Get Application and Module Information
* @apiDescription Retrieves information about the application instance, modules, and image version details.
*
* @apiVersion 4.0.0
* @apiName GetAppInfo
* @apiGroup About
*
* @apiSuccess {Object} app Application information.
* @apiSuccess {String} app.version The current version of the application.
* @apiSuccess {String} app.instance_id The unique identifier of the application instance.
* @apiSuccess {Boolean} app.vulnerable Indicates if the application is vulnerable.
* @apiSuccess {String} app.last_version The latest version available for the application.
* @apiSuccess {String} app.message Any important message related to the application.
*
* @apiSuccess {Object[]} modules List of modules integrated into the application.
* @apiSuccess {String} modules.name Name of the module.
* @apiSuccess {String} modules.version Current version of the module.
* @apiSuccess {Boolean} modules.enabled Indicates if the module is enabled.
*
* @apiSuccess {Object} image Information about the image version.
* @apiSuccess {String} image.version The version of the image (if available).
* @apiSuccess {Boolean} image.vulnerable Indicates if the image is vulnerable.
* @apiSuccess {String} image.last_version The latest available version for the image.
* @apiSuccess {String} image.message Any important message related to the image.
*
* @apiSuccessExample {json} Success Response:
* HTTP/1.1 200 OK
* {
* "app": {
* "version": "dev",
* "instance_id": null,
* "vulnerable": null,
* "last_version": null,
* "message": null
* },
* "modules": [
* {
* "name": "JiraIntegration",
* "version": "3.0.0",
* "enabled": false
* }
* ],
* "image": {
* "version": null,
* "vulnerable": null,
* "last_version": null,
* "message": null
* }
* }
*
* @apiUse 400Error
* @apiUse ValidationError
* @apiUse UnauthorizedError
* @apiUse ForbiddenError
*/
public function __invoke(): JsonResponse
{
$imageVersion = getenv('IMAGE_VERSION', true) ?: null;
$releaseInfo = $this->requestReleaseInfo();
$modulesInfo = $this->requestModulesInfo();
$imageInfo = ($imageVersion) ? $this->requestImageInfo($imageVersion) : false;
return responder()->success([
'app' => [
'version' => config('app.version'),
'instance_id' => Settings::scope('core')->get('instance'),
'vulnerable' => optional($releaseInfo)->vulnerable,
'last_version' => optional($releaseInfo)->lastVersion,
'message' => optional($releaseInfo)->flashMessage,
],
'modules' => $modulesInfo,
'image' => [
'version' => $imageVersion,
'vulnerable' => optional($imageInfo)->vulnerable,
'last_version' => optional($imageInfo)->lastVersion,
'message' => optional($imageInfo)->flashMessage,
]
])->respond();
}
private function requestReleaseInfo(): ?object
{
try {
return json_decode(
$this->client->get('release/' . config('app.version'))->getBody()->getContents(),
false,
512,
JSON_THROW_ON_ERROR | JSON_THROW_ON_ERROR
);
} catch (Exception) {
return null;
}
}
/**
* @throws JsonException
*/
private function requestModulesInfo(): array
{
$options = [
'json' => ModuleHelper::getModulesInfo(),
];
try {
return array_map(static function ($el) {
$el['version'] = (string)(new Version($el['name']));
return $el;
}, json_decode(
$this->client->post('modules', $options)->getBody()->getContents(),
true,
512,
JSON_THROW_ON_ERROR | JSON_THROW_ON_ERROR
)['modules']);
} catch (Exception) {
return ModuleHelper::getModulesInfo();
}
}
private function requestImageInfo(string $imageVersion): ?object
{
try {
return json_decode(
$this->client->get("image/$imageVersion")->getBody()->getContents(),
false,
512,
JSON_THROW_ON_ERROR | JSON_THROW_ON_ERROR
);
} catch (Exception) {
return null;
}
}
/**
* @throws BindingResolutionException
*/
/**
* @api {get} /storage Get Storage Information
* @apiDescription Retrieves information about the storage space, thinning status, and available screenshots.
*
* @apiVersion 4.0.0
* @apiName GetStorageInfo
* @apiGroup Storage
*
* @apiSuccess {Object} space Information about the storage space.
* @apiSuccess {Number} space.left The amount of free space left (in bytes).
* @apiSuccess {Number} space.used The amount of space currently used (in bytes).
* @apiSuccess {Number} space.total The total amount of storage space available (in bytes).
*
* @apiSuccess {Number} threshold The storage usage threshold percentage before thinning is needed.
* @apiSuccess {Boolean} need_thinning Indicates if the storage requires thinning.
* @apiSuccess {Number} screenshots_available The number of available screenshots in the storage.
*
* @apiSuccess {Object} thinning Information about the thinning process.
* @apiSuccess {Boolean} thinning.now Indicates if the thinning process is currently ongoing.
* @apiSuccess {String} thinning.last Timestamp of the last thinning process.
*
* @apiSuccessExample {json} Success Response:
* HTTP/1.1 200 OK
* {
* "space": {
* "left": 26579533824,
* "used": 178705711104,
* "total": 205285244928
* },
* "threshold": 75,
* "need_thinning": true,
* "screenshots_available": 0,
* "thinning": {
* "now": null,
* "last": null
* }
* }
*
* @apiUse 400Error
* @apiUse ValidationError
* @apiUse UnauthorizedError
* @apiUse ForbiddenError
*/
public function storage(): JsonResponse
{
return responder()->success([
'space' => [
'left' => StorageCleaner::getFreeSpace(),
'used' => StorageCleaner::getUsedSpace(),
'total' => config('cleaner.total_space'),
],
'threshold' => config('cleaner.threshold'),
'need_thinning' => StorageCleaner::needThinning(),
'screenshots_available' => StorageCleaner::countAvailableScreenshots(),
'thinning' => [
'now' => Cache::store('octane')->get('thinning_now'),
'last' => Cache::store('octane')->get('last_thin'),
]
])->respond();
}
/**
* @api {get} /storage Get Storage Information
* @apiDescription Retrieves information about the storage space, thinning status, and available screenshots.
*
* @apiVersion 4.0.0
* @apiName GetStorageInfo
* @apiGroup Storage
*
* @apiSuccessExample {json} Response Example
* HTTP/1.1 204 No Content
* {
* }
*
* @apiUse 400Error
* @apiUse UnauthorizedError
*
*/
public function startStorageClean(): JsonResponse
{
Artisan::queue(RotateScreenshots::class);
return responder()->success()->respond(204);
}
}