first commit
This commit is contained in:
14
app/Http/Controllers/ActuatorController.php
Normal file
14
app/Http/Controllers/ActuatorController.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
final class ActuatorController
|
||||
{
|
||||
public function __invoke(): JsonResponse
|
||||
{
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
}
|
||||
301
app/Http/Controllers/Api/AboutController.php
Normal file
301
app/Http/Controllers/Api/AboutController.php
Normal file
@@ -0,0 +1,301 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
92
app/Http/Controllers/Api/AttachmentController.php
Normal file
92
app/Http/Controllers/Api/AttachmentController.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Contracts\AttachmentService;
|
||||
use App\Enums\AttachmentStatus;
|
||||
use App\Http\Requests\Attachment\CreateAttachmentRequest;
|
||||
use App\Http\Requests\Attachment\DownloadAttachmentRequest;
|
||||
use App\Models\Attachment;
|
||||
use Filter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Throwable;
|
||||
use URL;
|
||||
|
||||
class AttachmentController extends ItemController
|
||||
{
|
||||
protected const MODEL = Attachment::class;
|
||||
|
||||
public function __construct(protected AttachmentService $attachmentService)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CreateAttachmentRequest $request
|
||||
* @return JsonResponse
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function create(CreateAttachmentRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(Filter::getActionFilterName(), static function (Attachment $attachment) {
|
||||
$attachment->load('user');
|
||||
return $attachment;
|
||||
});
|
||||
return $this->_create($request);
|
||||
}
|
||||
|
||||
public function tmpDownload(Request $request, Attachment $attachment): ?StreamedResponse
|
||||
{
|
||||
if (! $request->hasValidSignature()) {
|
||||
abort(401);
|
||||
}
|
||||
|
||||
return $this->streamDownloadLogic($attachment);
|
||||
}
|
||||
|
||||
public function createTemporaryUrl(DownloadAttachmentRequest $request, Attachment $attachment): string
|
||||
{
|
||||
// we do this because signature breaks if we use built in method for creating relative path
|
||||
// also cannot determine request scheme when ran inside docker
|
||||
$url = URL::temporarySignedRoute(
|
||||
'attachment.temporary-download',
|
||||
now()->addSeconds($request->validated('seconds') ?? 3600),
|
||||
$attachment
|
||||
);
|
||||
$parsedUrl = parse_url($url);
|
||||
|
||||
// Combine the path and query string
|
||||
return $parsedUrl['path'] . (isset($parsedUrl['query']) ? '?' . $parsedUrl['query'] : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
* @return StreamedResponse|void
|
||||
*/
|
||||
protected function streamDownloadLogic(Attachment $attachment)
|
||||
{
|
||||
if ($attachment->status === AttachmentStatus::GOOD && $this->attachmentService->fileExists($attachment)) {
|
||||
$headers = [];
|
||||
if ($attachment->mime_type !== '') {
|
||||
$headers['Content-Type'] = $attachment->mime_type;
|
||||
}
|
||||
|
||||
return response()->streamDownload(
|
||||
function () use ($attachment) {
|
||||
$stream = $this->attachmentService->readStream($attachment);
|
||||
|
||||
while (!feof($stream)) {
|
||||
echo fread($stream, 2048);
|
||||
}
|
||||
|
||||
fclose($stream);
|
||||
},
|
||||
$attachment->original_name,
|
||||
$headers
|
||||
);
|
||||
}
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
229
app/Http/Controllers/Api/CompanySettingsController.php
Normal file
229
app/Http/Controllers/Api/CompanySettingsController.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\CompanySettings\UpdateCompanySettingsRequest;
|
||||
use App\Http\Transformers\CompanySettingsTransformer;
|
||||
use App\Models\Priority;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Settings;
|
||||
|
||||
class CompanySettingsController extends Controller
|
||||
{
|
||||
/**
|
||||
* @api {get} /company-settings/ List
|
||||
* @apiDescription Returns all company settings.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName ListCompanySettings
|
||||
* @apiGroup Company Settings
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName ListCompanySettings
|
||||
* @apiGroup Company Settings
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccess {String} timezone The timezone setting for the company.
|
||||
* @apiSuccess {String} language The language setting for the company.
|
||||
* @apiSuccess {Integer} work_time The configured work time in hours.
|
||||
* @apiSuccess {Array} color Array of colors configured for the company settings.
|
||||
* @apiSuccess {Array} internal_priorities Array of internal priorities.
|
||||
* @apiSuccess {Integer} internal_priorities.id The unique ID of the priority.
|
||||
* @apiSuccess {String} internal_priorities.name The name of the priority.
|
||||
* @apiSuccess {String} internal_priorities.created_at The creation timestamp of the priority.
|
||||
* @apiSuccess {String} internal_priorities.updated_at The last update timestamp of the priority.
|
||||
* @apiSuccess {String} internal_priorities.color The color associated with the priority.
|
||||
* @apiSuccess {Integer} heartbeat_period The period for heartbeat checks in seconds.
|
||||
* @apiSuccess {Boolean} auto_thinning Indicates if automatic thinning of old data is enabled.
|
||||
* @apiSuccess {Integer} screenshots_state The current state of screenshot monitoring.
|
||||
* @apiSuccess {Integer} env_screenshots_state The environmental screenshot state.
|
||||
* @apiSuccess {Integer} default_priority_id The default priority ID.
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* {
|
||||
* "timezone": "UTC",
|
||||
* "language": "ru",
|
||||
* "work_time": 0,
|
||||
* "color": [],
|
||||
* "internal_priorities": [
|
||||
* {
|
||||
* "id": 1,
|
||||
* "name": "Normal",
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-07-12T17:57:40.000000Z",
|
||||
* "color": null
|
||||
* },
|
||||
* {
|
||||
* "id": 2,
|
||||
* "name": "Normal",
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-06-21T10:06:50.000000Z",
|
||||
* "color": "#49E637"
|
||||
* },
|
||||
* {
|
||||
* "id": 3,
|
||||
* "name": "High",
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-06-21T10:07:00.000000Z",
|
||||
* "color": "#D40C0C"
|
||||
* },
|
||||
* {
|
||||
* "id": 5,
|
||||
* "name": "Normal",
|
||||
* "created_at": "2024-07-12T17:10:54.000000Z",
|
||||
* "updated_at": "2024-07-12T17:10:54.000000Z",
|
||||
* "color": null
|
||||
* }
|
||||
*],
|
||||
* "heartbeat_period": 60,
|
||||
* "auto_thinning": true,
|
||||
* "screenshots_state": 1,
|
||||
* "env_screenshots_state": -1,
|
||||
* "default_priority_id": 2
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
return responder()->success(
|
||||
array_merge(
|
||||
Settings::scope('core')->all(),
|
||||
['internal_priorities' => Priority::all()]
|
||||
),
|
||||
new CompanySettingsTransformer
|
||||
)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UpdateCompanySettingsRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*
|
||||
* @api {patch} /company-settings/ Update
|
||||
* @apiDescription Updates the specified company settings.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName UpdateCompanySettings
|
||||
* @apiGroup Company Settings
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {String} timezone The timezone setting for the company.
|
||||
* @apiParam {String} language The language setting for the company.
|
||||
* @apiParam {Integer} work_time The configured work time in hours.
|
||||
* @apiParam {Array} color Array of colors configured for the company settings.
|
||||
* @apiParam {Array} internal_priorities Array of internal priorities.
|
||||
* @apiParam {Integer} internal_priorities.id The unique ID of the priority.
|
||||
* @apiParam {String} internal_priorities.name The name of the priority.
|
||||
* @apiParam {String} internal_priorities.created_at The creation timestamp of the priority.
|
||||
* @apiParam {String} internal_priorities.updated_at The last update timestamp of the priority.
|
||||
* @apiParam {String} internal_priorities.color The color associated with the priority.
|
||||
* @apiParam {Integer} heartbeat_period The period for heartbeat checks in seconds.
|
||||
* @apiParam {Boolean} auto_thinning Indicates if automatic thinning of old data is enabled.
|
||||
* @apiParam {Integer} screenshots_state The current state of screenshot monitoring.
|
||||
* @apiParam {Integer} env_screenshots_state The environmental screenshot state.
|
||||
* @apiParam {Integer} default_priority_id The default priority ID.
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* { "timezone" : "Europe/Moscow",
|
||||
* "language" : "en",
|
||||
* "work_time" : 0,
|
||||
* "color" : [],
|
||||
* "internal_priorities" : [
|
||||
* {
|
||||
* "id" : 1,
|
||||
* "name" : "Normal",
|
||||
* "created_at" : "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at" : "2024-07-12T17:57:40.000000Z",
|
||||
* "color" : null
|
||||
* },
|
||||
* {
|
||||
* "id" : 2,
|
||||
* "name" : "Normal",
|
||||
* "created_at" : "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at" : "2024-06-21T10:06:50.000000Z",
|
||||
* "color" : "#49E637"},
|
||||
* {
|
||||
* "id" : 3,
|
||||
* "name" : "High",
|
||||
* "created_at" : "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at" : "2024-06-21T10:07:00.000000Z",
|
||||
* "color" : "#D40C0C"
|
||||
* },{
|
||||
* "id" : 5,
|
||||
* "name" : "Normal",
|
||||
* "created_at" : "2024-07-12T17:10:54.000000Z",
|
||||
* "updated_at" : "2024-07-12T17:10:54.000000Z",
|
||||
* "color" : null
|
||||
* }
|
||||
* ],
|
||||
* "heartbeat_period" : 60,
|
||||
* "auto_thinning" : true,
|
||||
* "screenshots_state" : 1,
|
||||
* "env_screenshots_state" : -1,
|
||||
* "default_priority_id" : 2
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Array} data Contains an array of settings that changes were applied to
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 204 No Content
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function update(UpdateCompanySettingsRequest $request): JsonResponse
|
||||
{
|
||||
Settings::scope('core')->set($request->validated());
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
/**
|
||||
* @api {get} /offline-sync/public-key Get Offline Sync Public Key
|
||||
* @apiDescription Retrieves the public key for offline synchronization.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetOfflineSyncPublicKey
|
||||
* @apiGroup OfflineSync
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission offline_sync_view
|
||||
* @apiPermission offline_sync_full_access
|
||||
*
|
||||
* @apiSuccess {Boolean} success Indicates if the operation was successful.
|
||||
* @apiSuccess {Object} data The response data.
|
||||
* @apiSuccess {String} data.key The public key for offline synchronization.
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "key": null
|
||||
* }
|
||||
* @apiError (Error 401) Unauthorized The user is not authorized to access this resource.
|
||||
* @apiError (Error 403) Forbidden The user does not have the necessary permissions.
|
||||
* @apiError (Error 404) NotFound The requested resource was not found.
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ItemNotFoundError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
public function getOfflineSyncPublicKey(): JsonResponse
|
||||
{
|
||||
return responder()->success(
|
||||
[ 'key' => Settings::scope('core.offline-sync')->get('public_key')],
|
||||
)->respond();
|
||||
}
|
||||
}
|
||||
1046
app/Http/Controllers/Api/IntervalController.php
Normal file
1046
app/Http/Controllers/Api/IntervalController.php
Normal file
File diff suppressed because it is too large
Load Diff
306
app/Http/Controllers/Api/InvitationController.php
Normal file
306
app/Http/Controllers/Api/InvitationController.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Requests\Invitation\CreateInvitationRequest;
|
||||
use App\Http\Requests\Invitation\ListInvitationRequest;
|
||||
use App\Http\Requests\Invitation\DestroyInvitationRequest;
|
||||
use App\Http\Requests\Invitation\ShowInvitationRequest;
|
||||
use App\Http\Requests\Invitation\UpdateInvitationRequest;
|
||||
use App\Models\Invitation;
|
||||
use App\Services\InvitationService;
|
||||
use Exception;
|
||||
use Filter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Throwable;
|
||||
|
||||
class InvitationController extends ItemController
|
||||
{
|
||||
protected const MODEL = Invitation::class;
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /invitations/show Show
|
||||
* @apiDescription Show invitation.
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Show Invitation
|
||||
* @apiGroup Invitation
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id Invitation ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Array} res Array of records containing the id, email, key, expiration date and role id
|
||||
*
|
||||
* @apiUse InvitationObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "res": [
|
||||
* {
|
||||
* "id": 1
|
||||
* "email": "test@example.com",
|
||||
* "key": "06d4a090-9675-11ea-bf39-5f84c549e29c",
|
||||
* "expires_at": "2020-01-01T00:00:00.000000Z",
|
||||
* "role_id": 1
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function show(ShowInvitationRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @api {get} /invitations/list List
|
||||
* @apiDescription Get list of invitations.
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Invitation List
|
||||
* @apiGroup Invitation
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccess {Array} res Array of records containing the id, email, key, expiration date and role id
|
||||
*
|
||||
* @apiUse InvitationObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "res": [
|
||||
* {
|
||||
* "id": 1
|
||||
* "email": "test@example.com",
|
||||
* "key": "06d4a090-9675-11ea-bf39-5f84c549e29c",
|
||||
* "expires_at": "2020-01-01T00:00:00.000000Z",
|
||||
* "role_id": 1
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function index(ListInvitationRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_index($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CreateInvitationRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @api {post} /invitations/create Create
|
||||
* @apiDescription Creates a unique invitation token and sends an email to the users
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Create Invitation
|
||||
* @apiGroup Invitation
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Array} users List of users to send an invitation to
|
||||
* @apiParam {String} users.email User email
|
||||
* @apiParam {Integer} users.role_id ID of the role that will be assigned to the created user
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "users": [
|
||||
* {
|
||||
* "email": "test@example.com",
|
||||
* "role_id": 1
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} res Array of records containing the id, email, key, expiration date and role id
|
||||
*
|
||||
* @apiUse InvitationObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "res": [
|
||||
* {
|
||||
* "id": 1
|
||||
* "email": "test@example.com",
|
||||
* "key": "06d4a090-9675-11ea-bf39-5f84c549e29c",
|
||||
* "expires_at": "2020-01-01T00:00:00.000000Z",
|
||||
* "role_id": 1
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @apiErrorExample {json} Email is not specified
|
||||
* HTTP/1.1 400 Bad Request
|
||||
* {
|
||||
* "error_type": "validation",
|
||||
* "message": "Validation error",
|
||||
* "info": {
|
||||
* "users.0.email": [
|
||||
* "The email field is required."
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiErrorExample {json} Email already exists
|
||||
* HTTP/1.1 400 Bad Request
|
||||
* {
|
||||
* "error_type": "validation",
|
||||
* "message": "Validation error",
|
||||
* "info": {
|
||||
* "users.0.email": [
|
||||
* "The email test@example.com has already been taken."
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function create(CreateInvitationRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
$invitations = [];
|
||||
|
||||
foreach ($requestData['users'] as $user) {
|
||||
$invitations[] = InvitationService::create($user);
|
||||
}
|
||||
|
||||
return responder()->success($invitations)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UpdateInvitationRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*
|
||||
* @api {post} /invitations/resend Resend
|
||||
* @apiDescription Updates the token expiration date and sends an email to the user's email address.
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Update Invitation
|
||||
* @apiGroup Invitation
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id Invitation ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Array} res Invitation data
|
||||
*
|
||||
* @apiUse InvitationObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "res": {
|
||||
* "id": 1
|
||||
* "email": "test@example.com",
|
||||
* "key": "06d4a090-9675-11ea-bf39-5f84c549e29c",
|
||||
* "expires_at": "2020-01-01T00:00:00.000000Z",
|
||||
* "role_id": 1
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiErrorExample {json} The id does not exist
|
||||
* HTTP/1.1 400 Bad Request
|
||||
* {
|
||||
* "error_type": "validation",
|
||||
* "message": "Validation error",
|
||||
* "info": {
|
||||
* "id": [
|
||||
* "The selected id is invalid."
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function resend(UpdateInvitationRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
$invitation = InvitationService::update($requestData['id']);
|
||||
|
||||
return responder()->success($invitation)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /invitations/remove Destroy
|
||||
* @apiDescription Destroy User
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Destroy Invitation
|
||||
* @apiGroup Invitation
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id ID of the target invitation
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} message Destroy status
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "message": "Item has been removed"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function destroy(DestroyInvitationRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_destroy($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ListInvitationRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
/**
|
||||
* @api {get} /api/invitations/count Count Invitations
|
||||
* @apiDescription Returns the total count of invitations
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName CountInvitations
|
||||
* @apiGroup Invitations
|
||||
*
|
||||
* @apiUse TotalSuccess
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function count(ListInvitationRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_count($request);
|
||||
}
|
||||
}
|
||||
648
app/Http/Controllers/Api/ItemController.php
Normal file
648
app/Http/Controllers/Api/ItemController.php
Normal file
@@ -0,0 +1,648 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Requests\CattrFormRequest;
|
||||
use Filter;
|
||||
use App\Helpers\QueryHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use CatEvent;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Throwable;
|
||||
|
||||
abstract class ItemController extends Controller
|
||||
{
|
||||
protected const MODEL = Model::class;
|
||||
|
||||
/**
|
||||
* @apiDefine ItemNotFoundError
|
||||
* @apiErrorExample {json} No such item
|
||||
* HTTP/1.1 404 Not Found
|
||||
* {
|
||||
* "message": "Item not found",
|
||||
* "error_type": "query.item_not_found"
|
||||
* }
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine ValidationError
|
||||
* @apiErrorExample {json} Validation error
|
||||
* HTTP/1.1 400 Bad Request
|
||||
* {
|
||||
* "message": "Validation error",
|
||||
* "error_type": "validation",
|
||||
* "info": "Invalid id"
|
||||
* }
|
||||
*
|
||||
* @apiError (Error 400) {String} info Validation errors
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
*/
|
||||
/**
|
||||
* @apiDefine ProjectIDParam
|
||||
* @apiParam {Integer} id Project ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @apiDefine ProjectObject
|
||||
*
|
||||
* @apiSuccess {Integer} id Project ID
|
||||
* @apiSuccess {Integer} company_id Company ID
|
||||
* @apiSuccess {String} name Project name
|
||||
* @apiSuccess {String} description Project description
|
||||
* @apiSuccess {String} deleted_at Project deletion date or null
|
||||
* @apiSuccess {String} created_at Project creation date
|
||||
* @apiSuccess {String} updated_at Project update date
|
||||
* @apiSuccess {Boolean} important Project importance
|
||||
* @apiSuccess {String} source Project source
|
||||
* @apiSuccess {Integer} default_priority_id Default priority ID or null
|
||||
* @apiSuccess {Object[]} phases List of project phases
|
||||
* @apiSuccess {Integer} phases.id Phase ID
|
||||
* @apiSuccess {String} phases.name Phase name
|
||||
* @apiSuccess {String} phases.description Phase description
|
||||
* @apiSuccess {Integer} phases.tasks_count Number of tasks in the phase
|
||||
* @apiSuccess {String} phases.created_at Phase creation date
|
||||
* @apiSuccess {String} phases.updated_at Phase update date
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "company_id": 1,
|
||||
* "name": "Dolores voluptates.",
|
||||
* "description": "Deleniti maxime fugit nesciunt. Ut maiores deleniti tempora vel. Nisi aut doloremque accusantium tempore aut.",
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "important": 1,
|
||||
* "source": "internal",
|
||||
* "default_priority_id": null,
|
||||
* "phases": []
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @apiDefine UserObject
|
||||
* @apiSuccess {Integer} id User ID.
|
||||
* @apiSuccess {String} full_name Full name of the user.
|
||||
* @apiSuccess {String} email Email address of the user.
|
||||
* @apiSuccess {String} url URL associated with the user (if any).
|
||||
* @apiSuccess {Integer} company_id ID of the company the user belongs to.
|
||||
* @apiSuccess {String} avatar URL of the user's avatar (if any).
|
||||
* @apiSuccess {Boolean} screenshots_state State of screenshot capturing.
|
||||
* @apiSuccess {Boolean} manual_time Indicates whether manual time tracking is enabled.
|
||||
* @apiSuccess {Integer} computer_time_popup Time in seconds for the computer time popup.
|
||||
* @apiSuccess {Boolean} blur_screenshots Indicates if screenshots are blurred.
|
||||
* @apiSuccess {Boolean} web_and_app_monitoring Indicates if web and app monitoring is enabled.
|
||||
* @apiSuccess {Integer} screenshots_interval Interval for capturing screenshots in minutes.
|
||||
* @apiSuccess {Boolean} active Indicates if the user account is active.
|
||||
* @apiSuccess {String} created_at Timestamp of when the user was created.
|
||||
* @apiSuccess {String} updated_at Timestamp of when the user was last updated.
|
||||
* @apiSuccess {String} deleted_at Timestamp of when the user was deleted (if applicable).
|
||||
* @apiSuccess {String} timezone User's timezone.
|
||||
* @apiSuccess {Boolean} important Indicates if the user is marked as important.
|
||||
* @apiSuccess {Boolean} change_password Indicates if the user is required to change their password.
|
||||
* @apiSuccess {Integer} role_id Role ID associated with the user.
|
||||
* @apiSuccess {String} user_language Language preference of the user.
|
||||
* @apiSuccess {String} type User type (e.g., employee, admin).
|
||||
* @apiSuccess {Boolean} invitation_sent Indicates if an invitation has been sent to the user.
|
||||
* @apiSuccess {Boolean} client_installed Indicates if the tracking client is installed.
|
||||
* @apiSuccess {Boolean} permanent_screenshots Indicates if permanent screenshots are enabled.
|
||||
* @apiSuccess {String} last_activity Timestamp of the user's last activity.
|
||||
* @apiSuccess {Boolean} screenshots_state_locked Indicates if the screenshot state is locked.
|
||||
* @apiSuccess {Boolean} online Indicates if the user is currently online.
|
||||
* @apiSuccess {Boolean} can_view_team_tab Indicates if the user can view the team tab.
|
||||
* @apiSuccess {Boolean} can_create_task Indicates if the user can create tasks.
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "full_name": "Admin",
|
||||
* "email": "admin@cattr.app",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_state": 1,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-08-20T09:22:02.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 0,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2024-08-20 09:22:02",
|
||||
* "screenshots_state_locked": false,
|
||||
* "online": false,
|
||||
* "can_view_team_tab": true,
|
||||
* "can_create_task": true
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine UserParams
|
||||
*/
|
||||
/**
|
||||
* @apiDefine User
|
||||
* @apiSuccess {String} access_token Token
|
||||
* @apiSuccess {String} token_type Token Type
|
||||
* @apiSuccess {String} expires_in Token TTL (ISO 8601 Date)
|
||||
* @apiSuccess {Object} user User Entity
|
||||
* @apiSuccess {Integer} user.id ID of the user
|
||||
* @apiSuccess {String} user.full_name Full name of the user
|
||||
* @apiSuccess {String} user.email Email of the user
|
||||
* @apiSuccess {String} [user.url] URL of the user (optional)
|
||||
* @apiSuccess {Integer} user.company_id Company ID of the user
|
||||
* @apiSuccess {String} [user.avatar] Avatar URL of the user (optional)
|
||||
* @apiSuccess {Boolean} user.screenshots_active Indicates if screenshots are active
|
||||
* @apiSuccess {Boolean} user.manual_time Indicates if manual time tracking is allowed
|
||||
* @apiSuccess {Integer} user.computer_time_popup Time interval for computer time popup
|
||||
* @apiSuccess {Boolean} user.blur_screenshots Indicates if screenshots are blurred
|
||||
* @apiSuccess {Boolean} user.web_and_app_monitoring Indicates if web and app monitoring is enabled
|
||||
* @apiSuccess {Integer} user.screenshots_interval Interval for taking screenshots
|
||||
* @apiSuccess {Boolean} user.active Indicates if the user is active
|
||||
* @apiSuccess {String} [user.deleted_at] Deletion timestamp (if applicable, otherwise null)
|
||||
* @apiSuccess {String} user.created_at Creation timestamp
|
||||
* @apiSuccess {String} user.updated_at Last update timestamp
|
||||
* @apiSuccess {String} [user.timezone] Timezone of the user (optional)
|
||||
* @apiSuccess {Boolean} user.important Indicates if the user is marked as important
|
||||
* @apiSuccess {Boolean} user.change_password Indicates if the user needs to change password
|
||||
* @apiSuccess {Integer} user.role_id Role ID of the user
|
||||
* @apiSuccess {String} user.user_language Language of the user
|
||||
* @apiSuccess {String} user.type Type of the user (e.g., "employee")
|
||||
* @apiSuccess {Boolean} user.invitation_sent Indicates if invitation is sent to the user
|
||||
* @apiSuccess {Integer} user.nonce Nonce value of the user
|
||||
* @apiSuccess {Boolean} user.client_installed Indicates if client is installed
|
||||
* @apiSuccess {Boolean} user.permanent_screenshots Indicates if screenshots are permanent
|
||||
* @apiSuccess {String} user.last_activity Last activity timestamp of the user
|
||||
* @apiSuccess {Boolean} user.online Indicates if the user is online
|
||||
* @apiSuccess {Boolean} user.can_view_team_tab Indicates if the user can view team tab
|
||||
* @apiSuccess {Boolean} user.can_create_task Indicates if the user can create tasks
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "access_token": "51|d6HvWGk6zY1aqqRms5pkp6Pb6leBs7zaW4IAWGvQ5d00b8be",
|
||||
* "token_type": "bearer",
|
||||
* "expires_in": "2024-07-12T11:59:31+00:00",
|
||||
* "user": {
|
||||
* "id": 1,
|
||||
* "full_name": "Admin",
|
||||
* "email": "johndoe@example.com",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_active": 1,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-02-15T19:06:42.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 0,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2023-10-26 10:26:17",
|
||||
* "online": false,
|
||||
* "can_view_team_tab": true,
|
||||
* "can_create_task": true
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @apiDefine ScreenshotObject
|
||||
* @apiSuccess {Integer} id ID of the screenshot
|
||||
* @apiSuccess {Integer} time_interval_id Time interval ID to which the screenshot belongs
|
||||
* @apiSuccess {String} path Path to the screenshot
|
||||
* @apiSuccess {String} created_at Timestamp when the screenshot was created
|
||||
* @apiSuccess {String} updated_at Timestamp when the screenshot was last updated
|
||||
* @apiSuccess {String} [deleted_at] Timestamp when the screenshot was deleted (if applicable)
|
||||
* @apiSuccess {String} [thumbnail_path] Path to the thumbnail of the screenshot (if applicable)
|
||||
* @apiSuccess {Boolean} important Indicates if the screenshot is marked as important
|
||||
* @apiSuccess {Boolean} is_removed Indicates if the screenshot is removed
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "data": {
|
||||
* "id": 1,
|
||||
* "time_interval_id": 1,
|
||||
* "path": "uploads\/screenshots\/1_1_1.png",
|
||||
* "created_at": "2020-01-23T09:42:26+00:00",
|
||||
* "updated_at": "2020-01-23T09:42:26+00:00",
|
||||
* "deleted_at": null,
|
||||
* "thumbnail_path": null,
|
||||
* "important": false,
|
||||
* "is_removed": false
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine ScreenshotParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine TimeIntervalObject
|
||||
* @apiSuccess {Integer} id ID of the time interval
|
||||
* @apiSuccess {Integer} task_id ID of the task
|
||||
* @apiSuccess {String} start_at Start timestamp of the time interval
|
||||
* @apiSuccess {String} end_at End timestamp of the time interval
|
||||
* @apiSuccess {String} created_at Creation timestamp
|
||||
* @apiSuccess {String} updated_at Last update timestamp
|
||||
* @apiSuccess {String} [deleted_at] Deletion timestamp (if applicable)
|
||||
* @apiSuccess {String} user_id The ID of the user
|
||||
* @apiSuccess {Boolean} is_manual Indicates whether the time was logged manually (true) or automatically
|
||||
* @apiSuccess {Integer} activity_fill Activity fill percentage
|
||||
* @apiSuccess {Integer} mouse_fill Mouse activity fill percentage
|
||||
* @apiSuccess {Integer} keyboard_fill Keyboard activity fill percentage
|
||||
* @apiSuccess {Integer} location Additional location information, if available
|
||||
* @apiSuccess {Integer} screenshot_id The ID of the screenshot associated with this interval
|
||||
* @apiSuccess {Integer} has_screenshot Indicates if there is a screenshot for this interval
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* {
|
||||
* "id": 1,
|
||||
* "task_id": 1,
|
||||
* "start_at": "2023-10-26 10:21:17",
|
||||
* "end_at": "2023-10-26 10:26:17",
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "deleted_at": null,
|
||||
* "user_id": 2,
|
||||
* "is_manual": false,
|
||||
* "activity_fill": 60,
|
||||
* "mouse_fill": 47,
|
||||
* "keyboard_fill": 13,
|
||||
* "location": null,
|
||||
* "screenshot_id": null,
|
||||
* "has_screenshot": true
|
||||
* },...
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine TimeIntervalParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine InvitationObject
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine PriorityObject
|
||||
* @apiSuccess {Boolean} status Indicates if the request was successful
|
||||
* @apiSuccess {Boolean} success Indicates if the request was successful
|
||||
* @apiSuccess {Object} data Response object
|
||||
* @apiSuccess {Integer} data.id Priority ID
|
||||
* @apiSuccess {String} data.name Priority name
|
||||
* @apiSuccess {String} data.color Priority color (can be null)
|
||||
* @apiSuccess {String} data.created_at Creation timestamp
|
||||
* @apiSuccess {String} data.updated_at Update timestamp
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": {
|
||||
* "id": 1,
|
||||
* "name": "Normal",
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-07-12T17:57:40.000000Z",
|
||||
* "color": null
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine ProjectParams
|
||||
* @apiParam {Object} [filters] Filters to apply to the project list.
|
||||
* @apiParam {Object} [filters.id] Filter by project ID.
|
||||
* @apiParam {String} [filters.name] Filter by project name.
|
||||
* @apiParam {String} [filters.description] Filter by project description.
|
||||
* @apiParam {String} [filters.created_at] Filter by project creation date.
|
||||
* @apiParam {String} [filters.updated_at] Filter by project update date.
|
||||
* @apiParam {Integer} [page=1] Page number for pagination.
|
||||
* @apiParam {Integer} [perPage=15] Number of items per page.
|
||||
*/
|
||||
/**
|
||||
* @apiDefine StatusObject
|
||||
* @apiSuccess {Number} id The ID of the status.
|
||||
* @apiSuccess {String} name The name of the status.
|
||||
* @apiSuccess {Boolean} active Indicates if the status is active.
|
||||
* @apiSuccess {String} color The color of the status (in HEX).
|
||||
* @apiSuccess {Number} order The sort order of the status.
|
||||
* @apiSuccess {String} created_at The creation timestamp.
|
||||
* @apiSuccess {String} updated_at The last update timestamp.
|
||||
*
|
||||
* @apiSuccessExample {json} Success Response Example:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "name": "Normal",
|
||||
* "active": true,
|
||||
* "color": "#363334",
|
||||
* "order": 1,
|
||||
* "created_at": "2024-08-26T10:47:30.000000Z",
|
||||
* "updated_at": "2024-08-26T10:48:35.000000Z"
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @apiDefine ParamTimeInterval
|
||||
*
|
||||
* @apiParam {String} start_at The start datetime for the interval
|
||||
* @apiParam {String} end_at The end datetime for the interval
|
||||
* @apiParam {Integer} user_id The ID of the user
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "start_at": "2024-08-16T12:32:11.000000Z",
|
||||
* "end_at": "2024-08-17T12:32:11.000000Z",
|
||||
* "user_id": 1
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @apiDefine AuthHeader
|
||||
* @apiHeader {String} Authorization Token for user auth
|
||||
* @apiHeaderExample {json} Authorization Header Example
|
||||
* {
|
||||
* "Authorization": "bearer 16184cf3b2510464a53c0e573c75740540fe..."
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @apiDefine 400Error
|
||||
* @apiError (Error 4xx) {String} message Message from server
|
||||
* @apiError (Error 4xx) {Boolean} success Indicates erroneous response when `FALSE`
|
||||
* @apiError (Error 4xx) {String} error_type Error type
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @apiDefine UnauthorizedError
|
||||
* @apiErrorExample {json} Unauthorized
|
||||
* HTTP/1.1 401 Unauthorized
|
||||
* {
|
||||
* "message": "Not authorized",
|
||||
* "error_type": "authorization.unauthorized"
|
||||
* }
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
*/
|
||||
/**
|
||||
* @apiDefine ForbiddenError
|
||||
* @apiErrorExample {json} Forbidden
|
||||
* HTTP/1.1 403 Forbidden
|
||||
* {
|
||||
* "message": "Access denied to this item",
|
||||
* "error_type": "authorization.forbidden"
|
||||
* }
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
*/
|
||||
/**
|
||||
* @apiDefine TotalSuccess
|
||||
* @apiSuccess {Boolean} success Indicates if the request was successful
|
||||
* @apiSuccess {Object} data The response data
|
||||
* @apiSuccess {Integer} data.total The total count of items
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": {
|
||||
* "total": 0
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function _index(CattrFormRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
$itemsQuery = $this->getQuery($requestData);
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $requestData);
|
||||
|
||||
$items = $request->header('X-Paginate', true) !== 'false' ? $itemsQuery->paginate() : $itemsQuery->get();
|
||||
|
||||
Filter::process(
|
||||
Filter::getActionFilterName(),
|
||||
$items,
|
||||
);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$items, $requestData]);
|
||||
|
||||
return responder()->success($items)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getQuery(array $filter = []): Builder
|
||||
{
|
||||
$model = static::MODEL;
|
||||
$model = new $model;
|
||||
|
||||
$query = new Builder($model::getQuery());
|
||||
$query->setModel($model);
|
||||
|
||||
$modelScopes = $model->getGlobalScopes();
|
||||
|
||||
foreach ($modelScopes as $key => $value) {
|
||||
$query->withGlobalScope($key, $value);
|
||||
}
|
||||
|
||||
foreach (Filter::process(Filter::getQueryAdditionalRelationsFilterName(), []) as $with) {
|
||||
$query->with($with);
|
||||
}
|
||||
|
||||
foreach (Filter::process(Filter::getQueryAdditionalRelationsSumFilterName(), []) as $withSum) {
|
||||
$query->withSum(...$withSum);
|
||||
}
|
||||
|
||||
QueryHelper::apply($query, $model, $filter);
|
||||
|
||||
return Filter::process(
|
||||
Filter::getQueryFilterName(),
|
||||
$query
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function _create(CattrFormRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), [$requestData]);
|
||||
|
||||
/** @var Model $cls */
|
||||
$cls = static::MODEL;
|
||||
|
||||
$item = Filter::process(
|
||||
Filter::getActionFilterName(),
|
||||
$cls::create($requestData),
|
||||
);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$item, $requestData]);
|
||||
|
||||
return responder()->success($item)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function _edit(CattrFormRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
throw_unless(is_int($request->get('id')), ValidationException::withMessages(['Invalid id']));
|
||||
|
||||
$itemsQuery = $this->getQuery();
|
||||
|
||||
/** @var Model $item */
|
||||
$item = $itemsQuery->get()->collect()->firstWhere('id', $request->get('id'));
|
||||
|
||||
if (!$item) {
|
||||
/** @var Model $cls */
|
||||
$cls = static::MODEL;
|
||||
throw_if($cls::find($request->get('id'))?->count(), new AccessDeniedHttpException);
|
||||
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), [$item, $requestData]);
|
||||
|
||||
$item = Filter::process(Filter::getActionFilterName(), $item->fill($requestData));
|
||||
$item->save();
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$item, $requestData]);
|
||||
|
||||
return responder()->success($item)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function _destroy(CattrFormRequest $request): JsonResponse
|
||||
{
|
||||
$requestId = Filter::process(Filter::getRequestFilterName(), $request->validated('id'));
|
||||
|
||||
throw_unless(is_int($requestId), ValidationException::withMessages(['Invalid id']));
|
||||
|
||||
$itemsQuery = $this->getQuery(['where' => ['id' => $requestId]]);
|
||||
|
||||
/** @var Model $item */
|
||||
$item = $itemsQuery->first();
|
||||
if (!$item) {
|
||||
/** @var Model $cls */
|
||||
$cls = static::MODEL;
|
||||
throw_if($cls::find($requestId)?->count(), new AccessDeniedHttpException);
|
||||
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $requestId);
|
||||
|
||||
CatEvent::dispatch(
|
||||
Filter::getAfterActionEventName(),
|
||||
tap(
|
||||
Filter::process(Filter::getActionFilterName(), $item),
|
||||
static fn ($item) => $item->delete(),
|
||||
)
|
||||
);
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _count(CattrFormRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $requestData);
|
||||
|
||||
$itemsQuery = $this->getQuery($requestData);
|
||||
|
||||
$count = Filter::process(Filter::getActionFilterName(), $itemsQuery->count());
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$count, $requestData]);
|
||||
|
||||
return responder()->success(['total' => $count])->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
protected function _show(CattrFormRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
$itemId = (int)$requestData['id'];
|
||||
|
||||
throw_unless($itemId, ValidationException::withMessages(['Invalid id']));
|
||||
|
||||
$filters = [
|
||||
'where' => ['id' => $itemId]
|
||||
];
|
||||
|
||||
if (!empty($requestData['with'])) {
|
||||
$filters['with'] = $requestData['with'];
|
||||
}
|
||||
|
||||
if (!empty($requestData['withSum'])) {
|
||||
$filters['withSum'] = $requestData['withSum'];
|
||||
}
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $filters);
|
||||
|
||||
$itemsQuery = $this->getQuery($filters ?: []);
|
||||
|
||||
$item = Filter::process(Filter::getActionFilterName(), $itemsQuery->first());
|
||||
|
||||
throw_unless($item, new NotFoundHttpException);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$item, $filters]);
|
||||
|
||||
return responder()->success($item)->respond();
|
||||
}
|
||||
}
|
||||
220
app/Http/Controllers/Api/PriorityController.php
Normal file
220
app/Http/Controllers/Api/PriorityController.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Requests\Priority\CreatePriorityRequest;
|
||||
use App\Http\Requests\Priority\DestroyPriorityRequest;
|
||||
use App\Http\Requests\Priority\ListPriorityRequest;
|
||||
use App\Http\Requests\Priority\ShowPriorityRequest;
|
||||
use App\Http\Requests\Priority\UpdatePriorityRequest;
|
||||
use App\Models\Priority;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Throwable;
|
||||
|
||||
class PriorityController extends ItemController
|
||||
{
|
||||
protected const MODEL = Priority::class;
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /priorities/show Show
|
||||
* @apiDescription Show priority.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Show Priority
|
||||
* @apiGroup Priority
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id Priority ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object} res Priority
|
||||
*
|
||||
* @apiUse PriorityObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "res": {
|
||||
* "id": 1
|
||||
* "name": "Normal",
|
||||
* "color": null
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function show(ShowPriorityRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @api {get} /priorities/list List
|
||||
* @apiDescription Get list of priorities.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Priority List
|
||||
* @apiGroup Priority
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccess {Object} res Priority
|
||||
*
|
||||
* @apiUse PriorityObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "res": [{
|
||||
* "id": 1
|
||||
* "name": "Normal",
|
||||
* "color": null
|
||||
* }]
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function index(ListPriorityRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_index($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CreatePriorityRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
* @api {post} /priorities/create Create
|
||||
* @apiDescription Creates priority
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Create Priority
|
||||
* @apiGroup Priority
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {String} name Priority name
|
||||
* @apiParam {String} color Priority color
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "name": "Normal",
|
||||
* "color": null
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object} res Priority
|
||||
*
|
||||
* @apiUse PriorityObject
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function create(CreatePriorityRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_create($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /priorities/edit Edit
|
||||
* @apiDescription Edit Priority
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Edit
|
||||
* @apiGroup Priority
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id ID
|
||||
* @apiParam {String} name Priority name
|
||||
* @apiParam {String} color Priority color
|
||||
*
|
||||
* @apiParamExample {json} Simple Request Example
|
||||
* {
|
||||
* "id": 1,
|
||||
* "name": "Normal",
|
||||
* "color": null
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object} res Priority
|
||||
*
|
||||
* @apiUse PriorityObject
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ItemNotFoundError
|
||||
*/
|
||||
public function edit(UpdatePriorityRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_edit($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /priorities/remove Destroy
|
||||
* @apiDescription Destroy User
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Destroy Priority
|
||||
* @apiGroup Priority
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id ID of the target priority
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} message Destroy status
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "message": "Item has been removed"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function destroy(DestroyPriorityRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_destroy($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /api/priorities/count Count Priorities
|
||||
* @apiDescription Returns the total count of priorities
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName CountPriorities
|
||||
* @apiGroup Priorities
|
||||
*
|
||||
* @apiUse TotalSuccess
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
/**
|
||||
* @param ListPriorityRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function count(ListPriorityRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_count($request);
|
||||
}
|
||||
}
|
||||
521
app/Http/Controllers/Api/ProjectController.php
Normal file
521
app/Http/Controllers/Api/ProjectController.php
Normal file
@@ -0,0 +1,521 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Requests\Project\CreateProjectRequest;
|
||||
use App\Http\Requests\Project\EditProjectRequest;
|
||||
use App\Http\Requests\Project\DestroyProjectRequest;
|
||||
use App\Http\Requests\Project\GanttDataRequest;
|
||||
use App\Http\Requests\Project\ListProjectRequest;
|
||||
use App\Http\Requests\Project\PhasesRequest;
|
||||
use App\Http\Requests\Project\ShowProjectRequest;
|
||||
use CatEvent;
|
||||
use Filter;
|
||||
use App\Models\Project;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use DB;
|
||||
use Staudenmeir\LaravelAdjacencyList\Eloquent\Builder as AdjacencyListBuilder;
|
||||
use Throwable;
|
||||
|
||||
class ProjectController extends ItemController
|
||||
{
|
||||
protected const MODEL = Project::class;
|
||||
|
||||
/**
|
||||
* @api {get, post} /projects/list List
|
||||
* @apiDescription Get list of Projects
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetProjectList
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission projects_list
|
||||
* @apiPermission projects_full_access
|
||||
*
|
||||
* @apiUse ProjectParams
|
||||
*
|
||||
* @apiParamExample {json} Simple Request Example
|
||||
* {
|
||||
* "id": [">", 1],
|
||||
* "user_id": ["=", [1,2,3]],
|
||||
* "name": ["like", "%lorem%"],
|
||||
* "description": ["like", "%lorem%"],
|
||||
* "created_at": [">", "2019-01-01 00:00:00"],
|
||||
* "updated_at": ["<", "2019-01-01 00:00:00"]
|
||||
* }
|
||||
*
|
||||
* @apiUse ProjectObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* [
|
||||
* {
|
||||
* "id": 1,
|
||||
* "company_id": 1,
|
||||
* "name": "Dolores voluptates.",
|
||||
* "description": "Deleniti maxime fugit nesciunt. Ut maiores deleniti tempora vel. Nisi aut doloremque accusantium tempore aut.",
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "important": 1,
|
||||
* "source": "internal",
|
||||
* "default_priority_id": null
|
||||
* },
|
||||
* {
|
||||
* "id": 2,
|
||||
* "company_id": 5,
|
||||
* "name": "Et veniam velit tempore.",
|
||||
* "description": "Consequatur nulla distinctio reprehenderit rerum omnis debitis. Fugit illum ratione quia harum. Optio porro consequatur enim esse.",
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:42.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:42.000000Z",
|
||||
* "important": 1,
|
||||
* "source": "internal",
|
||||
* "default_priority_id": null
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
/**
|
||||
* @param ListProjectRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(ListProjectRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_index($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /projects/gantt-data Gantt Data
|
||||
* @apiDescription Получение данных для диаграммы Ганта по проекту
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetGanttData
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
* @apiUse ProjectIDParam
|
||||
*
|
||||
* @apiSuccess {Integer} id Project ID
|
||||
* @apiSuccess {Integer} company_id Company ID
|
||||
* @apiSuccess {String} name Project name
|
||||
* @apiSuccess {String} description Project description
|
||||
* @apiSuccess {String} deleted_at Deletion date (null if not deleted)
|
||||
* @apiSuccess {String} created_at Creation date
|
||||
* @apiSuccess {String} updated_at Update date
|
||||
* @apiSuccess {Integer} important Project importance (1 - important, 0 - not important)
|
||||
* @apiSuccess {String} source Project source (internal/external)
|
||||
* @apiSuccess {Integer} default_priority_id Default priority ID (null if not set)
|
||||
* @apiSuccess {Object[]} tasks_relations Task relations
|
||||
* @apiSuccess {Integer} tasks_relations.parent_id Parent task ID
|
||||
* @apiSuccess {Integer} tasks_relations.child_id Child task ID
|
||||
* @apiSuccess {Object[]} tasks List of tasks
|
||||
* @apiSuccess {Object[]} phases List of project phases
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "company_id": 1,
|
||||
* "name": "Dolores voluptates.",
|
||||
* "description": "Deleniti maxime fugit nesciunt. Ut maiores deleniti tempora vel. Nisi aut doloremque accusantium tempore aut.",
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "important": 1,
|
||||
* "source": "internal",
|
||||
* "default_priority_id": null,
|
||||
* "tasks_relations": [
|
||||
* {
|
||||
* "parent_id": 5,
|
||||
* "child_id": 1
|
||||
* }
|
||||
* ],
|
||||
* "tasks": [],
|
||||
* "phases": []
|
||||
*
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
/**
|
||||
* @param GanttDataRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function ganttData(GanttDataRequest $request): JsonResponse
|
||||
{
|
||||
|
||||
Filter::listen(Filter::getQueryFilterName(), static fn(Builder $query) => $query->with([
|
||||
'tasks' => fn(HasMany $queue) => $queue
|
||||
->orderBy('start_date')
|
||||
->select([
|
||||
'id',
|
||||
'task_name',
|
||||
'priority_id',
|
||||
'status_id',
|
||||
'estimate',
|
||||
'start_date',
|
||||
'due_date',
|
||||
'project_phase_id',
|
||||
'project_id'
|
||||
])->with(['status', 'priority'])
|
||||
->withSum(['workers as total_spent_time'], 'duration')
|
||||
->withSum(['workers as total_offset'], 'offset')
|
||||
->withCasts(['start_date' => 'string', 'due_date' => 'string'])
|
||||
->whereNotNull('start_date')->whereNotNull('due_date'),
|
||||
'phases' => fn(HasMany $queue) => $queue
|
||||
->select(['id', 'name', 'project_id'])
|
||||
->withMin([
|
||||
'tasks as start_date' => fn(AdjacencyListBuilder $q) => $q
|
||||
->whereNotNull('start_date')
|
||||
->whereNotNull('due_date')
|
||||
], 'start_date')
|
||||
->withMax([
|
||||
'tasks as due_date' => fn(AdjacencyListBuilder $q) => $q
|
||||
->whereNotNull('start_date')
|
||||
->whereNotNull('due_date')
|
||||
], 'due_date'),
|
||||
]));
|
||||
|
||||
Filter::listen(Filter::getActionFilterName(), static function (Project $item) {
|
||||
$item->append('tasks_relations');
|
||||
return $item;
|
||||
});
|
||||
|
||||
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /projects/phases Project Phases
|
||||
* @apiDescription Retrieve project phases along with the number of tasks in each phase.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetProjectPhases
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
* @apiUse ProjectIDParam
|
||||
* @apiUse ProjectObject
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param PhasesRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function phases(PhasesRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(
|
||||
Filter::getQueryFilterName(),
|
||||
static fn(Builder $query) => $query
|
||||
->with([
|
||||
'phases'=> fn(HasMany $q) => $q->withCount('tasks')
|
||||
])
|
||||
);
|
||||
|
||||
return $this->_show($request);
|
||||
}
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {get} /projects/show Project Show
|
||||
* @apiDescription Retrieve project show along with the number of tasks in each phase.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetProjectShow
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
* @apiUse ProjectIDParam
|
||||
* @apiUse ProjectObject
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function show(ShowProjectRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(
|
||||
Filter::getQueryFilterName(),
|
||||
static fn(Builder $query) => $query
|
||||
->with([
|
||||
'phases'=> fn(HasMany $q) => $q->withCount('tasks')
|
||||
])
|
||||
);
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /projects/create Create Project
|
||||
* @apiDescription Creates a new project
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName CreateProject
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Boolean} important Project importance
|
||||
* @apiParam {Integer} screenshots_state State of the screenshots
|
||||
* @apiParam {String} name Project name
|
||||
* @apiParam {String} description Project description
|
||||
* @apiParam {Integer} default_priority_id Default priority ID
|
||||
* @apiParam {Object[]} statuses Project statuses
|
||||
* @apiParam {Integer} statuses.id Status ID
|
||||
* @apiParam {String} statuses.color Status color
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "important": true,
|
||||
* "screenshots_state": 1,
|
||||
* "name": "test",
|
||||
* "description": "test",
|
||||
* "default_priority_id": 2,
|
||||
* "statuses": [
|
||||
* {
|
||||
* "id": 2,
|
||||
* "color": null
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} name Project name
|
||||
* @apiSuccess {String} description Project description
|
||||
* @apiSuccess {Boolean} important Project importance
|
||||
* @apiSuccess {Integer} default_priority_id Default priority ID
|
||||
* @apiSuccess {Integer} screenshots_state State of the screenshots
|
||||
* @apiSuccess {String} created_at Creation timestamp
|
||||
* @apiSuccess {String} updated_at Update timestamp
|
||||
* @apiSuccess {Object[]} statuses Project statuses
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "name": "test",
|
||||
* "description": "test",
|
||||
* "important": 1,
|
||||
* "default_priority_id": 2,
|
||||
* "screenshots_state": 1,
|
||||
* "updated_at": "2024-08-06T12:28:07.000000Z",
|
||||
* "created_at": "2024-08-06T12:28:07.000000Z",
|
||||
* "id": 161,
|
||||
* "statuses": []
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
public function create(CreateProjectRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(Filter::getRequestFilterName(), static function ($requestData) {
|
||||
if (isset($requestData['group']) && is_array($requestData['group'])) {
|
||||
$requestData['group'] = $requestData['group']['id'];
|
||||
}
|
||||
|
||||
return $requestData;
|
||||
});
|
||||
|
||||
CatEvent::listen(Filter::getAfterActionEventName(), static function (Project $project, $requestData) use ($request) {
|
||||
if ($request->has('statuses')) {
|
||||
$statuses = [];
|
||||
foreach ($request->get('statuses') as $status) {
|
||||
$statuses[$status['id']] = ['color' => $status['color']];
|
||||
}
|
||||
|
||||
$project->statuses()->sync($statuses);
|
||||
}
|
||||
|
||||
if (isset($requestData['phases'])) {
|
||||
$project->phases()->createMany($requestData['phases']);
|
||||
}
|
||||
});
|
||||
|
||||
Filter::listen(Filter::getActionFilterName(), static fn($data) => $data->load('statuses'));
|
||||
|
||||
return $this->_create($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /projects/edit Edit
|
||||
* @apiDescription Edit Project
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName EditProject
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission projects_edit
|
||||
* @apiPermission projects_full_access
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1,
|
||||
* "name": "test",
|
||||
* "description": "test"
|
||||
* }
|
||||
*
|
||||
* @apiParam {String} id Project id
|
||||
* @apiParam {String} name Project name
|
||||
* @apiParam {String} description Project description
|
||||
*
|
||||
* @apiSuccess {Integer} id Project ID
|
||||
* @apiSuccess {Integer} company_id Company ID
|
||||
* @apiSuccess {String} name Project name
|
||||
* @apiSuccess {String} description Project description
|
||||
* @apiSuccess {String} deleted_at Deletion timestamp
|
||||
* @apiSuccess {String} created_at Creation timestamp
|
||||
* @apiSuccess {String} updated_at Update timestamp
|
||||
* @apiSuccess {Boolean} important Project importance
|
||||
* @apiSuccess {String} source Project source
|
||||
* @apiSuccess {Integer} default_priority_id Default priority ID
|
||||
* @apiSuccess {Integer} screenshots_state State of the screenshots
|
||||
* @apiSuccess {Object[]} statuses Project statuses
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* {
|
||||
* "id": 1,
|
||||
* "company_id": 1,
|
||||
* "name": "test",
|
||||
* "description": "test",
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-08-07T16:47:01.000000Z",
|
||||
* "important": 1,
|
||||
* "source": "internal",
|
||||
* "default_priority_id": null,
|
||||
* "screenshots_state": 1,
|
||||
* "statuses": []
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ItemNotFoundError
|
||||
*/
|
||||
public function edit(EditProjectRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(Filter::getRequestFilterName(), static function ($requestData) {
|
||||
if (isset($requestData['group']) && is_array($requestData['group'])) {
|
||||
$requestData['group'] = $requestData['group']['id'];
|
||||
}
|
||||
|
||||
return $requestData;
|
||||
});
|
||||
|
||||
CatEvent::listen(Filter::getAfterActionEventName(), static function (Project $project, $requestData) use ($request) {
|
||||
if ($request->has('statuses')) {
|
||||
$statuses = [];
|
||||
foreach ($request->get('statuses') as $status) {
|
||||
$statuses[$status['id']] = ['color' => $status['color']];
|
||||
}
|
||||
|
||||
$project->statuses()->sync($statuses);
|
||||
}
|
||||
|
||||
if (isset($requestData['phases'])) {
|
||||
$phases = collect($requestData['phases']);
|
||||
$project->phases()
|
||||
->whereNotIn('id', $phases->pluck('id')->filter())
|
||||
->delete();
|
||||
$project->phases()->upsert(
|
||||
$phases->filter(fn (array $val) => isset($val['id']))->toArray(),
|
||||
['id'],
|
||||
['name']
|
||||
);
|
||||
$project->phases()->createMany($phases->filter(fn (array $val) => !isset($val['id'])));
|
||||
}
|
||||
});
|
||||
|
||||
Filter::listen(Filter::getActionFilterName(), static fn($data) => $data->load('statuses'));
|
||||
|
||||
return $this->_edit($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /projects/remove Destroy
|
||||
* @apiDescription Destroy Project
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName DestroyProject
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission projects_remove
|
||||
* @apiPermission projects_full_access
|
||||
*
|
||||
* @apiParam {String} id Project id
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} message Destroy status
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 204 No Content
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ItemNotFoundError
|
||||
*/
|
||||
public function destroy(DestroyProjectRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_destroy($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @api {get,post} /projects/count Count
|
||||
* @apiDescription Count Projects
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Count
|
||||
* @apiGroup Project
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission projects_count
|
||||
* @apiPermission projects_full_access
|
||||
*
|
||||
* @apiSuccess {Integer} total Amount of projects that we have
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "total": 159
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function count(ListProjectRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_count($request);
|
||||
}
|
||||
}
|
||||
155
app/Http/Controllers/Api/ProjectGroupController.php
Normal file
155
app/Http/Controllers/Api/ProjectGroupController.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\QueryHelper;
|
||||
use App\Http\Requests\ProjectGroup\CreateProjectGroupRequest;
|
||||
use App\Http\Requests\ProjectGroup\DestroyProjectGroupRequest;
|
||||
use App\Http\Requests\ProjectGroup\EditProjectGroupRequest;
|
||||
use App\Http\Requests\ProjectGroup\ListProjectGroupRequest;
|
||||
use App\Http\Requests\ProjectGroup\ShowProjectGroupRequest;
|
||||
use App\Models\ProjectGroup;
|
||||
use CatEvent;
|
||||
use Exception;
|
||||
use Filter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Kalnoy\Nestedset\QueryBuilder;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class ProjectGroupController extends ItemController
|
||||
{
|
||||
protected const MODEL = ProjectGroup::class;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param ListProjectGroupRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(ListProjectGroupRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
$itemsQuery = $this->getQuery($requestData);
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $requestData);
|
||||
|
||||
$itemsQuery->withDepth()->withCount('projects')->defaultOrder();
|
||||
|
||||
$items = $request->header('X-Paginate', true) !== 'false' ? $itemsQuery->paginate($request->input('limit', null)) : $itemsQuery->get();
|
||||
|
||||
Filter::process(
|
||||
Filter::getActionFilterName(),
|
||||
$items,
|
||||
);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$items, $requestData]);
|
||||
|
||||
return responder()->success($items)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getQuery(array $filter = []): QueryBuilder
|
||||
{
|
||||
$model = static::MODEL;
|
||||
$model = new $model;
|
||||
|
||||
$query = new QueryBuilder($model::getQuery());
|
||||
$query->setModel($model);
|
||||
|
||||
$modelScopes = $model->getGlobalScopes();
|
||||
|
||||
foreach ($modelScopes as $key => $value) {
|
||||
$query->withGlobalScope($key, $value);
|
||||
}
|
||||
|
||||
foreach (Filter::process(Filter::getQueryAdditionalRelationsFilterName(), []) as $with) {
|
||||
$query->with($with);
|
||||
}
|
||||
|
||||
QueryHelper::apply($query, $model, $filter);
|
||||
|
||||
return Filter::process(
|
||||
Filter::getQueryFilterName(),
|
||||
$query
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param CreateProjectGroupRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function create(CreateProjectGroupRequest $request): JsonResponse
|
||||
{
|
||||
if ($parent_id = $request->safe(['parent_id'])['parent_id'] ?? null) {
|
||||
CatEvent::listen(
|
||||
Filter::getAfterActionEventName(),
|
||||
static fn(ProjectGroup $group) => $group->parent()->associate($parent_id)->save(),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->_create($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param ShowProjectGroupRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function show(ShowProjectGroupRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param EditProjectGroupRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function edit(EditProjectGroupRequest $request): JsonResponse
|
||||
{
|
||||
CatEvent::listen(
|
||||
Filter::getAfterActionEventName(),
|
||||
static function (ProjectGroup $group) use ($request) {
|
||||
if ($parent_id = $request->input('parent_id', null)) {
|
||||
$group->parent()->associate($parent_id)->save();
|
||||
} else {
|
||||
$group->saveAsRoot();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return $this->_edit($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param DestroyProjectGroupRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function destroy(DestroyProjectGroupRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_destroy($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function count(ListProjectGroupRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_count($request);
|
||||
}
|
||||
}
|
||||
237
app/Http/Controllers/Api/ProjectMemberController.php
Normal file
237
app/Http/Controllers/Api/ProjectMemberController.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ProjectMember\BulkEditProjectMemberRequest;
|
||||
use App\Http\Requests\ProjectMember\ShowProjectMemberRequest;
|
||||
use App\Services\ProjectMemberService;
|
||||
use CatEvent;
|
||||
use Filter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Throwable;
|
||||
|
||||
class ProjectMemberController extends Controller
|
||||
{
|
||||
/**
|
||||
* @api {post} /project-members/list List Project Members
|
||||
* @apiDescription Get list of project members
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName ListProjectMembers
|
||||
* @apiGroup ProjectMember
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} project_id ID of the project
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "project_id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Integer} id Project ID
|
||||
* @apiSuccess {Object[]} users List of users
|
||||
* @apiSuccess {Integer} users.id User ID
|
||||
* @apiSuccess {String} users.full_name User full name
|
||||
* @apiSuccess {String} users.email User email
|
||||
* @apiSuccess {String} users.url User URL
|
||||
* @apiSuccess {Integer} users.company_id Company ID
|
||||
* @apiSuccess {String} users.avatar User avatar
|
||||
* @apiSuccess {Integer} users.screenshots_active Screenshots active status
|
||||
* @apiSuccess {Integer} users.manual_time Manual time status
|
||||
* @apiSuccess {Integer} users.computer_time_popup Computer time popup interval
|
||||
* @apiSuccess {Boolean} users.blur_screenshots Blur screenshots status
|
||||
* @apiSuccess {Boolean} users.web_and_app_monitoring Web and app monitoring status
|
||||
* @apiSuccess {Integer} users.screenshots_interval Screenshots interval
|
||||
* @apiSuccess {Boolean} users.active User active status
|
||||
* @apiSuccess {String} users.deleted_at Deletion timestamp
|
||||
* @apiSuccess {String} users.created_at Creation timestamp
|
||||
* @apiSuccess {String} users.updated_at Last update timestamp
|
||||
* @apiSuccess {String} users.timezone User timezone
|
||||
* @apiSuccess {Boolean} users.important User importance status
|
||||
* @apiSuccess {Boolean} users.change_password Change password status
|
||||
* @apiSuccess {Integer} users.role_id User role ID
|
||||
* @apiSuccess {String} users.user_language User language
|
||||
* @apiSuccess {String} users.type User type
|
||||
* @apiSuccess {Boolean} users.invitation_sent Invitation sent status
|
||||
* @apiSuccess {Integer} users.nonce User nonce
|
||||
* @apiSuccess {Boolean} users.client_installed Client installed status
|
||||
* @apiSuccess {Boolean} users.permanent_screenshots Permanent screenshots status
|
||||
* @apiSuccess {String} users.last_activity Last activity timestamp
|
||||
* @apiSuccess {Boolean} users.online Online status
|
||||
* @apiSuccess {Boolean} users.can_view_team_tab Can view team tab status
|
||||
* @apiSuccess {Boolean} users.can_create_task Can create task status
|
||||
* @apiSuccess {Object} users.pivot Pivot data
|
||||
* @apiSuccess {Integer} users.pivot.project_id Project ID in pivot
|
||||
* @apiSuccess {Integer} users.pivot.user_id User ID in pivot
|
||||
* @apiSuccess {Integer} users.pivot.role_id Role ID in pivot
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "users": [
|
||||
* {
|
||||
* "id": 1,
|
||||
* "full_name": "Admin",
|
||||
* "email": "admin@cattr.app",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_active": 1,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-02-15T19:06:42.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 0,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2023-10-26 10:26:17",
|
||||
* "online": false,
|
||||
* "can_view_team_tab": true,
|
||||
* "can_create_task": true,
|
||||
* "pivot": {
|
||||
* "project_id": 1,
|
||||
* "user_id": 1,
|
||||
* "role_id": 2
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* "id": 2,
|
||||
* "full_name": "Fabiola Mertz",
|
||||
* "email": "projectManager@example.com",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_active": 1,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 2,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2023-10-26 09:44:17",
|
||||
* "online": false,
|
||||
* "can_view_team_tab": false,
|
||||
* "can_create_task": false,
|
||||
* "pivot": {
|
||||
* "project_id": 1,
|
||||
* "user_id": 2,
|
||||
* "role_id": 2
|
||||
* }
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @param ShowProjectMemberRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function list(ShowProjectMemberRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
|
||||
throw_unless($data, ValidationException::withMessages([]));
|
||||
|
||||
$projectMembers = ProjectMemberService::getMembers($data['project_id']);
|
||||
|
||||
$projectMembers['users'] = $projectMembers['users'] ?? [];
|
||||
|
||||
return responder()->success($projectMembers)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /api/project-members/bulk-edit Bulk Edit Project Members
|
||||
* @apiDescription Edit roles of multiple project members
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName BulkEditProjectMembers
|
||||
* @apiGroup ProjectMember
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} project_id Project ID
|
||||
* @apiParam {Object[]} user_roles Array of user roles
|
||||
* @apiParam {Integer} user_roles.user_id User ID
|
||||
* @apiParam {Integer} user_roles.role_id Role ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "project_id": 1,
|
||||
* "user_roles": [
|
||||
* {
|
||||
* "user_id": 1,
|
||||
* "role_id": 2
|
||||
* },
|
||||
* {
|
||||
* "user_id": 2,
|
||||
* "role_id": 3
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 204 No Content
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
/**
|
||||
* @param BulkEditProjectMemberRequest $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function bulkEdit(BulkEditProjectMemberRequest $request): JsonResponse
|
||||
{
|
||||
$data = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
|
||||
$userRoles = [];
|
||||
|
||||
foreach ($data['user_roles'] as $value) {
|
||||
$userRoles[$value['user_id']] = ['role_id' => $value['role_id']];
|
||||
}
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), [$data['project_id'], $userRoles]);
|
||||
|
||||
ProjectMemberService::syncMembers($data['project_id'], $userRoles);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$data['project_id'], $userRoles]);
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
}
|
||||
193
app/Http/Controllers/Api/Reports/DashboardController.php
Normal file
193
app/Http/Controllers/Api/Reports/DashboardController.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Reports;
|
||||
|
||||
use App\Enums\DashboardSortBy;
|
||||
use App\Enums\SortDirection;
|
||||
use App\Helpers\ReportHelper;
|
||||
use App\Http\Requests\Reports\DashboardRequest;
|
||||
use App\Jobs\GenerateAndSendReport;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Reports\DashboardExport;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Settings;
|
||||
use Throwable;
|
||||
|
||||
class DashboardController
|
||||
{
|
||||
/**
|
||||
* @api {post} /report/dashboard Dashboard Data
|
||||
* @apiDescription Retrieve dashboard data based on provided parameters.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName DashboardData
|
||||
* @apiGroup Dashboard
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {String} start_at Start date-time in "Y-m-d H:i:s" format.
|
||||
* @apiParam {String} end_at End date-time in "Y-m-d H:i:s" format.
|
||||
* @apiParam {String} user_timezone User's timezone.
|
||||
* @apiParam {Array} [users] Array of user IDs. If not provided, all users are considered.
|
||||
* @apiParam {Array} [projects] Array of project IDs. If not provided, all projects are considered.
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "start_at": "2006-05-31 16:15:09",
|
||||
* "end_at": "2006-05-31 16:20:07",
|
||||
* "user_timezone": "Asia/Omsk"
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object} data Dashboard data keyed by user ID.
|
||||
* @apiSuccess {Array} data.7 Array of records for user with ID 7.
|
||||
* @apiSuccess {String} data.7.start_at Start date-time of the record.
|
||||
* @apiSuccess {Integer} data.7.activity_fill Activity fill percentage.
|
||||
* @apiSuccess {Integer} data.7.mouse_fill Mouse activity fill percentage.
|
||||
* @apiSuccess {Integer} data.7.keyboard_fill Keyboard activity fill percentage.
|
||||
* @apiSuccess {String} data.7.end_at End date-time of the record.
|
||||
* @apiSuccess {Boolean} data.7.is_manual Indicates if the record is manual.
|
||||
* @apiSuccess {String} data.7.user_email User's email address.
|
||||
* @apiSuccess {Integer} data.7.id Record ID.
|
||||
* @apiSuccess {Integer} data.7.project_id Project ID.
|
||||
* @apiSuccess {String} data.7.project_name Project name.
|
||||
* @apiSuccess {Integer} data.7.task_id Task ID.
|
||||
* @apiSuccess {String} data.7.task_name Task name.
|
||||
* @apiSuccess {Integer} data.7.user_id User ID.
|
||||
* @apiSuccess {String} data.7.full_name User's full name.
|
||||
* @apiSuccess {Integer} data.7.duration Duration in seconds.
|
||||
* @apiSuccess {Integer} data.7.from_midnight Time from midnight in seconds.
|
||||
* @apiSuccess {Object} data.7.durationByDay Duration grouped by day.
|
||||
* @apiSuccess {Integer} data.7.durationByDay.2018-05-31 Duration for the day 2018-05-31.
|
||||
* @apiSuccess {Integer} data.7.durationByDay.2018-06-04 Duration for the day 2018-06-04.
|
||||
* @apiSuccess {Integer} data.7.durationAtSelectedPeriod Duration for the selected period.
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": {
|
||||
* "7": [
|
||||
* {
|
||||
* "start_at": "2018-05-31 10:43:45",
|
||||
* "activity_fill": 111,
|
||||
* "mouse_fill": 64,
|
||||
* "keyboard_fill": 47,
|
||||
* "end_at": "2018-06-03 22:03:45",
|
||||
* "is_manual": 0,
|
||||
* "user_email": "projectManager1231@example.com",
|
||||
* "id": 2109,
|
||||
* "project_id": 159,
|
||||
* "project_name": "Voluptas ab et ea.",
|
||||
* "task_id": 54,
|
||||
* "task_name": "Quo consequatur mollitia nam.",
|
||||
* "user_id": 7,
|
||||
* "full_name": "Dr. Adaline Toy",
|
||||
* "duration": 300000,
|
||||
* "from_midnight": 38625,
|
||||
* "durationByDay": {
|
||||
* "2018-05-31": 285375,
|
||||
* "2018-06-04": 14625
|
||||
* },
|
||||
* "durationAtSelectedPeriod": 14625
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function __invoke(DashboardRequest $request): JsonResponse
|
||||
{
|
||||
$companyTimezone = Settings::scope('core')->get('timezone', 'UTC');
|
||||
|
||||
return responder()->success(
|
||||
DashboardExport::init(
|
||||
$request->input('users') ?? User::all()->pluck('id')->toArray(),
|
||||
$request->input('projects') ?? Project::all()->pluck('id')->toArray(),
|
||||
Carbon::parse($request->input('start_at'))->setTimezone($companyTimezone),
|
||||
Carbon::parse($request->input('end_at'))->setTimezone($companyTimezone),
|
||||
$companyTimezone,
|
||||
$request->input('user_timezone'),
|
||||
)->collection()->all(),
|
||||
)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /report/dashboard/download Download Dashboard Report
|
||||
* @apiDescription Generate and download a dashboard report
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName DownloadDashboardReport
|
||||
* @apiGroup Report
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiHeader {String} Accept Specifies the content type of the response. (Example: `text/csv`)
|
||||
* @apiHeader {String} Authorization Bearer token for API access. (Example: `82|LosbyrFljFDJqUcqMNG6UveCgrclt6OzTrCWdnJBEZ1fee08e6`)
|
||||
* @apiPermission report_generate
|
||||
* @apiPermission report_full_access
|
||||
*
|
||||
* @apiParam {String} start_at Start date and time (ISO 8601 format)
|
||||
* @apiParam {String} end_at End date and time (ISO 8601 format)
|
||||
* @apiParam {String} user_timezone User's timezone
|
||||
* @apiParam {String} sort_column Column to sort by
|
||||
* @apiParam {String} sort_direction Direction to sort (asc/desc)
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "start_at": "2024-08-06T18:00:00.000Z",
|
||||
* "end_at": "2024-08-07T17:59:59.999Z",
|
||||
* "user_timezone": "Asia/Omsk",
|
||||
* "sort_column": "user",
|
||||
* "sort_direction": "asc",
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} url URL to download the generated report
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": {
|
||||
* "url": "/storage/reports/f7ac500e-a741-47ee-9e61-1b62a341fb8d/Dashboard_Report.csv"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse ItemNotFoundError
|
||||
*/
|
||||
public function download(DashboardRequest $request): JsonResponse
|
||||
{
|
||||
$companyTimezone = Settings::scope('core')->get('timezone', 'UTC');
|
||||
|
||||
$job = new GenerateAndSendReport(
|
||||
DashboardExport::init(
|
||||
$request->input('users') ?? User::all()->pluck('id')->toArray(),
|
||||
$request->input('projects') ?? Project::all()->pluck('id')->toArray(),
|
||||
Carbon::parse($request->input('start_at'))->setTimezone($companyTimezone),
|
||||
Carbon::parse($request->input('end_at'))->setTimezone($companyTimezone),
|
||||
$companyTimezone,
|
||||
$request->input('user_timezone'),
|
||||
DashboardSortBy::tryFrom($request->input('sort_column')),
|
||||
SortDirection::tryFrom($request->input('sort_direction')),
|
||||
),
|
||||
$request->user(),
|
||||
ReportHelper::getReportFormat($request),
|
||||
);
|
||||
|
||||
app(Dispatcher::class)->dispatchSync($job);
|
||||
|
||||
return responder()->success(['url' => $job->getPublicPath()])->respond();
|
||||
}
|
||||
}
|
||||
159
app/Http/Controllers/Api/Reports/PlannedTimeReportController.php
Normal file
159
app/Http/Controllers/Api/Reports/PlannedTimeReportController.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Reports;
|
||||
|
||||
use App\Helpers\ReportHelper;
|
||||
use App\Http\Requests\Reports\PlannedTimeReportRequest;
|
||||
use App\Jobs\GenerateAndSendReport;
|
||||
use App\Models\Project;
|
||||
use App\Reports\PlannedTimeReportExport;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Throwable;
|
||||
|
||||
class PlannedTimeReportController
|
||||
{
|
||||
/**
|
||||
* @api {post} /report/planned-time Planned Time Report
|
||||
* @apiDescription Generate a report on planned tasks and associated time for a given project
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName PlannedTimeReport
|
||||
* @apiGroup Report
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission report_generate
|
||||
* @apiPermission report_full_access
|
||||
*
|
||||
* @apiParam {Integer} id Project ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object} reportData Report data object
|
||||
* @apiSuccess {Integer} reportData.id Project ID
|
||||
* @apiSuccess {Integer} reportData.company_id Company ID
|
||||
* @apiSuccess {String} reportData.name Project name
|
||||
* @apiSuccess {String} reportData.description Project description
|
||||
* @apiSuccess {String} reportData.deleted_at Deleted timestamp (null if not deleted)
|
||||
* @apiSuccess {String} reportData.created_at Creation timestamp
|
||||
* @apiSuccess {String} reportData.updated_at Update timestamp
|
||||
* @apiSuccess {Boolean} reportData.important Whether the project is marked as important
|
||||
* @apiSuccess {String} reportData.source Source of the project (e.g., "internal")
|
||||
* @apiSuccess {Integer} reportData.screenshots_state Screenshots state (1 if active)
|
||||
* @apiSuccess {Integer} reportData.default_priority_id Default priority ID (null if not set)
|
||||
* @apiSuccess {Integer} reportData.total_spent_time Total time spent on the project
|
||||
* @apiSuccess {Object[]} reportData.tasks List of tasks under the project
|
||||
* @apiSuccess {Integer} reportData.tasks.id Task ID
|
||||
* @apiSuccess {String} reportData.tasks.task_name Task name
|
||||
* @apiSuccess {String} reportData.tasks.due_date Task due date (null if not set)
|
||||
* @apiSuccess {String} reportData.tasks.estimate Estimated time for the task (null if not set)
|
||||
* @apiSuccess {Integer} reportData.tasks.project_id Project ID to which the task belongs
|
||||
* @apiSuccess {Integer} reportData.tasks.total_spent_time Total time spent on the task
|
||||
* @apiSuccess {Object[]} reportData.tasks.workers List of workers assigned to the task
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "reportData": [
|
||||
* {
|
||||
* "id": 2,
|
||||
* "company_id": 5,
|
||||
* "name": "Et veniam velit tempore.",
|
||||
* "description": "Consequatur nulla distinctio reprehenderit rerum omnis debitis. Fugit illum ratione quia harum. Optio porro consequatur enim esse.",
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:42.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:42.000000Z",
|
||||
* "important": 1,
|
||||
* "source": "internal",
|
||||
* "default_priority_id": null,
|
||||
* "screenshots_state": 1,
|
||||
* "total_spent_time": null,
|
||||
* "tasks": [
|
||||
* {
|
||||
* "id": 11,
|
||||
* "task_name": "Qui velit fugiat magni accusantium.",
|
||||
* "due_date": null,
|
||||
* "estimate": null,
|
||||
* "project_id": 2,
|
||||
* "total_spent_time": null,
|
||||
* "workers": []
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse ItemNotFoundError
|
||||
*/
|
||||
public function __invoke(PlannedTimeReportRequest $request): JsonResponse
|
||||
{
|
||||
return responder()->success(
|
||||
PlannedTimeReportExport::init(
|
||||
$request->input('projects', Project::all()->pluck('id')->toArray()),
|
||||
)->collection()->all(),
|
||||
)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /report/planned-time/download Download Planned Time Report
|
||||
* @apiDescription Generate and download a report on planned time for specific projects.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName DownloadPlannedTimeReport
|
||||
* @apiGroup Report
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiHeader {String} Accept Specifies the content type of the response. (Example: `text/csv`)
|
||||
* @apiHeader {String} Authorization Bearer token for API access. (Example: `82|LosbyrFljFDJqUcqMNG6UveCgrclt6OzTrCWdnJBEZ1fee08e6`)
|
||||
* @apiPermission report_generate
|
||||
* @apiPermission report_full_access
|
||||
*
|
||||
* @apiParam {Array} projects Array of project IDs to include in the report. If not provided, all projects will be included.
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "projects": [2]
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} url URL to the generated report file.
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "url": "/storage/reports/0611766a-2807-4524-9add-2e8be33c3e58/PlannedTime_Report.csv"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function download(PlannedTimeReportRequest $request): JsonResponse
|
||||
{
|
||||
$job = new GenerateAndSendReport(
|
||||
PlannedTimeReportExport::init(
|
||||
$request->input('projects', Project::all()->pluck('id')->toArray()),
|
||||
),
|
||||
$request->user(),
|
||||
ReportHelper::getReportFormat($request),
|
||||
);
|
||||
|
||||
app(Dispatcher::class)->dispatchSync($job);
|
||||
|
||||
return responder()->success(['url' => $job->getPublicPath()])->respond();
|
||||
}
|
||||
}
|
||||
228
app/Http/Controllers/Api/Reports/ProjectReportController.php
Normal file
228
app/Http/Controllers/Api/Reports/ProjectReportController.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Reports;
|
||||
|
||||
use App\Helpers\ReportHelper;
|
||||
use App\Http\Requests\Reports\ProjectReportRequest;
|
||||
use App\Jobs\GenerateAndSendReport;
|
||||
use App\Models\User;
|
||||
use App\Models\Project;
|
||||
use App\Reports\ProjectReportExport;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Settings;
|
||||
use Throwable;
|
||||
|
||||
class ProjectReportController
|
||||
{
|
||||
/**
|
||||
* @api {post} /report/project Project Report
|
||||
* @apiDescription Retrieve detailed project report data including user tasks and time intervals.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName ProjectReport
|
||||
* @apiGroup Report
|
||||
* @apiUse AuthHeader
|
||||
* @apiPermission report_view
|
||||
* @apiPermission report_full_access
|
||||
*
|
||||
* @apiParam {String} start_at The start date and time for the report period (ISO 8601 format).
|
||||
* @apiParam {String} end_at The end date and time for the report period (ISO 8601 format).
|
||||
* @apiParam {String} user_timezone The timezone of the user making the request.
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "start_at": "2023-05-31 16:15:09",
|
||||
* "end_at": "2023-11-30 16:20:07",
|
||||
* "user_timezone": "Asia/Omsk"
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object[]} data List of projects.
|
||||
* @apiSuccess {Integer} data.id Project ID.
|
||||
* @apiSuccess {String} data.name Project name.
|
||||
* @apiSuccess {Integer} data.time Total time spent on the project.
|
||||
* @apiSuccess {Object[]} data.users List of users associated with the project.
|
||||
* @apiSuccess {Integer} data.users.id User ID.
|
||||
* @apiSuccess {String} data.users.full_name User's full name.
|
||||
* @apiSuccess {String} data.users.email User's email address.
|
||||
* @apiSuccess {Integer} data.users.time Total time spent by the user on the project.
|
||||
* @apiSuccess {Object[]} data.users.tasks List of tasks associated with the user.
|
||||
* @apiSuccess {Integer} data.users.tasks.id Task ID.
|
||||
* @apiSuccess {String} data.users.tasks.task_name Task name.
|
||||
* @apiSuccess {Integer} data.users.tasks.time Total time spent on the task.
|
||||
* @apiSuccess {Object[]} data.users.tasks.intervals List of time intervals for the task.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.date Date of the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.time Time spent in the interval.
|
||||
* @apiSuccess {Object[]} data.users.tasks.intervals.items Detailed breakdown of intervals.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.items.start_at Start time of the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.activity_fill Activity fill percentage during the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.mouse_fill Mouse activity fill percentage.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.keyboard_fill Keyboard activity fill percentage.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.items.end_at End time of the interval.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.items.user_email User's email associated with the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.id Interval ID.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.project_id Project ID associated with the interval.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.items.project_name Project name associated with the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.task_id Task ID associated with the interval.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.items.task_name Task name associated with the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.user_id User ID associated with the interval.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.items.full_name User's full name associated with the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.hour Hour of the day for the interval.
|
||||
* @apiSuccess {String} data.users.tasks.intervals.items.day Day of the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.minute Minute of the hour for the interval.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.duration Duration of the interval.
|
||||
* @apiSuccess {Object} data.users.tasks.intervals.items.durationByDay Duration of the interval by day.
|
||||
* @apiSuccess {Integer} data.users.tasks.intervals.items.durationAtSelectedPeriod Duration at the selected period.
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": [
|
||||
* {
|
||||
* "id": 159,
|
||||
* "name": "Voluptas ab et ea.",
|
||||
* "time": 3851703975,
|
||||
* "users": [
|
||||
* {
|
||||
* "id": 7,
|
||||
* "full_name": "Dr. Adaline Toy",
|
||||
* "email": "projectManager1231@example.com",
|
||||
* "time": 3851703975,
|
||||
* "tasks": [
|
||||
* {
|
||||
* "id": 54,
|
||||
* "task_name": "Quo consequatur mollitia nam.",
|
||||
* "time": 550243425,
|
||||
* "intervals": [
|
||||
* {
|
||||
* "date": "2006-05-29",
|
||||
* "time": 43425,
|
||||
* "items": [
|
||||
* {
|
||||
* "start_at": "2006-05-29 00:43:45",
|
||||
* "activity_fill": 87,
|
||||
* "mouse_fill": 81,
|
||||
* "keyboard_fill": 6,
|
||||
* "end_at": "2006-06-01 12:03:45",
|
||||
* "user_email": "projectManager1231@example.com",
|
||||
* "id": 3372,
|
||||
* "project_id": 159,
|
||||
* "project_name": "Voluptas ab et ea.",
|
||||
* "task_id": 54,
|
||||
* "task_name": "Quo consequatur mollitia nam.",
|
||||
* "user_id": 7,
|
||||
* "full_name": "Dr. Adaline Toy",
|
||||
* "hour": 0,
|
||||
* "day": "2006-05-29",
|
||||
* "minute": 40,
|
||||
* "duration": 300000,
|
||||
* "durationByDay": {
|
||||
* "2006-05-29": 256575,
|
||||
* "2006-06-01": 43425
|
||||
* },
|
||||
* "durationAtSelectedPeriod": 43425
|
||||
* }
|
||||
* ]
|
||||
* },
|
||||
* // More intervals...
|
||||
* ]
|
||||
* }
|
||||
* // More tasks...
|
||||
* ]
|
||||
* }
|
||||
* // More users...
|
||||
* ]
|
||||
* }
|
||||
* // More projects...
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
public function __invoke(ProjectReportRequest $request): JsonResponse
|
||||
{
|
||||
$companyTimezone = Settings::scope('core')->get('timezone', 'UTC');
|
||||
|
||||
return responder()->success(
|
||||
ProjectReportExport::init(
|
||||
$request->input('users', User::all()->pluck('id')->toArray()),
|
||||
$request->input('projects', Project::all()->pluck('id')->toArray()),
|
||||
Carbon::parse($request->input('start_at'))->setTimezone($companyTimezone),
|
||||
Carbon::parse($request->input('end_at'))->setTimezone($companyTimezone),
|
||||
$companyTimezone
|
||||
)->collection()->all(),
|
||||
)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /api/report/dashboard/download Download Dashboard Report
|
||||
* @apiDescription Downloads a dashboard report in the specified file format.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName DownloadDashboardReport
|
||||
* @apiGroup Reports
|
||||
* @apiUse AuthHeader
|
||||
* @apiHeader {String} Accept Accept mime type. Example: `text/csv`.
|
||||
*
|
||||
* @apiParam {String} start_at The start date and time for the report in ISO 8601 format.
|
||||
* @apiParam {String} end_at The end date and time for the report in ISO 8601 format.
|
||||
* @apiParam {String} user_timezone The timezone of the user. Example: `Asia/Omsk`.
|
||||
* @apiParam {Array} users List of user IDs to include in the report.
|
||||
* @apiParam {Array} projects List of project IDs to include in the report.
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "start_at": "2023-11-01T16:15:09Z",
|
||||
* "end_at": "2023-11-30T23:59:07Z",
|
||||
* "user_timezone": "Asia/Omsk",
|
||||
* "users": [7],
|
||||
* "projects": [159]
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} url The URL where the generated report can be downloaded.
|
||||
*
|
||||
* @apiSuccessExample {json} Success Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": {
|
||||
* "url": "/storage/reports/1b11d8f9-c5a3-4fe5-86bd-ae6a3031352c/Dashboard_Report.csv"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function download(ProjectReportRequest $request): JsonResponse
|
||||
{
|
||||
$companyTimezone = Settings::scope('core')->get('timezone', 'UTC');
|
||||
|
||||
$job = new GenerateAndSendReport(
|
||||
ProjectReportExport::init(
|
||||
$request->input('users', User::all()->pluck('id')->toArray()),
|
||||
$request->input('projects', Project::all()->pluck('id')->toArray()),
|
||||
Carbon::parse($request->input('start_at'))->setTimezone($companyTimezone),
|
||||
Carbon::parse($request->input('end_at'))->setTimezone($companyTimezone),
|
||||
$companyTimezone
|
||||
),
|
||||
$request->user(),
|
||||
ReportHelper::getReportFormat($request),
|
||||
);
|
||||
|
||||
app(Dispatcher::class)->dispatchSync($job);
|
||||
|
||||
return responder()->success(['url' => $job->getPublicPath()])->respond();
|
||||
}
|
||||
|
||||
}
|
||||
98
app/Http/Controllers/Api/Reports/TimeUseReportController.php
Normal file
98
app/Http/Controllers/Api/Reports/TimeUseReportController.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Reports;
|
||||
|
||||
use App\Http\Requests\Reports\TimeUseReportRequest;
|
||||
use App\Models\User;
|
||||
use App\Reports\TimeUseReportExport;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Settings;
|
||||
|
||||
/**
|
||||
* Class TimeUseReportController
|
||||
*
|
||||
*/
|
||||
class TimeUseReportController
|
||||
{
|
||||
/**
|
||||
* @api {post} /api/report/time Get User Time Report
|
||||
* @apiDescription Retrieves the time report for specified users within a given time range.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetUserTimeReport
|
||||
* @apiGroup Reports
|
||||
*
|
||||
* @apiParam {String} start_at The start date and time for the report in ISO 8601 format.
|
||||
* @apiParam {String} end_at The end date and time for the report in ISO 8601 format.
|
||||
* @apiParam {String} user_timezone The timezone of the user. Example: `Asia/Omsk`.
|
||||
* @apiParam {Array} users List of user IDs to include in the report.
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "start_at": "2023-11-01T16:15:09Z",
|
||||
* "end_at": "2023-11-30T23:59:07Z",
|
||||
* "user_timezone": "Asia/Omsk",
|
||||
* "users": [7]
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object[]} data List of users and their respective time logs.
|
||||
* @apiSuccess {Number} data.time Total time logged by the user within the specified period (in seconds).
|
||||
* @apiSuccess {Object} data.user User information.
|
||||
* @apiSuccess {Number} data.user.id User ID.
|
||||
* @apiSuccess {String} data.user.email User's email address.
|
||||
* @apiSuccess {String} data.user.full_name User's full name.
|
||||
* @apiSuccess {Object[]} data.tasks List of tasks the user has logged time for.
|
||||
* @apiSuccess {Number} data.tasks.time Time logged for the task (in seconds).
|
||||
* @apiSuccess {Number} data.tasks.task_id Task ID.
|
||||
* @apiSuccess {String} data.tasks.task_name Task name.
|
||||
* @apiSuccess {Number} data.tasks.project_id Project ID associated with the task.
|
||||
* @apiSuccess {String} data.tasks.project_name Project name associated with the task.
|
||||
*
|
||||
* @apiSuccessExample {json} Success Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": [
|
||||
* {
|
||||
* "time": 2151975,
|
||||
* "user": {
|
||||
* "id": 7,
|
||||
* "email": "projectManager1231@example.com",
|
||||
* "full_name": "Dr. Adaline Toy"
|
||||
* },
|
||||
* "tasks": [
|
||||
* {
|
||||
* "time": 307425,
|
||||
* "task_id": 56,
|
||||
* "task_name": "Similique enim aspernatur.",
|
||||
* "project_id": 159,
|
||||
* "project_name": "Voluptas ab et ea."
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
|
||||
public function __invoke(TimeUseReportRequest $request): JsonResponse
|
||||
{
|
||||
$companyTimezone = Settings::scope('core')->get('timezone', 'UTC');
|
||||
|
||||
return responder()->success(
|
||||
TimeUseReportExport::init(
|
||||
$request->input('users') ?? User::all()->pluck('id')->toArray(),
|
||||
Carbon::parse($request->input('start_at'))->setTimezone($companyTimezone),
|
||||
Carbon::parse($request->input('end_at'))->setTimezone($companyTimezone),
|
||||
$companyTimezone
|
||||
)->collection()->all(),
|
||||
)->respond();
|
||||
}
|
||||
}
|
||||
201
app/Http/Controllers/Api/Reports/UniversalReportController.php
Normal file
201
app/Http/Controllers/Api/Reports/UniversalReportController.php
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Reports;
|
||||
|
||||
use App\Enums\UniversalReportType;
|
||||
use App\Enums\UniversalReportBase;
|
||||
use App\Exceptions\Entities\NotEnoughRightsException;
|
||||
use App\Helpers\ReportHelper;
|
||||
use App\Http\Requests\Reports\UniversalReport\UniversalReportEditRequest;
|
||||
use App\Http\Requests\Reports\UniversalReport\UniversalReportRequest;
|
||||
use App\Http\Requests\Reports\UniversalReport\UniversalReportShowRequest;
|
||||
use App\Http\Requests\Reports\UniversalReport\UniversalReportStoreRequest;
|
||||
use App\Http\Requests\Reports\UniversalReport\UniversalReportDestroyRequest;
|
||||
|
||||
use App\Jobs\GenerateAndSendReport;
|
||||
use App\Models\Project;
|
||||
use App\Models\UniversalReport;
|
||||
use App\Reports\PlannedTimeReportExport;
|
||||
use App\Reports\UniversalReportExport;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Settings;
|
||||
use Throwable;
|
||||
|
||||
class UniversalReportController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$items = [
|
||||
UniversalReportType::COMPANY->value => [],
|
||||
UniversalReportType::PERSONAL->value => [],
|
||||
];
|
||||
$user = request()->user();
|
||||
|
||||
if (request()->user()->isAdmin()) {
|
||||
UniversalReport::select('id', 'name', 'type')
|
||||
->where([
|
||||
['type', '=', UniversalReportType::COMPANY->value, 'or'],
|
||||
['user_id', '=', request()->user()->id, 'or']
|
||||
])
|
||||
->get()
|
||||
->each(function ($item) use (&$items) {
|
||||
$items[$item->type->value][] = $item->toArray();
|
||||
});
|
||||
|
||||
return responder()->success($items)->respond();
|
||||
}
|
||||
|
||||
UniversalReport::select('id', 'name', 'data_objects', 'base', 'type')->get()->each(function ($item) use (&$items) {
|
||||
if ($item->base->checkAccess($item->data_objects)) {
|
||||
unset($item->data_objects, $item->base);
|
||||
$items[$item->type->value][] = $item->toArray();
|
||||
}
|
||||
});
|
||||
|
||||
return responder()->success($items)->respond();
|
||||
}
|
||||
|
||||
public function getBases()
|
||||
{
|
||||
return responder()->success(UniversalReportBase::bases())->respond();
|
||||
}
|
||||
|
||||
public function getDataObjectsAndFields(Request $request)
|
||||
{
|
||||
$base = UniversalReportBase::tryFrom($request->input('base', null));
|
||||
// dd($base->dataObjects());
|
||||
return responder()->success([
|
||||
'fields' => $base->fields(),
|
||||
'dataObjects' => $base->dataObjects(),
|
||||
'charts' => $base->charts(),
|
||||
])->respond();
|
||||
}
|
||||
|
||||
public function store(UniversalReportStoreRequest $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if ($request->input('type') === UniversalReportType::COMPANY->value) {
|
||||
if ($request->user()->isAdmin()) {
|
||||
$report = $user->universalReports()->create([
|
||||
'name' => $request->name,
|
||||
'type' => $request->type,
|
||||
'base' => $request->base,
|
||||
'data_objects' => $request->dataObjects,
|
||||
'fields' => $request->fields,
|
||||
'charts' => $request->charts,
|
||||
]);
|
||||
return responder()->success(['message' => "The report was saved successfully", 'id' => $report->id])->respond(200);
|
||||
} else {
|
||||
return throw new NotEnoughRightsException('User rights do not allow saving the report for the company');
|
||||
}
|
||||
}
|
||||
|
||||
$report = $user->universalReports()->create([
|
||||
'name' => $request->name,
|
||||
'type' => $request->type,
|
||||
'base' => $request->base,
|
||||
'data_objects' => $request->dataObjects,
|
||||
'fields' => $request->fields,
|
||||
'charts' => $request->charts,
|
||||
]);
|
||||
|
||||
return responder()->success(['message' => "The report was saved successfully", 'id' => $report->id])->respond(200);
|
||||
}
|
||||
|
||||
public function show(UniversalReportShowRequest $request)
|
||||
{
|
||||
return responder()->success(UniversalReport::find($request->id))->respond();
|
||||
}
|
||||
|
||||
public function edit(UniversalReportEditRequest $request)
|
||||
{
|
||||
if ($request->input('type') === UniversalReportType::COMPANY->value) {
|
||||
if ($request->user()->isAdmin()) {
|
||||
UniversalReport::where('id', $request->id)->update([
|
||||
'name' => $request->name,
|
||||
'type' => $request->type,
|
||||
'base' => $request->base,
|
||||
'data_objects' => $request->dataObjects,
|
||||
'fields' => $request->fields,
|
||||
'charts' => $request->charts,
|
||||
]);
|
||||
} else {
|
||||
return throw new NotEnoughRightsException('User rights do not allow saving the report for the company');
|
||||
}
|
||||
}
|
||||
|
||||
UniversalReport::where('id', $request->id)->update([
|
||||
'name' => $request->name,
|
||||
'type' => $request->type,
|
||||
'base' => $request->base,
|
||||
'data_objects' => $request->dataObjects,
|
||||
'fields' => $request->fields,
|
||||
'charts' => $request->charts,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function __invoke(UniversalReportRequest $request): JsonResponse
|
||||
{
|
||||
$companyTimezone = Settings::scope('core')->get('timezone', 'UTC');
|
||||
|
||||
return responder()->success(
|
||||
UniversalReportExport::init(
|
||||
$request->input('id'),
|
||||
Carbon::parse($request->input('start_at'))->setTimezone($companyTimezone),
|
||||
Carbon::parse($request->input('end_at'))->setTimezone($companyTimezone),
|
||||
Settings::scope('core')->get('timezone', 'UTC'),
|
||||
)->collection()->all(),
|
||||
)->respond();
|
||||
}
|
||||
|
||||
public function destroy(UniversalReportDestroyRequest $request)
|
||||
{
|
||||
UniversalReport::find($request->input('id', null))->delete();
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
|
||||
public function download(UniversalReportRequest $request): JsonResponse
|
||||
{
|
||||
$companyTimezone = Settings::scope('core')->get('timezone', 'UTC');
|
||||
$job = new GenerateAndSendReport(
|
||||
UniversalReportExport::init(
|
||||
$request->id,
|
||||
Carbon::parse($request->start_at) ?? Carbon::parse(),
|
||||
Carbon::parse($request->end_at) ?? Carbon::parse(),
|
||||
Settings::scope('core')->get('timezone', 'UTC'),
|
||||
$request->user()?->timezone ?? 'UTC',
|
||||
),
|
||||
|
||||
$request->user(),
|
||||
ReportHelper::getReportFormat($request),
|
||||
);
|
||||
|
||||
$job->handle();
|
||||
// app(Dispatcher::class)->dispatchSync($job);
|
||||
|
||||
return responder()->success(['url' => $job->getPublicPath()])->respond();
|
||||
}
|
||||
// /**
|
||||
// * @throws Throwable
|
||||
// */
|
||||
// public function download(UniversalReportRequest $request): JsonResponse
|
||||
// {
|
||||
// $job = new GenerateAndSendReport(
|
||||
// PlannedTimeReportExport::init(
|
||||
// $request->input('projects', Project::all()->pluck('id')->toArray()),
|
||||
// ),
|
||||
// $request->user(),
|
||||
// ReportHelper::getReportFormat($request),
|
||||
// );
|
||||
|
||||
// app(Dispatcher::class)->dispatchSync($job);
|
||||
|
||||
// return responder()->success(['url' => $job->getPublicPath()])->respond();
|
||||
// }
|
||||
}
|
||||
73
app/Http/Controllers/Api/RoleController.php
Normal file
73
app/Http/Controllers/Api/RoleController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\Role;
|
||||
use CatEvent;
|
||||
use Filter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class RoleController extends ItemController
|
||||
{
|
||||
/**
|
||||
* @api {post} /api/roles/list Get Roles List
|
||||
* @apiDescription Retrieves the list of roles available in the system.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetRolesList
|
||||
* @apiGroup Roles
|
||||
*
|
||||
* @apiSuccess {Number} status HTTP status code.
|
||||
* @apiSuccess {Boolean} success Request success status.
|
||||
* @apiSuccess {Object[]} data List of roles.
|
||||
* @apiSuccess {String} data.name Role name.
|
||||
* @apiSuccess {Number} data.id Role ID.
|
||||
*
|
||||
* @apiSuccessExample {json} Success Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "status": 200,
|
||||
* "success": true,
|
||||
* "data": [
|
||||
* {
|
||||
* "name": "ANY",
|
||||
* "id": -1
|
||||
* },
|
||||
* {
|
||||
* "name": "ADMIN",
|
||||
* "id": 0
|
||||
* },
|
||||
* {
|
||||
* "name": "MANAGER",
|
||||
* "id": 1
|
||||
* },
|
||||
* {
|
||||
* "name": "USER",
|
||||
* "id": 2
|
||||
* },
|
||||
* {
|
||||
* "name": "AUDITOR",
|
||||
* "id": 3
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName());
|
||||
|
||||
$items = Filter::process(
|
||||
Filter::getActionFilterName(),
|
||||
//For compatibility reasons generate serialized model-like array
|
||||
array_map(fn ($role) => ['name' => $role->name, 'id' => $role->value], Role::cases()),
|
||||
);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$items]);
|
||||
|
||||
return responder()->success($items)->respond();
|
||||
}
|
||||
}
|
||||
253
app/Http/Controllers/Api/StatusController.php
Normal file
253
app/Http/Controllers/Api/StatusController.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
|
||||
|
||||
use App\Models\Status;
|
||||
use App\Http\Requests\Status\CreateStatusRequest;
|
||||
use App\Http\Requests\Status\DestroyStatusRequest;
|
||||
use App\Http\Requests\Status\ListStatusRequest;
|
||||
use App\Http\Requests\Status\ShowStatusRequestStatus;
|
||||
use App\Http\Requests\Status\UpdateStatusRequest;
|
||||
use CatEvent;
|
||||
use Exception;
|
||||
use Filter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Throwable;
|
||||
|
||||
class StatusController extends ItemController
|
||||
{
|
||||
protected const MODEL = Status::class;
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /statuses/show Show
|
||||
* @apiDescription Show status.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Show Status
|
||||
* @apiGroup Status
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id Status ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiUse StatusObject
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function show(ShowStatusRequestStatus $request): JsonResponse
|
||||
{
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {get} /statuses/list List
|
||||
* @apiDescription Get list of statuses.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Status List
|
||||
* @apiGroup Status
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccess {Object} res Status
|
||||
*
|
||||
* @apiUse StatusObject
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*
|
||||
*/
|
||||
public function index(ListStatusRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_index($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CreateStatusRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
* @api {post} /statuses/create Create
|
||||
* @apiDescription Creates status
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Create Status
|
||||
* @apiGroup Status
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {String} name Status name
|
||||
* @apiParam {String} active Status active
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "name": "Normal",
|
||||
* "active": false
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object} res Status
|
||||
*
|
||||
* @apiSuccess {Number} id The ID of the status.
|
||||
* @apiSuccess {String} name The name of the status.
|
||||
* @apiSuccess {Boolean} active Indicates if the status is active.\
|
||||
* @apiSuccess {String} created_at The creation timestamp.
|
||||
* @apiSuccess {String} updated_at The last update timestamp.
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 10,
|
||||
* "name": "Normal",
|
||||
* "active": false,
|
||||
* "created_at": "2024-08-15T14:04:03.000000Z",
|
||||
* "updated_at": "2024-08-15T14:04:03.000000Z"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function create(CreateStatusRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(Filter::getRequestFilterName(), static function ($item) {
|
||||
$maxOrder = Status::max('order');
|
||||
$item['order'] = $maxOrder + 1;
|
||||
return $item;
|
||||
});
|
||||
|
||||
return $this->_create($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /statuses/edit Edit
|
||||
* @apiDescription Edit Status
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Edit
|
||||
* @apiGroup Status
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id ID
|
||||
* @apiParam {String} name Status name
|
||||
* @apiParam {String} active Status active
|
||||
*
|
||||
* @apiParamExample {json} Simple Request Example
|
||||
* {
|
||||
* "id": 1,
|
||||
* "name": "Normal",
|
||||
* "active": false
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Object} res Status
|
||||
*
|
||||
* @apiUse StatusObject
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ItemNotFoundError
|
||||
*/
|
||||
public function edit(UpdateStatusRequest $request): JsonResponse
|
||||
{
|
||||
CatEvent::listen(Filter::getBeforeActionEventName(), static function ($item, $requestData) {
|
||||
if (isset($requestData['order'])) {
|
||||
$newOrder = $requestData['order'];
|
||||
$oldOrder = $item->order;
|
||||
if ($newOrder < 1) {
|
||||
$newOrder = 1;
|
||||
}
|
||||
$maxOrder = Status::max('order');
|
||||
if ($newOrder > $maxOrder) {
|
||||
$newOrder = $maxOrder + 1;
|
||||
}
|
||||
$swapItem = Status::where('order', '=', $newOrder)->first();
|
||||
if (isset($swapItem)) {
|
||||
$swapItemOrder = $swapItem->order;
|
||||
|
||||
$item->order = 0;
|
||||
$item->save();
|
||||
|
||||
$swapItem->order = $oldOrder;
|
||||
$swapItem->save();
|
||||
|
||||
$item->order = $swapItemOrder;
|
||||
$item->save();
|
||||
|
||||
} else {
|
||||
$item->order = $newOrder;
|
||||
}
|
||||
}
|
||||
});
|
||||
return $this->_edit($request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /statuses/remove Destroy
|
||||
* @apiDescription Destroy User
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Destroy Status
|
||||
* @apiGroup Status
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiParam {Integer} id ID of the target status
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess (204) No Content Indicates that the status was successfully removed or deactivated.
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function destroy(DestroyStatusRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_destroy($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ListStatusRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
/**
|
||||
* @api {get} /invitations/count Count Invitations
|
||||
* @apiDescription Get the count of invitations
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName CountInvitations
|
||||
* @apiGroup Invitations
|
||||
*
|
||||
* @apiSuccess {Integer} total The total count of pending invitations.
|
||||
*
|
||||
* @apiSuccessExample {json} Success Response:
|
||||
* {
|
||||
* "total": 0
|
||||
* }
|
||||
*
|
||||
* @apiUse TotalSuccess
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function count(ListStatusRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_count($request);
|
||||
}
|
||||
}
|
||||
108
app/Http/Controllers/Api/TaskActivityController.php
Normal file
108
app/Http/Controllers/Api/TaskActivityController.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\ActivityType;
|
||||
use App\Enums\SortDirection;
|
||||
use App\Http\Requests\TaskActivity\ShowTaskActivityRequest;
|
||||
use App\Models\TaskComment;
|
||||
use App\Models\TaskHistory;
|
||||
use CatEvent;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Filter;
|
||||
use App\Helpers\QueryHelper;
|
||||
|
||||
class TaskActivityController extends ItemController
|
||||
{
|
||||
private function getQueryBuilder(array $filter, string $model): Builder
|
||||
{
|
||||
$model = new $model;
|
||||
|
||||
$query = new Builder($model::getQuery());
|
||||
$query->setModel($model);
|
||||
|
||||
$modelScopes = $model->getGlobalScopes();
|
||||
|
||||
foreach ($modelScopes as $key => $value) {
|
||||
$query->withGlobalScope($key, $value);
|
||||
}
|
||||
|
||||
foreach (Filter::process(Filter::getQueryAdditionalRelationsFilterName(), []) as $with) {
|
||||
$query->with($with);
|
||||
}
|
||||
|
||||
QueryHelper::apply($query, $model, $filter);
|
||||
$sortDirection = SortDirection::tryFrom($filter['orderBy'][1])?->value ?? 'asc';
|
||||
$query->orderBy('id', $sortDirection);
|
||||
|
||||
return Filter::process(
|
||||
Filter::getQueryFilterName(),
|
||||
$query
|
||||
);
|
||||
}
|
||||
|
||||
private function getCollectionFromModel(array $requestData, string $model): LengthAwarePaginator
|
||||
{
|
||||
if ($model === TaskComment::class) {
|
||||
$requestData['with'][] = 'attachmentsRelation';
|
||||
$requestData['with'][] = 'attachmentsRelation.user:id,full_name';
|
||||
}
|
||||
|
||||
$itemsQuery = $this->getQueryBuilder($requestData, $model);
|
||||
|
||||
$items = $itemsQuery->paginate(30);
|
||||
|
||||
Filter::process(
|
||||
Filter::getActionFilterName(),
|
||||
$items,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShowTaskActivityRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(ShowTaskActivityRequest $request): JsonResponse
|
||||
{
|
||||
$requestData = Filter::process(Filter::getRequestFilterName(), $request->validated());
|
||||
$requestedActivity = ActivityType::from($requestData['type']);
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $requestData);
|
||||
|
||||
$items = [];
|
||||
$total = 0;
|
||||
$perPage = 0;
|
||||
if ($requestedActivity === ActivityType::ALL) {
|
||||
$taskComments = $this->getCollectionFromModel($requestData, TaskComment::class);
|
||||
$taskHistory = $this->getCollectionFromModel($requestData, TaskHistory::class);
|
||||
$total = $taskComments->total() + $taskHistory->total();
|
||||
$perPage = $taskComments->perPage() + $taskHistory->perPage();
|
||||
|
||||
$sortDirection = SortDirection::tryFrom($requestData['orderBy'][1])?->value ?? 'asc';
|
||||
$items = collect(array_merge($taskComments->items(), $taskHistory->items()))->sortBy([
|
||||
fn ($a, $b) => $sortDirection === 'asc'
|
||||
? strtotime($a['created_at']) <=> strtotime($b['created_at'])
|
||||
: strtotime($b['created_at']) <=> strtotime($a['created_at']),
|
||||
], SORT_REGULAR, $sortDirection === 'desc');
|
||||
} elseif ($requestedActivity === ActivityType::HISTORY) {
|
||||
$taskHistory = $this->getCollectionFromModel($requestData, TaskHistory::class);
|
||||
$total = $taskHistory->total();
|
||||
$perPage = $taskHistory->perPage();
|
||||
$items = $taskHistory->items();
|
||||
} elseif ($requestedActivity === ActivityType::COMMENTS) {
|
||||
$taskComments = $this->getCollectionFromModel($requestData, TaskComment::class);
|
||||
$total = $taskComments->total();
|
||||
$perPage = $taskComments->perPage();
|
||||
$items = $taskComments->items();
|
||||
}
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$items, $requestData]);
|
||||
|
||||
return responder()->success(new LengthAwarePaginator($items, $total, $perPage))->respond();
|
||||
}
|
||||
}
|
||||
281
app/Http/Controllers/Api/TaskCommentController.php
Normal file
281
app/Http/Controllers/Api/TaskCommentController.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Http\Requests\Status\DestroyStatusRequest;
|
||||
use App\Http\Requests\TaskComment\CreateTaskCommentRequest;
|
||||
use App\Http\Requests\TaskComment\DestroyTaskCommentRequest;
|
||||
use App\Http\Requests\TaskComment\ListTaskCommentRequest;
|
||||
use App\Http\Requests\TaskComment\ShowTaskCommentRequestStatus;
|
||||
use App\Http\Requests\TaskComment\UpdateTaskCommentRequest;
|
||||
use Filter;
|
||||
use App\Models\TaskComment;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Throwable;
|
||||
|
||||
class TaskCommentController extends ItemController
|
||||
{
|
||||
protected const MODEL = TaskComment::class;
|
||||
|
||||
/**
|
||||
* @api {post} /task-comment/create Create Task Comment
|
||||
* @apiDescription Create a new task comment
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName CreateTaskComment
|
||||
* @apiGroup TaskComments
|
||||
*
|
||||
*
|
||||
* @apiPermission task_comment_create
|
||||
* @apiPermission task_comment_full_access
|
||||
* @apiParam {Integer} task_id ID of the task
|
||||
* @apiParam {String} comment The content of the comment
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "task_id": 1,
|
||||
* "comment": "This is a new comment"
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Integer} id ID of the created comment
|
||||
* @apiSuccess {Integer} task_id ID of the task
|
||||
* @apiSuccess {Integer} user_id ID of the user who created the comment
|
||||
* @apiSuccess {String} comment The content of the comment
|
||||
* @apiSuccess {String} created_at Creation timestamp
|
||||
* @apiSuccess {String} updated_at Last update timestamp
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example:
|
||||
* HTTP/1.1 201 Created
|
||||
* {
|
||||
* "id": 1,
|
||||
* "task_id": 1,
|
||||
* "user_id": 1,
|
||||
* "comment": "This is a new comment",
|
||||
* "created_at": "2024-07-09T10:00:00.000000Z",
|
||||
* "updated_at": "2024-07-09T10:00:00.000000Z"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function create(CreateTaskCommentRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(
|
||||
Filter::getRequestFilterName(),
|
||||
static function (array $data) use ($request) {
|
||||
$data['user_id'] = $request->user()->id;
|
||||
|
||||
return $data;
|
||||
}
|
||||
);
|
||||
|
||||
return $this->_create($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /task-comment/edit Edit Task Comment
|
||||
* @apiDescription Edit an existing task comment
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName EditTaskComment
|
||||
* @apiGroup TaskComments
|
||||
*
|
||||
*
|
||||
* @apiParam {Integer} id ID of the comment to edit
|
||||
* @apiParam {String} comment The updated content of the comment
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "id": 1,
|
||||
* "comment": "This is the updated comment"
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Integer} id ID of the edited comment
|
||||
* @apiSuccess {Integer} task_id ID of the task
|
||||
* @apiSuccess {Integer} user_id ID of the user who edited the comment
|
||||
* @apiSuccess {String} comment The updated content of the comment
|
||||
* @apiSuccess {String} created_at Creation timestamp
|
||||
* @apiSuccess {String} updated_at Last update timestamp
|
||||
* @apiSuccess {String} deleted_at Deletion timestamp (if applicable, otherwise null)
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "task_id": 1,
|
||||
* "user_id": 1,
|
||||
* "content": "2344",
|
||||
* "created_at": "2024-05-03T10:45:36.000000Z",
|
||||
* "updated_at": "2024-05-03T10:45:36.000000Z",
|
||||
* "deleted_at": null
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function edit(UpdateTaskCommentRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_edit($request);
|
||||
}
|
||||
/**
|
||||
* @api {any} /task-comment/list List Task Comments
|
||||
* @apiDescription Get list of Task Comments
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetTaskCommentList
|
||||
* @apiGroup Task Comments
|
||||
*
|
||||
* @apiPermission task_comment_list
|
||||
* @apiPermission task_comment_full_access
|
||||
*
|
||||
* @apiParam {Integer} [task_id] Optional task ID to filter comments
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "task_id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Integer} id ID of the comment
|
||||
* @apiSuccess {Integer} task_id ID of the task
|
||||
* @apiSuccess {Integer} user_id ID of the user who created the comment
|
||||
* @apiSuccess {String} content Content of the comment
|
||||
* @apiSuccess {String} created_at Creation timestamp
|
||||
* @apiSuccess {String} updated_at Last update timestamp
|
||||
* @apiSuccess {String} deleted_at Deletion timestamp (if applicable, otherwise null)
|
||||
* @apiSuccess {Object} user User who created the comment
|
||||
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "task_id": 1,
|
||||
* "user_id": 1,
|
||||
* "content": "2344",
|
||||
* "created_at": "2024-05-03T10:45:36.000000Z",
|
||||
* "updated_at": "2024-05-03T10:45:36.000000Z",
|
||||
* "deleted_at": null,
|
||||
* "user": {
|
||||
* "id": 1,
|
||||
* "full_name": "Admin",
|
||||
* "email": "admin@cattr.app",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_active": 1,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-02-15T19:06:42.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 0,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2023-10-26 10:26:17",
|
||||
* "online": false,
|
||||
* "can_view_team_tab": true,
|
||||
* "can_create_task": true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function index(ListTaskCommentRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(
|
||||
Filter::getQueryFilterName(),
|
||||
static function ($query) use ($request) {
|
||||
if (!$request->user()->can('edit', TaskComment::class)) {
|
||||
$query = $query->whereHas(
|
||||
'task',
|
||||
static fn (Builder $taskQuery) => $taskQuery->where(
|
||||
'user_id',
|
||||
'=',
|
||||
$request->user()->id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $query->with('user');
|
||||
}
|
||||
);
|
||||
|
||||
return $this->_index($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @apiDeprecated since 1.0.0
|
||||
* @throws Throwable
|
||||
* @api {post} /task-comment/show Show
|
||||
* @apiDescription Show Task Comment
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName ShowTaskComment
|
||||
* @apiGroup Task Comment
|
||||
*
|
||||
* @apiPermission task_comment_show
|
||||
* @apiPermission task_comment_full_access
|
||||
*/
|
||||
public function show(ShowTaskCommentRequestStatus $request): JsonResponse
|
||||
{
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @apiDeprecated since 4.0.0
|
||||
* @api {post} /task-comment/remove Destroy Task Comment
|
||||
* @apiDescription Destroy a Task Comment
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName DestroyTaskComment
|
||||
* @apiGroup Task Comment
|
||||
*
|
||||
* @apiPermission task_comment_remove
|
||||
* @apiPermission task_comment_full_access
|
||||
*
|
||||
* @apiParam {Integer} id ID of the task comment to destroy
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Integer} status Response status code
|
||||
* @apiSuccess {Boolean} success Response success status
|
||||
* @apiSuccess {String} message Success message
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function destroy(DestroyTaskCommentRequest $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
Filter::listen(
|
||||
Filter::getQueryFilterName(),
|
||||
static fn($query) => $user->hasRole([Role::ADMIN, Role::MANAGER]) ? $query :
|
||||
$query->where(['user_id' => $user->id])
|
||||
->whereHas('task', static fn($taskQuery) => $taskQuery->where(['user_id' => $user->id]))
|
||||
);
|
||||
|
||||
return $this->_destroy($request);
|
||||
}
|
||||
}
|
||||
1139
app/Http/Controllers/Api/TaskController.php
Normal file
1139
app/Http/Controllers/Api/TaskController.php
Normal file
File diff suppressed because it is too large
Load Diff
279
app/Http/Controllers/Api/TimeController.php
Normal file
279
app/Http/Controllers/Api/TimeController.php
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Filter;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Validator;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class TimeController extends Controller
|
||||
{
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getControllerRules(): array
|
||||
{
|
||||
return [
|
||||
'total' => 'time.total',
|
||||
'project' => 'time.project',
|
||||
'tasks' => 'time.tasks',
|
||||
'task' => 'time.task',
|
||||
'taskUser' => 'time.task-user',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @api {get,post} /time/total Total
|
||||
* @apiDescription Get total of Time
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Total
|
||||
* @apiGroup Time
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission time_total
|
||||
* @apiPermission time_full_access
|
||||
*
|
||||
* @apiParam {String} start_at Start DataTime
|
||||
* @apiParam {String} end_at End DataTime
|
||||
* @apiParam {Integer} user_id User ID
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "user_id": 1,
|
||||
* "start_at": "2005-01-01 00:00:00",
|
||||
* "end_at": "2019-01-01 00:00:00"
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Integer} time Total time in seconds
|
||||
* @apiSuccess {String} start Datetime of first Time Interval start_at
|
||||
* @apiSuccess {String} end Datetime of last Time Interval end_at
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "time": 338230,
|
||||
* "start": "2020-01-23T19:42:27+00:00",
|
||||
* "end": "2020-04-30T21:58:31+00:00"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse ValidationError
|
||||
*/
|
||||
|
||||
/**
|
||||
* Display a total of time
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function total(Request $request): JsonResponse
|
||||
{
|
||||
$validationRules = [
|
||||
'start_at' => 'required|date',
|
||||
'end_at' => 'required|date',
|
||||
'user_id' => 'required|integer|exists:users,id'
|
||||
];
|
||||
|
||||
$validator = Validator::make($request->all(), $validationRules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return new JsonResponse(
|
||||
Filter::process($this->getEventUniqueName('answer.error.time.total'), [
|
||||
'error_type' => 'validation',
|
||||
'message' => 'Validation error',
|
||||
'info' => $validator->errors()
|
||||
]),
|
||||
400
|
||||
);
|
||||
}
|
||||
$filters = [
|
||||
'start_at' => ['>=', $request->get('start_at')],
|
||||
'end_at' => ['<=', $request->get('end_at')],
|
||||
'user_id' => ['=', $request->get('user_id')]
|
||||
];
|
||||
|
||||
/** @var Builder $itemsQuery */
|
||||
$itemsQuery = Filter::process(
|
||||
$this->getEventUniqueName('answer.success.item.query.prepare'),
|
||||
$this->applyQueryFilter($this->getQuery(), $filters)
|
||||
);
|
||||
|
||||
$timeIntervals = $itemsQuery->get();
|
||||
|
||||
$totalTime = $timeIntervals->sum(static fn($el) => Carbon::parse($el->end_at)->diffInSeconds($el->start_at));
|
||||
|
||||
return responder()->success([
|
||||
'time' => $totalTime,
|
||||
'start' => $timeIntervals->min('start_at'),
|
||||
'end' => $timeIntervals->max('end_at')
|
||||
])->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get,post} /time/tasks Tasks
|
||||
* @apiDescription Get tasks and its total time
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Tasks
|
||||
* @apiGroup Time
|
||||
*
|
||||
* @apiUse TimeIntervalParams
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "user_id": 1,
|
||||
* "task_id": 1,
|
||||
* "project_id": 2,
|
||||
* "start_at": "2005-01-01 00:00:00",
|
||||
* "end_at": "2019-01-01 00:00:00",
|
||||
* "activity_fill": 42,
|
||||
* "mouse_fill": 43,
|
||||
* "keyboard_fill": 43,
|
||||
* "id": [">", 1]
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} current_datetime Current datetime of server
|
||||
* @apiSuccess {Object[]} tasks Array of objects Task
|
||||
* @apiSuccess {Integer} tasks.id Tasks id
|
||||
* @apiSuccess {Integer} tasks.user_id Tasks User id
|
||||
* @apiSuccess {Integer} tasks.project_id Tasks Project id
|
||||
* @apiSuccess {Integer} tasks.time Tasks total time in seconds
|
||||
* @apiSuccess {String} tasks.start Datetime of first Tasks Time Interval start_at
|
||||
* @apiSuccess {String} tasks.end Datetime of last Tasks Time Interval end_at
|
||||
* @apiSuccess {Object[]} total Array of total tasks time
|
||||
* @apiSuccess {Integer} total.time Total time of tasks in seconds
|
||||
* @apiSuccess {String} total.start Datetime of first Time Interval start_at
|
||||
* @apiSuccess {String} total.end DateTime of last Time Interval end_at
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "current_datetime": "2020-01-28T10:57:40+00:00",
|
||||
* "tasks": [
|
||||
* {
|
||||
* "id": 1,
|
||||
* "user_id": 1,
|
||||
* "project_id": 1,
|
||||
* "time": 1490,
|
||||
* "start": "2020-01-23T19:42:27+00:00",
|
||||
* "end": "2020-01-23T20:07:21+00:00"
|
||||
* },
|
||||
* ],
|
||||
* "total": {
|
||||
* "time": 971480,
|
||||
* "start": "2020-01-23T19:42:27+00:00",
|
||||
* "end": "2020-11-01T08:28:06+00:00"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ValidationError
|
||||
*/
|
||||
/**
|
||||
* Display the Tasks and theirs total time
|
||||
*
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function tasks(Request $request): JsonResponse
|
||||
{
|
||||
$validationRules = [
|
||||
'start_at' => 'date',
|
||||
'end_at' => 'date',
|
||||
'project_id' => 'exists:projects,id',
|
||||
'task_id' => 'exists:tasks,id'
|
||||
];
|
||||
|
||||
$validator = Validator::make($request->all(), $validationRules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return new JsonResponse(
|
||||
Filter::process($this->getEventUniqueName('answer.error.time.total'), [
|
||||
'error_type' => 'validation',
|
||||
'message' => 'Validation error',
|
||||
'info' => $validator->errors()
|
||||
]),
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
$filters = $request->all();
|
||||
$request->get('start_at') ? $filters['start_at'] = ['>=', (string)$request->get('start_at')] : false;
|
||||
$request->get('end_at') ? $filters['end_at'] = ['<=', (string)$request->get('end_at')] : false;
|
||||
$request->get('project_id') ? $filters['task.project_id'] = $request->get('project_id') : false;
|
||||
$request->get('task_id') ? $filters['task_id'] = ['in', $request->get('task_id')] : false;
|
||||
|
||||
$baseQuery = $this->applyQueryFilter(
|
||||
$this->getQuery(),
|
||||
$filters ?: []
|
||||
);
|
||||
|
||||
$itemsQuery = Filter::process(
|
||||
$this->getEventUniqueName('answer.success.item.list.query.prepare'),
|
||||
$baseQuery
|
||||
);
|
||||
|
||||
$totalTime = 0;
|
||||
|
||||
$tasks = $itemsQuery
|
||||
->with('task')
|
||||
->get()
|
||||
->groupBy(['task_id', 'user_id'])
|
||||
->map(static function ($taskIntervals, $taskId) use (&$totalTime) {
|
||||
$task = [];
|
||||
|
||||
foreach ($taskIntervals as $userId => $userIntervals) {
|
||||
$taskTime = 0;
|
||||
|
||||
foreach ($userIntervals as $interval) {
|
||||
$taskTime += Carbon::parse($interval->end_at)->diffInSeconds($interval->start_at);
|
||||
}
|
||||
|
||||
$firstUserInterval = $userIntervals->first();
|
||||
$lastUserInterval = $userIntervals->last();
|
||||
|
||||
$task = [
|
||||
'id' => $taskId,
|
||||
'user_id' => $userId,
|
||||
'project_id' => $userIntervals[0]['task']['project_id'],
|
||||
'time' => $taskTime,
|
||||
'start' => Carbon::parse($firstUserInterval->start_at)->toISOString(),
|
||||
'end' => Carbon::parse($lastUserInterval->end_at)->toISOString()
|
||||
];
|
||||
|
||||
$totalTime += $taskTime;
|
||||
}
|
||||
|
||||
return $task;
|
||||
})
|
||||
->values();
|
||||
|
||||
$first = $itemsQuery->get()->first();
|
||||
$last = $itemsQuery->get()->last();
|
||||
|
||||
return responder()->success([
|
||||
'tasks' => $tasks,
|
||||
'total' => [
|
||||
'time' => $totalTime,
|
||||
'start' => $first ? Carbon::parse($first->start_at)->toISOString() : null,
|
||||
'end' => $last ? Carbon::parse($last->end_at)->toISOString() : null,
|
||||
]
|
||||
])->respond();
|
||||
}
|
||||
}
|
||||
570
app/Http/Controllers/Api/UserController.php
Normal file
570
app/Http/Controllers/Api/UserController.php
Normal file
@@ -0,0 +1,570 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App;
|
||||
use App\Enums\Role;
|
||||
use App\Enums\ScreenshotsState;
|
||||
use App\Http\Requests\User\ListUsersRequest;
|
||||
use App\Scopes\UserAccessScope;
|
||||
use Settings;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Filter;
|
||||
use App\Mail\UserCreated;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use CatEvent;
|
||||
use Mail;
|
||||
use App\Http\Requests\User\CreateUserRequest;
|
||||
use App\Http\Requests\User\EditUserRequest;
|
||||
use App\Http\Requests\User\SendInviteUserRequest;
|
||||
use App\Http\Requests\User\ShowUserRequest;
|
||||
use App\Http\Requests\User\DestroyUserRequest;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Throwable;
|
||||
|
||||
class UserController extends ItemController
|
||||
{
|
||||
protected const MODEL = User::class;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @api {get, post} /users/list List
|
||||
* @apiDescription Get list of Users with any params
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName GetUserList
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission users_list
|
||||
* @apiPermission users_full_access
|
||||
*
|
||||
* @apiSuccess {Object[]} users List of users.
|
||||
* @apiSuccess {Integer} users.id The unique ID of the user.
|
||||
* @apiSuccess {String} users.full_name Full name of the user.
|
||||
* @apiSuccess {String} users.email Email address of the user.
|
||||
* @apiSuccess {String} users.url URL associated with the user.
|
||||
* @apiSuccess {Integer} users.company_id ID of the company the user belongs to.
|
||||
* @apiSuccess {String} users.avatar URL of the user's avatar image.
|
||||
* @apiSuccess {Integer} users.screenshots_state The current state of screenshot monitoring.
|
||||
* @apiSuccess {Boolean} users.manual_time Indicates if manual time tracking is allowed.
|
||||
* @apiSuccess {Integer} users.computer_time_popup Time in seconds before showing a time popup.
|
||||
* @apiSuccess {Boolean} users.blur_screenshots Indicates if screenshots are blurred.
|
||||
* @apiSuccess {Boolean} users.web_and_app_monitoring Indicates if web and app monitoring is enabled.
|
||||
* @apiSuccess {Integer} users.screenshots_interval Interval in minutes for taking screenshots.
|
||||
* @apiSuccess {Boolean} users.active Indicates if the user is active.
|
||||
* @apiSuccess {String} users.deleted_at Deletion timestamp, or `null` if the user is not deleted.
|
||||
* @apiSuccess {String} users.created_at Creation timestamp of the user.
|
||||
* @apiSuccess {String} users.updated_at Last update timestamp of the user.
|
||||
* @apiSuccess {String} users.timezone The timezone of the user, or `null`.
|
||||
* @apiSuccess {Boolean} users.important Indicates if the user is marked as important.
|
||||
* @apiSuccess {Boolean} users.change_password Indicates if the user must change their password.
|
||||
* @apiSuccess {Integer} users.role_id ID of the user's role.
|
||||
* @apiSuccess {String} users.user_language Language preference of the user.
|
||||
* @apiSuccess {String} users.type The user type, e.g., "employee".
|
||||
* @apiSuccess {Boolean} users.invitation_sent Indicates if an invitation has been sent.
|
||||
* @apiSuccess {Integer} users.nonce Nonce value for secure actions.
|
||||
* @apiSuccess {Boolean} users.client_installed Indicates if the client software is installed.
|
||||
* @apiSuccess {Boolean} users.permanent_screenshots Indicates if permanent screenshots are enabled.
|
||||
* @apiSuccess {String} users.last_activity The last recorded activity timestamp.
|
||||
* @apiSuccess {Boolean} users.screenshots_state_locked Indicates if screenshot state is locked.
|
||||
* @apiSuccess {Boolean} users.online Indicates if the user is currently online.
|
||||
* @apiSuccess {Boolean} users.can_view_team_tab Indicates if the user can view the team tab.
|
||||
* @apiSuccess {Boolean} users.can_create_task Indicates if the user can create tasks.
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* [
|
||||
* {
|
||||
* "id": 1,
|
||||
* "full_name": "Admin",
|
||||
* "email": "admin@cattr.app",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_state": 1,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-08-19T10:42:18.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 0,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2024-08-19 10:42:18",
|
||||
* "screenshots_state_locked": false,
|
||||
* "online": false,
|
||||
* "can_view_team_tab": true,
|
||||
* "can_create_task": true
|
||||
* },
|
||||
* {
|
||||
* "id": 2,
|
||||
* "full_name": "Fabiola Mertz",
|
||||
* "email": "projectManager@example.com",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_state": 2,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 2,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2023-10-26 09:44:17",
|
||||
* "screenshots_state_locked": false,
|
||||
* "online": false,
|
||||
* "can_view_team_tab": false,
|
||||
* "can_create_task": false
|
||||
* },...
|
||||
* ]
|
||||
* }
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
public function index(ListUsersRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_index($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /users/create Create
|
||||
* @apiDescription Create User Entity
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName CreateUser
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission users_create
|
||||
* @apiPermission users_full_access
|
||||
*
|
||||
* @apiParam {String} user_language The language of the new user (e.g., "en")
|
||||
* @apiParam {String} timezone The timezone of the new user (e.g., "Europe/Moscow")
|
||||
* @apiParam {Integer} role_id ID of the role of the new user
|
||||
* @apiParam {Integer} active Will new user be active or not `(1 - active, 0 - not)`
|
||||
* @apiParam {Integer} screenshots_state State of screenshots monitoring (e.g., 1 for enabled)
|
||||
* @apiParam {Boolean} send_invite Whether to send an invitation to the new user (true - send, false - do not send)
|
||||
* @apiParam {Boolean} manual_time Whether manual time tracking is enabled for the new user
|
||||
* @apiParam {Integer} screenshots_interval Interval in minutes for taking screenshots
|
||||
* @apiParam {Integer} computer_time_popup Time in minutes before showing a time popup
|
||||
* @apiParam {String} type The type of user (e.g., "employee")
|
||||
* @apiParam {Boolean} web_and_app_monitoring Whether web and app monitoring is enabled
|
||||
* @apiParam {String} email New user email
|
||||
* @apiParam {String} password New user password
|
||||
* @apiParam {String} full_name New user name
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "user_language" : "en",
|
||||
* "timezone" : "Europe/Moscow",
|
||||
* "role_id" : 2,
|
||||
* "active" : true,
|
||||
* "screenshots_state" : 1,
|
||||
* "send_invite" : 1,
|
||||
* "manual_time" : 1,
|
||||
* "screenshots_interval" : 10,
|
||||
* "computer_time_popup" : 3,
|
||||
* "type" : "employee",
|
||||
* "web_and_app_monitoring" : 1,
|
||||
* "email" : "123@cattr.app",
|
||||
* "password" : "password",
|
||||
* "full_name" : "name"
|
||||
* }
|
||||
* @apiSuccess {String} full_name Full name of the user.
|
||||
* @apiSuccess {String} email Email address of the user.
|
||||
* @apiSuccess {String} user_language Language of the user.
|
||||
* @apiSuccess {Boolean} active Whether the user is active.
|
||||
* @apiSuccess {Integer} screenshots_state State of screenshots monitoring.
|
||||
* @apiSuccess {Boolean} manual_time Whether manual time tracking is enabled.
|
||||
* @apiSuccess {Integer} screenshots_interval Interval in minutes for taking screenshots.
|
||||
* @apiSuccess {Integer} computer_time_popup Time in minutes before showing a time popup.
|
||||
* @apiSuccess {String} timezone Timezone of the user.
|
||||
* @apiSuccess {Integer} role_id ID of the role assigned to the user.
|
||||
* @apiSuccess {String} type Type of the user (e.g., "employee").
|
||||
* @apiSuccess {Boolean} web_and_app_monitoring Whether web and app monitoring is enabled.
|
||||
* @apiSuccess {Boolean} screenshots_state_locked Whether the screenshot state is locked.
|
||||
* @apiSuccess {Boolean} invitation_sent Whether an invitation has been sent.
|
||||
* @apiSuccess {String} updated_at Timestamp of the last update.
|
||||
* @apiSuccess {String} created_at Timestamp of when the user was created.
|
||||
* @apiSuccess {Integer} id ID of the created user.
|
||||
* @apiSuccess {Boolean} online Whether the user is currently online.
|
||||
* @apiSuccess {Boolean} can_view_team_tab Whether the user can view the team tab.
|
||||
* @apiSuccess {Boolean} can_create_task Whether the user can create tasks.
|
||||
*
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "full_name": "name",
|
||||
* "email": "123@cattr.app",
|
||||
* "user_language": "en",
|
||||
* "active": 1,
|
||||
* "screenshots_state": 1,
|
||||
* "manual_time": 1,
|
||||
* "screenshots_interval": 10,
|
||||
* "computer_time_popup": 3,
|
||||
* "timezone": "Europe/Moscow",
|
||||
* "role_id": 2,
|
||||
* "type": "employee",
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_state_locked": true,
|
||||
* "invitation_sent": true,
|
||||
* "updated_at": "2024-08-21T14:29:06.000000Z",
|
||||
* "created_at": "2024-08-21T14:29:06.000000Z",
|
||||
* "id": 10,
|
||||
* "online": false,
|
||||
* "can_view_team_tab": false,
|
||||
* "can_create_task": false
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ForbiddenError
|
||||
*/
|
||||
/**
|
||||
* @param CreateUserRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function create(CreateUserRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(Filter::getRequestFilterName(), static function ($requestData) use ($request) {
|
||||
$requestData['screenshots_state_locked'] = $request->user()->isAdmin() && ScreenshotsState::tryFrom($requestData['screenshots_state'])->mustBeInherited();
|
||||
|
||||
return $requestData;
|
||||
});
|
||||
|
||||
return $this->_create($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /users/edit Edit
|
||||
* @apiDescription Edit User
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName EditUser
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission users_edit
|
||||
* @apiPermission users_full_access
|
||||
* @apiParam {String} user_language The language of the new user (e.g., "en")
|
||||
* @apiParam {String} timezone The timezone of the new user (e.g., "Europe/Moscow")
|
||||
* @apiParam {Integer} role_id ID of the role of the new user
|
||||
* @apiParam {Integer} id The ID of the user being edited.
|
||||
* @apiParam {String} full_name New user name
|
||||
* @apiParam {String} email New user email
|
||||
* @apiParam {String} url URL associated with the user
|
||||
* @apiParam {Integer} company_id The ID of the company to which the user belongs
|
||||
* @apiParam {String} avatar The URL of the user’s avatar
|
||||
* @apiParam {Integer} screenshots_state State of screenshots monitoring (e.g., 1 for enabled)
|
||||
* @apiParam {Boolean} manual_time Whether manual time tracking is enabled for the new user
|
||||
* @apiParam {Integer} computer_time_popup Time in minutes before showing a time popup
|
||||
* @apiParam {Boolean} blur_screenshots Indicates if screenshots are blurred
|
||||
* @apiParam {Boolean} web_and_app_monitoring Whether web and app monitoring is enabled
|
||||
* @apiParam {Integer} screenshots_interval Interval in minutes for taking screenshots
|
||||
* @apiParam {Integer} active Will new user be active or not `(1 - active, 0 - not)`
|
||||
* @apiParam {String} deleted_at Deletion timestamp, or `null` if the user is not deleted.
|
||||
* @apiParam {Boolean} send_invite Whether to send an invitation to the new user (true - send, false - do not send)
|
||||
*
|
||||
*
|
||||
*
|
||||
* @apiParam {String} type The type of user (e.g., "employee")
|
||||
*
|
||||
* @apiParam {String} password New user password
|
||||
*
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "user_language" : "en",
|
||||
* "timezone" : "Europe/Moscow",
|
||||
* "role_id" : 2,
|
||||
* "id" : 3,
|
||||
* "full_name" : "Rachael Reichert",
|
||||
* "email": "projectAuditor@example.com",
|
||||
* "url" : null,
|
||||
* "company_id" : 1,
|
||||
* "avatar" : null,
|
||||
* "screenshots_state" : 1,
|
||||
* "manual_time" : 0,
|
||||
* "computer_time_popup" : 300,
|
||||
* "blur_screenshots" : false,
|
||||
* "web_and_app_monitoring" : true,
|
||||
* "screenshots_interval" : 5,
|
||||
* "active" : true,
|
||||
* "deleted_at" : null,
|
||||
* "created_at" : "2023-10-26T10:26:42.000000Z",
|
||||
* "updated_at" : "2023-10-26T10:26:42.000000Z",
|
||||
* "important" : 0,
|
||||
* "change_password" : 0,
|
||||
* "type" : "employee",
|
||||
* "invitation_sent" : false,
|
||||
* "nonce" : 0,
|
||||
* "client_installed" : 0,
|
||||
* "permanent_screenshots" : 0,
|
||||
* "last_activity" : "2023-10-26 10:05:42",
|
||||
* "screenshots_state_locked" : false,
|
||||
* "online" : false,
|
||||
* "can_view_team_tab" : false,
|
||||
* "can_create_task" : false
|
||||
* }
|
||||
* @apiUse UserObject
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ItemNotFoundError
|
||||
*/
|
||||
/**
|
||||
* @param EditUserRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function edit(EditUserRequest $request): JsonResponse
|
||||
{
|
||||
Filter::listen(Filter::getActionFilterName(), static function (User $user) use ($request) {
|
||||
if ($user->screenshots_state_locked && !$request->user()->isAdmin()) {
|
||||
$user->screenshots_state = $user->getOriginal('screenshots_state');
|
||||
return $user;
|
||||
}
|
||||
|
||||
$user->screenshots_state_locked = $request->user()->isAdmin() && ScreenshotsState::tryFrom($user->screenshots_state)->mustBeInherited();
|
||||
|
||||
return $user;
|
||||
});
|
||||
|
||||
return $this->_edit($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get, post} /users/show Show User
|
||||
* @apiDescription Retrieves detailed information about a specific user.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName ShowUser
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission users_show
|
||||
* @apiPermission users_full_access
|
||||
*
|
||||
* @apiParam {Integer} id User id
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
* @apiUse UserObject
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse ItemNotFoundError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse ValidationError
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param ShowUserRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function show(ShowUserRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_show($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
* @api {post} /users/remove Destroy
|
||||
* @apiDescription Destroy User
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName DestroyUser
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission users_remove
|
||||
* @apiPermission users_full_access
|
||||
*
|
||||
* @apiParam {Integer} id ID of the target user
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} message Destroy status
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 204 No Content
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ValidationError
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function destroy(DestroyUserRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_destroy($request);
|
||||
}
|
||||
/**
|
||||
* @throws Exception
|
||||
* @api {get,post} /users/count Count
|
||||
* @apiDescription Count Users
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Count
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission users_count
|
||||
* @apiPermission users_full_access
|
||||
*
|
||||
* @apiSuccess {String} total Amount of users that we have
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "total": 2
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function count(ListUsersRequest $request): JsonResponse
|
||||
{
|
||||
return $this->_count($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SendInviteUserRequest $request
|
||||
* @return JsonResponse
|
||||
* @throws Throwable
|
||||
*/
|
||||
/**
|
||||
* @api {post} /api/users/send-invite Send User Invitation
|
||||
* @apiDescription Sends an invitation to a user by generating a password, marking the invitation as sent, and dispatching relevant events.
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName SendUserInvite
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiPermission users_invite
|
||||
*
|
||||
* @apiParam {Integer} id The ID of the user to whom the invitation will be sent.
|
||||
*
|
||||
* @apiParamExample {json} Request Example:
|
||||
* {
|
||||
* "id": 1
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} message A confirmation that the invite was sent successfully.
|
||||
*
|
||||
* @apiSuccessExample {json} Success Response:
|
||||
* HTTP/1.1 204 No Content
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ForbiddenError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function sendInvite(SendInviteUserRequest $request): JsonResponse
|
||||
{
|
||||
$requestId = Filter::process(Filter::getRequestFilterName(), $request->validated('id'));
|
||||
|
||||
$itemsQuery = $this->getQuery(['id' => $requestId]);
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $requestId);
|
||||
|
||||
$item = Filter::process(Filter::getActionFilterName(), $itemsQuery->first());
|
||||
|
||||
$password = Str::random();
|
||||
$item->password = $password;
|
||||
$item->invitation_sent = true;
|
||||
$item->save();
|
||||
|
||||
throw_unless($item, new NotFoundHttpException);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), [$requestId, $item]);
|
||||
|
||||
$language = Settings::scope('core')->get('language', 'en');
|
||||
|
||||
Mail::to($item->email)->locale($language)->send(new UserCreated($item->email, $password));
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {patch} /users/activity Activity
|
||||
* @apiDescription Updates the time of the user's last activity
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Activity
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 204 No Content
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function updateActivity(): JsonResponse
|
||||
{
|
||||
$user = request()->user();
|
||||
|
||||
CatEvent::dispatch(Filter::getBeforeActionEventName(), $user);
|
||||
|
||||
Filter::process(Filter::getActionFilterName(), $user)->update(['last_activity' => Carbon::now()]);
|
||||
|
||||
CatEvent::dispatch(Filter::getAfterActionEventName(), $user);
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
}
|
||||
334
app/Http/Controllers/AuthController.php
Normal file
334
app/Http/Controllers/AuthController.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\Entities\AuthorizationException;
|
||||
use App\Exceptions\Entities\DeprecatedApiException;
|
||||
use App\Helpers\Recaptcha;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use App\Http\Transformers\AuthTokenTransformer;
|
||||
use Cache;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class AuthController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @apiDefine AuthHeader
|
||||
* @apiHeader {String} Authorization Token for user auth
|
||||
* @apiHeaderExample {json} Authorization Header Example
|
||||
* {
|
||||
* "Authorization": "bearer 16184cf3b2510464a53c0e573c75740540fe..."
|
||||
* }
|
||||
*/
|
||||
|
||||
public function __construct(protected Recaptcha $recaptcha)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /auth/login Login
|
||||
* @apiDescription Get user Token
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Login
|
||||
* @apiGroup Auth
|
||||
*
|
||||
* @apiParam {String} email User email
|
||||
* @apiParam {String} password User password
|
||||
* @apiParam {String} [recaptcha] Recaptcha token
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "email": "johndoe@example.com",
|
||||
* "password": "amazingpassword",
|
||||
* "recaptcha": "03AOLTBLR5UtIoenazYWjaZ4AFZiv1OWegWV..."
|
||||
* }
|
||||
* @apiUse User
|
||||
* @apiUse 400Error
|
||||
* @apiUse ParamsValidationError
|
||||
* @apiUse UnauthorizedError
|
||||
* @apiUse UserDeactivatedError
|
||||
* @apiUse CaptchaError
|
||||
* @apiUse LimiterError
|
||||
*/
|
||||
public function login(LoginRequest $request): JsonResponse
|
||||
{
|
||||
$credentials = $request->only(['email', 'password', 'recaptcha']);
|
||||
|
||||
$this->recaptcha->check($credentials);
|
||||
|
||||
if (!auth()->attempt([
|
||||
'email' => $credentials['email'],
|
||||
'password' => $credentials['password'],
|
||||
])) {
|
||||
$this->recaptcha->incrementCaptchaAmounts();
|
||||
$this->recaptcha->check($credentials);
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$user = auth()->user();
|
||||
if (!$user || !$user->active) {
|
||||
$this->recaptcha->incrementCaptchaAmounts();
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_USER_DISABLED);
|
||||
}
|
||||
|
||||
if ($user->invitation_sent) {
|
||||
$user->invitation_sent = false;
|
||||
$user->save();
|
||||
}
|
||||
|
||||
$this->recaptcha->clearCaptchaAmounts();
|
||||
|
||||
if (preg_match('/' . config('auth.cattr-client-agent') . '/', $request->header('User_agent'))) {
|
||||
$user->client_installed = 1;
|
||||
$user->save();
|
||||
}
|
||||
|
||||
return responder()->success([
|
||||
'token' => $user->createToken(Str::uuid())->plainTextToken,
|
||||
], new AuthTokenTransformer)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /auth/logout Logout
|
||||
* @apiDescription Invalidate current token
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Logout
|
||||
* @apiGroup Auth
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 204 OK
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function logout(Request $request): JsonResponse
|
||||
{
|
||||
$request->user()->currentAccessToken()->delete();
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /auth/logout-from-all Logout from all
|
||||
* @apiDescription Invalidate all user tokens
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Logout all
|
||||
* @apiGroup Auth
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 204 OK
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function logoutFromAll(Request $request): JsonResponse
|
||||
{
|
||||
$request->user()->tokens()->delete();
|
||||
|
||||
return responder()->success()->respond(204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /auth/me Me
|
||||
* @apiDescription Get authenticated User Entity
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Me
|
||||
* @apiGroup Auth
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccess {Integer} id ID of the user
|
||||
* @apiSuccess {String} full_name Full name of the user
|
||||
* @apiSuccess {String} email Email of the user
|
||||
* @apiSuccess {String} [url] URL of the user (optional)
|
||||
* @apiSuccess {Integer} company_id Company ID of the user
|
||||
* @apiSuccess {String} [avatar] Avatar URL of the user (optional)
|
||||
* @apiSuccess {Boolean} screenshots_active Indicates if screenshots are active
|
||||
* @apiSuccess {Boolean} manual_time Indicates if manual time tracking is allowed
|
||||
* @apiSuccess {Integer} computer_time_popup Time interval for computer time popup
|
||||
* @apiSuccess {Boolean} blur_screenshots Indicates if screenshots are blurred
|
||||
* @apiSuccess {Boolean} web_and_app_monitoring Indicates if web and app monitoring is enabled
|
||||
* @apiSuccess {Integer} screenshots_interval Interval for taking screenshots
|
||||
* @apiSuccess {Boolean} active Indicates if the user is active
|
||||
* @apiSuccess {String} [deleted_at] Deletion timestamp (if applicable, otherwise null)
|
||||
* @apiSuccess {String} created_at Creation timestamp
|
||||
* @apiSuccess {String} updated_at Last update timestamp
|
||||
* @apiSuccess {String} [timezone] Timezone of the user (optional)
|
||||
* @apiSuccess {Boolean} important Indicates if the user is marked as important
|
||||
* @apiSuccess {Boolean} change_password Indicates if the user needs to change password
|
||||
* @apiSuccess {Integer} role_id Role ID of the user
|
||||
* @apiSuccess {String} user_language Language of the user
|
||||
* @apiSuccess {String} type Type of the user (e.g., "employee")
|
||||
* @apiSuccess {Boolean} invitation_sent Indicates if invitation is sent to the user
|
||||
* @apiSuccess {Integer} nonce Nonce value of the user
|
||||
* @apiSuccess {Boolean} client_installed Indicates if client is installed
|
||||
* @apiSuccess {Boolean} permanent_screenshots Indicates if screenshots are permanent
|
||||
* @apiSuccess {String} last_activity Last activity timestamp of the user
|
||||
* @apiSuccess {Boolean} online Indicates if the user is online
|
||||
* @apiSuccess {Boolean} can_view_team_tab Indicates if the user can view team tab
|
||||
* @apiSuccess {Boolean} can_create_task Indicates if the user can create tasks
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "id": 1,
|
||||
* "full_name": "Admin",
|
||||
* "email": "admin@cattr.app",
|
||||
* "url": "",
|
||||
* "company_id": 1,
|
||||
* "avatar": "",
|
||||
* "screenshots_active": 1,
|
||||
* "manual_time": 0,
|
||||
* "computer_time_popup": 300,
|
||||
* "blur_screenshots": false,
|
||||
* "web_and_app_monitoring": true,
|
||||
* "screenshots_interval": 5,
|
||||
* "active": 1,
|
||||
* "deleted_at": null,
|
||||
* "created_at": "2023-10-26T10:26:17.000000Z",
|
||||
* "updated_at": "2024-02-15T19:06:42.000000Z",
|
||||
* "timezone": null,
|
||||
* "important": 0,
|
||||
* "change_password": 0,
|
||||
* "role_id": 0,
|
||||
* "user_language": "en",
|
||||
* "type": "employee",
|
||||
* "invitation_sent": false,
|
||||
* "nonce": 0,
|
||||
* "client_installed": 0,
|
||||
* "permanent_screenshots": 0,
|
||||
* "last_activity": "2023-10-26 10:26:17",
|
||||
* "online": false,
|
||||
* "can_view_team_tab": true,
|
||||
* "can_create_task": true
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
public function me(Request $request): JsonResponse
|
||||
{
|
||||
return responder()->success($request->user())->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /auth/desktop-key Issue key
|
||||
* @apiDescription Issues key for desktop auth
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Issue key
|
||||
* @apiGroup Auth
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
* @apiUse User
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function issueDesktopKey(Request $request): JsonResponse
|
||||
{
|
||||
$token = Str::random(40);
|
||||
|
||||
$lifetime = now()->addMinutes(config('auth.lifetime_minutes.desktop_token'));
|
||||
|
||||
Cache::store('octane')->put(
|
||||
sha1($request->ip()) . ":$token",
|
||||
$request->user()->id,
|
||||
$lifetime,
|
||||
);
|
||||
return responder()->success([
|
||||
'token' => $token,
|
||||
'type' => 'desktop',
|
||||
'expires' => now()->addMinutes(config('auth.lifetime_minutes.desktop_token'))->toIso8601String(),
|
||||
], new AuthTokenTransformer)->meta(['frontend_uri' => config('app.frontend_url')])->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {put} /auth/desktop-key Key auth
|
||||
* @apiDescription Exchange desktop key to JWT
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Key auth
|
||||
* @apiGroup Auth
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
* @apiUse User
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function authDesktopKey(Request $request): JsonResponse
|
||||
{
|
||||
$token = $request->header('Authorization');
|
||||
|
||||
if (!$token) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$token = explode(' ', $token);
|
||||
|
||||
if (count($token) !== 2 || $token[0] !== 'desktop' || !Cache::store('octane')->has(sha1($request->ip()) . ":$token[1]")) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$user = auth()->loginUsingId(Cache::store('octane')->get(sha1($request->ip()) . ":$token[1]"));
|
||||
|
||||
if (!optional($user)->active) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_USER_DISABLED);
|
||||
}
|
||||
|
||||
return responder()->success([
|
||||
'token' => $user->createToken(Str::uuid())->plainTextToken,
|
||||
], new AuthTokenTransformer)->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* @apiDeprecated Exists only for compatibility with old Cattr client
|
||||
* @api {post} /auth/refresh Refresh
|
||||
* @apiDescription Refreshes JWT
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Refresh
|
||||
* @apiGroup Auth
|
||||
*
|
||||
* @apiUse AuthHeader
|
||||
*
|
||||
* @apiSuccess {String} access_token Token
|
||||
* @apiSuccess {String} token_type Token Type
|
||||
* @apiSuccess {String} expires_in Token TTL 8601String Date
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return JsonResponse
|
||||
* @deprecated Exists only for compatibility with old Cattr client
|
||||
*/
|
||||
public function refresh(): JsonResponse
|
||||
{
|
||||
throw new DeprecatedApiException();
|
||||
}
|
||||
}
|
||||
71
app/Http/Controllers/Controller.php
Normal file
71
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Routing\Route as RouteModel;
|
||||
use Illuminate\Routing\RouteCollection;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\Collection;
|
||||
use Route;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
use DispatchesJobs;
|
||||
use ValidatesRequests;
|
||||
|
||||
public static function getControllerRules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function frontendRoute(Request $request) {
|
||||
return view('app');
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel router pass to fallback not non-exist urls only but wrong-method requests too.
|
||||
* So required to check if route have alternative request methods
|
||||
* throw not-found or wrong-method exceptions manually
|
||||
* @param Request $request
|
||||
*/
|
||||
public function universalRoute(Request $request): void
|
||||
{
|
||||
/** @var Router $router */
|
||||
$router = app('router');
|
||||
/** @var RouteCollection $routes */
|
||||
$routeCollection = $router->getRoutes();
|
||||
/** @var string[] $methods */
|
||||
$methods = array_diff(Router::$verbs, [$request->getMethod(), 'OPTIONS']);
|
||||
|
||||
foreach ($methods as $method) {
|
||||
// Get all routes for method without fallback routes
|
||||
/** @var Route[]|Collection $routes */
|
||||
$routes = collect($routeCollection->get($method))->filter(static function ($route) {
|
||||
/** @var RouteModel $route */
|
||||
return !$route->isFallback && $route->uri !== '{fallbackPlaceholder}';
|
||||
});
|
||||
|
||||
// Look if any route have match with current request
|
||||
$mismatch = $routes->first(static function ($value) use ($request) {
|
||||
/** @var RouteModel $value */
|
||||
return $value->matches($request, false);
|
||||
});
|
||||
|
||||
// Throw wrong-method exception if matches found
|
||||
if ($mismatch !== null) {
|
||||
throw new MethodNotAllowedHttpException([]);
|
||||
}
|
||||
}
|
||||
|
||||
// No matches, throw not-found exception
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
}
|
||||
237
app/Http/Controllers/PasswordResetController.php
Normal file
237
app/Http/Controllers/PasswordResetController.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\Entities\AuthorizationException;
|
||||
use App\Helpers\Recaptcha;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\PasswordReset as PasswordResetEvent;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use DB;
|
||||
use Mail;
|
||||
use Password;
|
||||
use Validator;
|
||||
|
||||
class PasswordResetController extends BaseController
|
||||
{
|
||||
public function __construct(private Recaptcha $recaptcha)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /v1/auth/password/reset/validate Validate
|
||||
* @apiDescription Validates password reset token
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Validate token
|
||||
* @apiGroup Password Reset
|
||||
*
|
||||
* @apiParam {String} email User email
|
||||
* @apiParam {String} token Password reset token
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "email": "johndoe@example.com",
|
||||
* "token": "03AOLTBLR5UtIoenazYWjaZ4AFZiv1OWegWV..."
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} message Message from server
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "message": "Password reset data is valid"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ParamsValidationError
|
||||
* @apiUse InvalidPasswordResetDataError
|
||||
*/
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function validate(Request $request): JsonResponse
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'email' => 'required|email',
|
||||
'token' => 'required|string'
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_VALIDATION_FAILED);
|
||||
}
|
||||
|
||||
$user = Password::broker()->getUser($request->all());
|
||||
if (!$user) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_INVALID_PASSWORD_RESET_DATA);
|
||||
}
|
||||
|
||||
$isValidToken = Password::broker()->getRepository()->exists($user, $request->input('token'));
|
||||
if (!$isValidToken) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_INVALID_PASSWORD_RESET_DATA);
|
||||
}
|
||||
|
||||
return new JsonResponse(['message' => 'Password reset data is valid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /v1/auth/password/reset/request Request
|
||||
* @apiDescription Sends email to user with reset link
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Request
|
||||
* @apiGroup Password Reset
|
||||
*
|
||||
* @apiParam {String} login User login
|
||||
* @apiParam {String} [recaptcha] Recaptcha token
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "email": "johndoe@example.com",
|
||||
* "recaptcha": "03AOLTBLR5UtIoenazYWjaZ4AFZiv1OWegWV..."
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} message Message from server
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "message": "Link for restore password has been sent to specified email"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ParamsValidationError
|
||||
* @apiUse NoSuchUserError
|
||||
* @apiUse CaptchaError
|
||||
* @apiUse LimiterError
|
||||
*/
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function request(Request $request): JsonResponse
|
||||
{
|
||||
$validator = Validator::make($request->all(), ['email' => 'required|email']);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_VALIDATION_FAILED);
|
||||
}
|
||||
|
||||
$credentials = $request->only(['email', 'recaptcha']);
|
||||
$this->recaptcha->check($credentials);
|
||||
$user = User::where('email', $credentials['email'])->first();
|
||||
|
||||
if (!$user) {
|
||||
$this->recaptcha->incrementCaptchaAmounts();
|
||||
$this->recaptcha->check($credentials);
|
||||
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$this->recaptcha->clearCaptchaAmounts();
|
||||
|
||||
Password::broker()->sendResetLink(['email' => $credentials['email']]);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => 'Link for restore password has been sent to specified email',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @api {post} /v1/auth/password/reset/process Process
|
||||
* @apiDescription Resets user password
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName Process
|
||||
* @apiGroup Password Reset
|
||||
*
|
||||
* @apiParam {String} email User email
|
||||
* @apiParam {String} token Password reset token
|
||||
* @apiParam {String} password New password
|
||||
* @apiParam {String} password_confirmation Password confirmation
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "email": "johndoe@example.com",
|
||||
* "token": "16184cf3b2510464a53c0e573c75740540fe...",
|
||||
* "password_confirmation": "amazingpassword",
|
||||
* "password": "amazingpassword"
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {String} access_token Token
|
||||
* @apiSuccess {String} token_type Token Type
|
||||
* @apiSuccess {String} expires_in Token TTL in seconds
|
||||
* @apiSuccess {Object} user User Entity
|
||||
*
|
||||
* @apiUse UserObject
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "access_token": "16184cf3b2510464a53c0e573c75740540fe...",
|
||||
* "token_type": "bearer",
|
||||
* "password": "amazingpassword",
|
||||
* "expires_in": "3600",
|
||||
* "user": {}
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
* @apiUse ParamsValidationError
|
||||
* @apiUse InvalidPasswordResetDataError
|
||||
* @apiUse UnauthorizedError
|
||||
*/
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function process(Request $request): JsonResponse
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'email' => 'required|email',
|
||||
'token' => 'required|string',
|
||||
'password' => 'required',
|
||||
'password_confirmation' => 'required'
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_VALIDATION_FAILED);
|
||||
}
|
||||
|
||||
$resetRequest = DB::table('password_resets')
|
||||
->where('email', $request->input('email'))
|
||||
->first();
|
||||
|
||||
if (!$resetRequest) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_INVALID_PASSWORD_RESET_DATA);
|
||||
}
|
||||
|
||||
$response = Password::broker()->reset(
|
||||
$request->all(),
|
||||
static function (User $user, string $password) {
|
||||
$user->password = $password;
|
||||
$user->save();
|
||||
event(new PasswordResetEvent($user));
|
||||
auth()->login($user);
|
||||
}
|
||||
);
|
||||
|
||||
if ($response !== Password::PASSWORD_RESET) {
|
||||
throw new AuthorizationException(AuthorizationException::ERROR_TYPE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$token = auth()->setTTL(config('auth.lifetime_minutes.jwt'))->refresh();
|
||||
|
||||
return new JsonResponse([
|
||||
'access_token' => $token,
|
||||
'token_type' => 'bearer',
|
||||
'expires_in' => now()->addMinutes(config('auth.lifetime_minutes.jwt'))->toIso8601String(),
|
||||
'user' => auth()->user(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
150
app/Http/Controllers/RegistrationController.php
Normal file
150
app/Http/Controllers/RegistrationController.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\ScreenshotsState;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Settings;
|
||||
|
||||
/**
|
||||
* Class RegistrationController
|
||||
* @codeCoverageIgnore until it is implemented on frontend
|
||||
*/
|
||||
class RegistrationController extends Controller
|
||||
{
|
||||
/**
|
||||
* @param $key
|
||||
* @return JsonResponse
|
||||
* @api {get} /auth/register/{key} Get Form
|
||||
* @apiDescription Returns invitation form data by a invitation token
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName GetRegistration
|
||||
* @apiGroup Invitation
|
||||
*
|
||||
* @apiParam (Parameters from url) {String} key User invitation key
|
||||
*
|
||||
* @apiSuccess {String} email UserInvited email
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "email": "test@example.com"
|
||||
* }
|
||||
*
|
||||
* @apiErrorExample {json} Email not found
|
||||
* HTTP/1.1 404 Not found
|
||||
* {
|
||||
* "error": "Not found"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
*
|
||||
*/
|
||||
public function getForm($key): JsonResponse
|
||||
{
|
||||
$invitation = Invitation::where('key', $key)
|
||||
->where('expires_at', '>=', time())
|
||||
->first();
|
||||
|
||||
if (!isset($invitation)) {
|
||||
return new JsonResponse([
|
||||
'message' => __('The specified key has expired or does not exist')
|
||||
], 404);
|
||||
}
|
||||
|
||||
return responder()->success(['email' => $invitation->email])->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $key
|
||||
* @return JsonResponse
|
||||
* @api {post} /auth/register/{key} Post Form
|
||||
* @apiDescription Registers user by key
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName PostRegistration
|
||||
* @apiGroup Invitation
|
||||
*
|
||||
* @apiParam (Parameters from url) {String} key User invitation key
|
||||
*
|
||||
* @apiParam {String} email New user email
|
||||
* @apiParam {String} password New user password
|
||||
* @apiParam {String} fullName New user name
|
||||
*
|
||||
* @apiParamExample {json} Request Example
|
||||
* {
|
||||
* "email": "johndoe@example.com",
|
||||
* "password": "amazingpassword",
|
||||
* "fullName": "John Doe"
|
||||
* }
|
||||
*
|
||||
* @apiSuccess {Number} user_id New user ID
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "user_id": 2
|
||||
* }
|
||||
*
|
||||
* @apiErrorExample {json} Email not found
|
||||
* HTTP/1.1 404 Not found
|
||||
* {
|
||||
* "message": "The specified key has expired or does not exist"
|
||||
* }
|
||||
*
|
||||
* @apiErrorExample {json} Email mismatch
|
||||
* HTTP/1.1 400 Bad request
|
||||
* {
|
||||
* "message": "The email address does not match the key"
|
||||
* }
|
||||
*
|
||||
* @apiUse 400Error
|
||||
*
|
||||
*/
|
||||
public function postForm(Request $request, string $key): JsonResponse
|
||||
{
|
||||
$invitation = Invitation::where('key', $key)
|
||||
->where('expires_at', '>=', time())
|
||||
->first();
|
||||
|
||||
if (!isset($invitation)) {
|
||||
return new JsonResponse([
|
||||
'message' => __('The specified key has expired or does not exist'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
if ($request->input('email') !== $invitation->email) {
|
||||
return new JsonResponse([
|
||||
'message' => __('The email address does not match the key'),
|
||||
], 400);
|
||||
}
|
||||
|
||||
$language = Settings::scope('core')->get('language', 'en');
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::create([
|
||||
'full_name' => $request->input('full_name'),
|
||||
'email' => $request->input('email'),
|
||||
'password' => $request->input('password'),
|
||||
'active' => true,
|
||||
'manual_time' => false,
|
||||
'screenshots_state' => ScreenshotsState::REQUIRED,
|
||||
'screenshots_state_locked' => true,
|
||||
'computer_time_popup' => 3,
|
||||
'screenshots_interval' => 10,
|
||||
'role_id' => $invitation->role_id,
|
||||
'user_language' => $language,
|
||||
]);
|
||||
|
||||
$invitation->delete();
|
||||
|
||||
return responder()->success(['user_id' => $user->id])->respond();
|
||||
}
|
||||
}
|
||||
44
app/Http/Controllers/StatusController.php
Normal file
44
app/Http/Controllers/StatusController.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\CatHelper;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class StatusController extends Controller
|
||||
{
|
||||
/**
|
||||
* @api {get} /status Status
|
||||
* @apiDescription Check API status
|
||||
*
|
||||
* @apiVersion 4.0.0
|
||||
* @apiName Status
|
||||
* @apiGroup Status
|
||||
*
|
||||
* @apiSuccess {String} cat A cat for you
|
||||
* @apiSuccess {Array} modules Information about installed modules
|
||||
* @apiSuccess {Array} version Information about version modules
|
||||
*
|
||||
* @apiSuccessExample {json} Response Example
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "cattr": true,
|
||||
* "cat": "(=ㅇ༝ㅇ=)"
|
||||
* "version": "dev"
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __invoke(): JsonResponse
|
||||
{
|
||||
return responder()->success([
|
||||
'cattr' => true,
|
||||
'cat' => CatHelper::getCat(),
|
||||
'version' => config('app.version'),
|
||||
])->respond();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user