Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions backend/app/Http/Actions/Admin/Events/GetAllEventsAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace HiEvents\Http\Actions\Admin\Events;

use HiEvents\DomainObjects\Enums\Role;
use HiEvents\Http\Actions\BaseAction;
use HiEvents\Resources\Event\AdminEventResource;
use HiEvents\Services\Application\Handlers\Admin\DTO\GetAllEventsDTO;
use HiEvents\Services\Application\Handlers\Admin\GetAllEventsHandler;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class GetAllEventsAction extends BaseAction
{
public function __construct(
private readonly GetAllEventsHandler $handler,
)
{
}

public function __invoke(Request $request): JsonResponse
{
$this->minimumAllowedRole(Role::SUPERADMIN);

$events = $this->handler->handle(new GetAllEventsDTO(
perPage: min((int)$request->query('per_page', 20), 100),
search: $request->query('search'),
sortBy: $request->query('sort_by', 'start_date'),
sortDirection: $request->query('sort_direction', 'desc'),
));

return $this->resourceResponse(
resource: AdminEventResource::class,
data: $events
);
}
}
17 changes: 17 additions & 0 deletions backend/app/Http/Actions/BaseAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,23 @@ protected function getAuthenticatedAccountId(): int
throw new UnauthorizedException();
}

protected function getAuthenticatedUserRole(): Role
{
if (Auth::check()) {
/** @var AuthUserService $service */
$service = app(AuthUserService::class);
$role = $service->getAuthenticatedUserRole();

if ($role === null) {
throw new UnauthorizedException(__('No user role found in token'));
}

return $role;
}

throw new UnauthorizedException();
}

protected function getAuthenticatedUser(): UserDomainObject|DomainObjectInterface
{
if (Auth::check()) {
Expand Down
9 changes: 9 additions & 0 deletions backend/app/Http/Actions/Events/GetEventPublicAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace HiEvents\Http\Actions\Events;

use HiEvents\DomainObjects\Enums\Role;
use HiEvents\DomainObjects\EventDomainObject;
use HiEvents\DomainObjects\Status\EventStatus;
use HiEvents\Http\Actions\BaseAction;
Expand Down Expand Up @@ -52,6 +53,14 @@ private function canUserViewEvent(EventDomainObject $event): bool
return true;
}

if ($this->isUserAuthenticated() && $this->getAuthenticatedUserRole() === Role::SUPERADMIN) {
$this->logger->debug(__('Superadmin user is viewing non-live event with ID :eventId', [
'eventId' => $event->getId(),
'accountId' => $this->getAuthenticatedAccountId(),
]));
return true;
}

return false;
}
}
37 changes: 36 additions & 1 deletion backend/app/Repository/Eloquent/EventRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

namespace HiEvents\Repository\Eloquent;

use HiEvents\DomainObjects\AccountDomainObject;
use HiEvents\DomainObjects\EventDomainObject;
use HiEvents\DomainObjects\EventStatisticDomainObject;
use HiEvents\DomainObjects\Generated\EventDomainObjectAbstract;
use HiEvents\DomainObjects\OrganizerDomainObject;
use HiEvents\DomainObjects\Status\EventStatus;
use HiEvents\Http\DTO\QueryParamsDTO;
use HiEvents\Models\Event;
use HiEvents\Repository\Eloquent\Value\Relationship;
use HiEvents\Repository\Interfaces\EventRepositoryInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pagination\LengthAwarePaginator;
Expand Down Expand Up @@ -96,9 +100,40 @@ public function getUpcomingEventsForAdmin(int $perPage): LengthAwarePaginator
->where(EventDomainObjectAbstract::START_DATE, '<=', $next24Hours)
->whereIn(EventDomainObjectAbstract::STATUS, [
EventStatus::LIVE->name,
EventStatus::DRAFT->name,
])
->orderBy(EventDomainObjectAbstract::START_DATE, 'asc')
->paginate($perPage));
}

