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

649 lines
25 KiB
PHP

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