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); } }