public function getAllEventsForAdmin(
?string $search = null,
int $perPage = 20,
?string $sortBy = 'start_date',
?string $sortDirection = 'desc'
): LengthAwarePaginator {
$this->model = $this->model
->select('events.*')
->withCount('attendees');

if ($search) {
$this->model = $this->model->where(function ($q) use ($search) {
$q->where(EventDomainObjectAbstract::TITLE, 'ilike', '%' . $search . '%')
->orWhereHas('organizer', function ($orgQuery) use ($search) {
$orgQuery->where('name', 'ilike', '%' . $search . '%');
});
});
}

$allowedSortColumns = ['start_date', 'end_date', 'title', 'created_at'];
$sortColumn = in_array($sortBy, $allowedSortColumns, true) ? $sortBy : 'start_date';
$sortDir = in_array(strtolower($sortDirection), ['asc', 'desc']) ? $sortDirection : 'desc';

$this->model = $this->model->orderBy($sortColumn, $sortDir);

$this->loadRelation(new Relationship(OrganizerDomainObject::class, name: 'organizer'));
$this->loadRelation(new Relationship(AccountDomainObject::class, name: 'account'));
$this->loadRelation(new Relationship(EventStatisticDomainObject::class, name: 'event_statistics'));

return $this->paginate($perPage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ public function findEventsForOrganizer(int $organizerId, int $accountId, QueryPa
public function findEvents(array $where, QueryParamsDTO $params): LengthAwarePaginator;

public function getUpcomingEventsForAdmin(int $perPage): LengthAwarePaginator;

public function getAllEventsForAdmin(
?string $search = null,
int $perPage = 20,
?string $sortBy = 'start_date',
?string $sortDirection = 'desc'
): LengthAwarePaginator;
}
39 changes: 39 additions & 0 deletions backend/app/Resources/Event/AdminEventResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace HiEvents\Resources\Event;

use HiEvents\DomainObjects\EventDomainObject;
use HiEvents\Resources\BaseResource;
use Illuminate\Http\Request;

/**
* @mixin EventDomainObject
*/
class AdminEventResource extends BaseResource
{
public function toArray(Request $request): array
{
$statistics = $this->getEventStatistics();

return [
'id' => $this->getId(),
'title' => $this->getTitle(),
'start_date' => $this->getStartDate(),
'end_date' => $this->getEndDate(),
'status' => $this->getStatus(),
'organizer_name' => $this->getOrganizer()?->getName(),
'organizer_id' => $this->getOrganizerId(),
'account_name' => $this->getAccount()?->getName(),
'account_id' => $this->getAccountId(),
'user_id' => $this->getUserId(),
'slug' => $this->getSlug(),
'statistics' => $statistics ? [
'total_gross_sales' => $statistics->getSalesTotalGross(),
'products_sold' => $statistics->getProductsSold(),
'attendees_registered' => $statistics->getAttendeesRegistered(),
'orders_created' => $statistics->getOrdersCreated(),
'orders_cancelled' => $statistics->getOrdersCancelled(),
] : null,
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace HiEvents\Services\Application\Handlers\Admin\DTO;

use HiEvents\DataTransferObjects\BaseDataObject;
use HiEvents\DomainObjects\Generated\EventDomainObjectAbstract;

class GetAllEventsDTO extends BaseDataObject
{
public function __construct(
public readonly int $perPage = 20,
public readonly ?string $search = null,
public readonly ?string $sortBy = EventDomainObjectAbstract::START_DATE,
public readonly ?string $sortDirection = 'desc',
)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace HiEvents\Services\Application\Handlers\Admin;

use HiEvents\Repository\Interfaces\EventRepositoryInterface;
use HiEvents\Services\Application\Handlers\Admin\DTO\GetAllEventsDTO;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class GetAllEventsHandler
{
public function __construct(
private readonly EventRepositoryInterface $eventRepository,
)
{
}

public function handle(GetAllEventsDTO $dto): LengthAwarePaginator
{
return $this->eventRepository->getAllEventsForAdmin(
search: $dto->search,
perPage: $dto->perPage,
sortBy: $dto->sortBy,
sortDirection: $dto->sortDirection,
);
}
}
22 changes: 22 additions & 0 deletions backend/app/Services/Domain/Auth/AuthUserService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace HiEvents\Services\Domain\Auth;

use Exception;
use HiEvents\DomainObjects\Enums\Role;
use HiEvents\DomainObjects\Interfaces\DomainObjectInterface;
use HiEvents\DomainObjects\UserDomainObject;
use HiEvents\Models\User;
Expand Down Expand Up @@ -38,6 +40,26 @@ public function getAuthenticatedAccountId(): ?int
return $payload->get('account_id');
}

public function getAuthenticatedUserRole(): ?Role
{
if (!$this->authManager->check()) {
return null;
}

try {
/** @var Payload $payload */
$payload = $this->authManager->payload();
} catch (JWTException) {
return null;
}

try {
return Role::from($payload->get('role'));
} catch (Exception) {
return null;
}
}

public function getUser(): UserDomainObject|DomainObjectInterface|null
{
/** @var User $user */
Expand Down
42 changes: 37 additions & 5 deletions backend/app/Services/Domain/Auth/LoginService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use HiEvents\DomainObjects\AccountDomainObject;
use HiEvents\DomainObjects\AccountUserDomainObject;
use HiEvents\DomainObjects\Enums\Role;
use HiEvents\DomainObjects\Status\UserStatus;
use HiEvents\DomainObjects\UserDomainObject;
use HiEvents\Exceptions\UnauthorizedException;
Expand Down Expand Up @@ -56,9 +57,17 @@ public function authenticate(string $email, string $password, ?int $requestedAcc
$this->validateUserStatus($accountId, $userAccounts);
}

$userRole = $this->getUserRole($accountId, $userAccounts);

return new LoginResponse(
accounts: $accounts,
token: $this->getToken($accounts, $email, $password, $requestedAccountId),
token: $this->getToken(
accounts: $accounts,
email: $email,
password: $password,
requestedAccountId: $requestedAccountId,
userRole: $userRole,
),
user: $user,
accountId: $accountId,
);
Expand All @@ -83,7 +92,13 @@ private function getAccountId(Collection $accounts, ?int $requestedAccountId): ?
return null;
}

private function getToken(Collection $accounts, string $email, string $password, ?int $requestedAccountId): ?string
private function getToken(
Collection $accounts,
string $email,
string $password,
?int $requestedAccountId,
?Role $userRole,
): ?string
{
$accountId = $this->getAccountId($accounts, $requestedAccountId);

Expand All @@ -92,9 +107,13 @@ private function getToken(Collection $accounts, string $email, string $password,
return null;
}

$token = $this->jwtAuth->claims([
'account_id' => $accountId,
])->attempt([
$claims = ['account_id' => $accountId];

if ($userRole !== null) {
$claims['role'] = $userRole->value;
}

$token = $this->jwtAuth->claims($claims)->attempt([
'email' => strtolower($email),
'password' => $password,
]);
Expand All @@ -118,4 +137,17 @@ private function validateUserStatus(int $accountId, Collection $userAccounts): v
throw new UnauthorizedException(__('User account is not active'));
}
}

private function getUserRole(?int $accountId, Collection $userAccounts): ?Role
{
if ($accountId === null) {
return null;
}

/** @var AccountUserDomainObject $currentAccount */
$currentAccount = $userAccounts
->first(fn(AccountUserDomainObject $userAccount) => $userAccount->getAccountId() === $accountId);

return Role::from($currentAccount?->getRole());
}
}
2 changes: 2 additions & 0 deletions backend/routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
use HiEvents\Http\Actions\Users\UpdateMeAction;
use HiEvents\Http\Actions\Users\UpdateUserAction;
use HiEvents\Http\Actions\Admin\Accounts\GetAllAccountsAction;
use HiEvents\Http\Actions\Admin\Events\GetAllEventsAction;
use HiEvents\Http\Actions\Admin\Events\GetUpcomingEventsAction;
use HiEvents\Http\Actions\Admin\Stats\GetAdminStatsAction;
use HiEvents\Http\Actions\Admin\Users\GetAllUsersAction;
Expand Down Expand Up @@ -374,6 +375,7 @@ function (Router $router): void {
$router->get('/stats', GetAdminStatsAction::class);
$router->get('/accounts', GetAllAccountsAction::class);
$router->get('/users', GetAllUsersAction::class);
$router->get('/events', GetAllEventsAction::class);
$router->get('/events/upcoming', GetUpcomingEventsAction::class);
$router->post('/impersonate/{user_id}', StartImpersonationAction::class);
$router->post('/stop-impersonation', StopImpersonationAction::class);
Expand Down
Loading
Loading