Skip to content

Commit

Permalink
Merge branch 'develop' into dev-ks/dbperf
Browse files Browse the repository at this point in the history
  • Loading branch information
MrKrisKrisu authored Aug 2, 2024
2 parents b182e2b + d6771a6 commit 0fea4e2
Show file tree
Hide file tree
Showing 34 changed files with 1,247 additions and 305 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ WORKDIR /usr/src/trwl
RUN composer install --ignore-platform-reqs --no-interaction --no-progress --no-suggest --optimize-autoloader
RUN php artisan optimize

FROM php:8.3.9-apache
FROM php:8.3.10-apache
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public

RUN apt update && \
Expand Down
1 change: 1 addition & 0 deletions app/Console/Commands/DatabaseCleaner/DatabaseCleaner.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public function handle(): int {
$this->call(Polylines::class);
$this->call(PolylinesBrouter::class);
$this->call(User::class);
$this->call(TrustedUser::class);
$this->call(Trips::class);

$this->call('queue-monitor:purge', ['--beforeDays' => 7]);
Expand Down
18 changes: 18 additions & 0 deletions app/Console/Commands/DatabaseCleaner/TrustedUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Console\Commands\DatabaseCleaner;

use App\Models\TrustedUser as TrustedUserModel;
use Illuminate\Console\Command;

class TrustedUser extends Command
{
protected $signature = 'app:clean-db:trusted-user';
protected $description = 'Find and delete expired trusted users from database';

public function handle(): int {
$affectedRows = TrustedUserModel::where('expires_at', '<', now())->delete();
$this->info($affectedRows . ' expired trusted users deleted.');
return 0;
}
}
20 changes: 20 additions & 0 deletions app/Enum/User/FriendCheckinSetting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);

namespace App\Enum\User;

/**
* @OA\Schema(
* title="FriendCheckinSetting",
* type="string",
* enum={"forbidden", "friends", "list"},
* example="forbidden",
* )
*/
enum FriendCheckinSetting: string
{
case FORBIDDEN = 'forbidden'; // default
case FRIENDS = 'friends'; // user who are following each other
case LIST = 'list'; // specific list of users
}

9 changes: 9 additions & 0 deletions app/Http/Controllers/API/v1/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace App\Http\Controllers\API\v1;

use App\Models\OAuthClient;
use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Throwable;
Expand Down Expand Up @@ -137,4 +139,11 @@ public static function getCurrentOAuthClient(): OAuthClient|null {
return null;
}
}

protected function getUserOrSelf(string|int $userIdOrSelf): Authenticatable {
if ($userIdOrSelf === 'self') {
return auth()->user();
}
return User::findOrFail($userIdOrSelf);
}
}
82 changes: 45 additions & 37 deletions app/Http/Controllers/API/v1/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Enum\MapProvider;
use App\Enum\MastodonVisibility;
use App\Enum\StatusVisibility;
use App\Enum\User\FriendCheckinSetting;
use App\Exceptions\RateLimitExceededException;
use App\Http\Controllers\Backend\SettingsController as BackendSettingsController;
use App\Http\Resources\UserProfileSettingsResource;
Expand Down Expand Up @@ -63,39 +64,6 @@ public function updateMail(Request $request): UserProfileSettingsResource|JsonRe
}
}

public function resendMail(): void {
try {
auth()->user()->sendEmailVerificationNotification();
$this->sendResponse('', 204);
} catch (RateLimitExceededException) {
$this->sendError(error: __('email.verification.too-many-requests'), code: 429);
}
}

/**
* @throws ValidationException
*/
public function updatePassword(Request $request): UserProfileSettingsResource|JsonResponse {
$userHasPassword = auth()->user()->password !== null;

$validated = $request->validate([
'currentPassword' => [Rule::requiredIf($userHasPassword)],
'password' => ['required', 'string', 'min:8', 'confirmed']
]);

if ($userHasPassword && !Hash::check($validated['currentPassword'], auth()->user()->password)) {
throw ValidationException::withMessages([__('controller.user.password-wrong')]);
}

$validated['password'] = Hash::make($validated['password']);

try {
return new UserProfileSettingsResource(BackendSettingsController::updateSettings($validated));
} catch (RateLimitExceededException) {
return $this->sendError(error: __('email.verification.too-many-requests'), code: 400);
}
}

/**
* @OA\Put(
* path="/settings/profile",
Expand Down Expand Up @@ -128,6 +96,13 @@ public function updatePassword(Request $request): UserProfileSettingsResource|Js
* type="string",
* nullable=true,
* @OA\Schema(ref="#/components/schemas/MapProvider")
* ),
* @OA\Property(
* property="friendCheckin",
* type="string",
* nullable=true,
* @OA\Schema(ref="#/components/schemas/FriendCheckinSetting"),
* example="forbidden"
* )
* )
* ),
Expand All @@ -148,10 +123,9 @@ public function updatePassword(Request $request): UserProfileSettingsResource|Js
*/
public function updateSettings(Request $request): UserProfileSettingsResource|JsonResponse {
$validated = $request->validate([
'username' => ['required',
'string',
'max:25',
'regex:/^[a-zA-Z0-9_]*$/'],
'username' => [
'required', 'string', 'max:25', 'regex:/^[a-zA-Z0-9_]*$/'
],
'displayName' => ['required', 'string', 'max:50'],
'privateProfile' => ['boolean', 'nullable'],
'preventIndex' => ['boolean', 'nullable'],
Expand All @@ -165,8 +139,42 @@ public function updateSettings(Request $request): UserProfileSettingsResource|Js
new Enum(MastodonVisibility::class),
],
'mapProvider' => ['nullable', new Enum(MapProvider::class)],
'friendCheckin' => ['nullable', new Enum(FriendCheckinSetting::class)]
]);

try {
return new UserProfileSettingsResource(BackendSettingsController::updateSettings($validated));
} catch (RateLimitExceededException) {
return $this->sendError(error: __('email.verification.too-many-requests'), code: 400);
}
}

public function resendMail(): void {
try {
auth()->user()->sendEmailVerificationNotification();
$this->sendResponse('', 204);
} catch (RateLimitExceededException) {
$this->sendError(error: __('email.verification.too-many-requests'), code: 429);
}
}

/**
* @throws ValidationException
*/
public function updatePassword(Request $request): UserProfileSettingsResource|JsonResponse {
$userHasPassword = auth()->user()->password !== null;

$validated = $request->validate([
'currentPassword' => [Rule::requiredIf($userHasPassword)],
'password' => ['required', 'string', 'min:8', 'confirmed']
]);

if ($userHasPassword && !Hash::check($validated['currentPassword'], auth()->user()->password)) {
throw ValidationException::withMessages([__('controller.user.password-wrong')]);
}

$validated['password'] = Hash::make($validated['password']);

try {
return new UserProfileSettingsResource(BackendSettingsController::updateSettings($validated));
} catch (RateLimitExceededException) {
Expand Down
51 changes: 39 additions & 12 deletions app/Http/Controllers/API/v1/TransportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@

namespace App\Http\Controllers\API\v1;

use App\Dto\Internal\CheckinSuccessDto;
use App\Dto\Transport\Station as StationDto;
use App\Enum\Business;
use App\Enum\StatusVisibility;
use App\Enum\TravelType;
use App\Exceptions\Checkin\AlreadyCheckedInException;
use App\Exceptions\CheckInCollisionException;
use App\Exceptions\HafasException;
use App\Exceptions\NotConnectedException;
use App\Exceptions\StationNotOnTripException;
use App\Http\Controllers\Backend\Transport\HomeController;
use App\Http\Controllers\Backend\Transport\StationController;
use App\Http\Controllers\Backend\Transport\TrainCheckinController;
use App\Http\Controllers\HafasController;
use App\Http\Controllers\TransportController as TransportBackend;
use App\Http\Resources\CheckinSuccessResource;
use App\Http\Resources\StationResource;
use App\Http\Resources\StatusResource;
use App\Http\Resources\TripResource;
use App\Hydrators\CheckinRequestHydrator;
use App\Models\Event;
use App\Models\Station;
use App\Models\User;
use App\Notifications\YouHaveBeenCheckedIn;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
Expand Down Expand Up @@ -332,9 +329,9 @@ public function getNextStationByCoordinates(Request $request): JsonResponse {
/**
* @OA\Post(
* path="/trains/checkin",
* operationId="createTrainCheckin",
* operationId="createCheckin",
* tags={"Checkin"},
* summary="Create a checkin",
* summary="Check in to a trip.",
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(ref="#/components/schemas/CheckinRequestBody")
Expand All @@ -345,11 +342,11 @@ public function getNextStationByCoordinates(Request $request): JsonResponse {
* @OA\JsonContent(ref="#/components/schemas/CheckinSuccessResource")
* ),
* @OA\Response(response=400, description="Bad request"),
* @OA\Response(response=409, description="Checkin collision"),
* @OA\Response(response=401, description="Unauthorized"),
* @OA\Response(response=403, description="Forbidden", @OA\JsonContent(ref="#/components/schemas/CheckinForbiddenWithUsersResponse")),
* @OA\Response(response=409, description="Checkin collision"),
* security={
* {"passport": {"create-statuses"}}, {"token": {}}
*
* }
* )
*
Expand All @@ -372,13 +369,43 @@ public function create(Request $request): JsonResponse {
'destination' => ['required', 'numeric'],
'departure' => ['required', 'date'],
'arrival' => ['required', 'date'],
'force' => ['nullable', 'boolean']
'force' => ['nullable', 'boolean'],
'with' => ['nullable', 'array', 'max:10'],
]);
if (isset($validated['with'])) {
$withUsers = User::whereIn('id', $validated['with'])->get();
$forbiddenUsers = collect();
foreach ($withUsers as $user) {
if (!Auth::user()?->can('checkin', $user)) {
$forbiddenUsers->push($user);
}
}
if ($forbiddenUsers->isNotEmpty()) {
$forbiddenUserIds = $forbiddenUsers->pluck('id')->toArray();
return response()->json(
data: [
'message' => 'You are not allowed to check in the following users: ' . implode(',', $forbiddenUserIds),
'meta' => [
'invalidUsers' => $forbiddenUserIds
]
],
status: 403
);
}
}

try {
$checkinResponse = TrainCheckinController::checkin((new CheckinRequestHydrator($validated))->hydrateFromApi());
$dto = (new CheckinRequestHydrator($validated))->hydrateFromApi();
$checkinResponse = TrainCheckinController::checkin($dto);

// if isset, check in the other users with their default values
foreach ($withUsers ?? [] as $user) {
$dto->setUser($user);
$dto->setStatusVisibility($user->default_status_visibility);
$checkin = TrainCheckinController::checkin($dto);
$user->notify(new YouHaveBeenCheckedIn($checkin->status, auth()->user()));
}

//ToDo: Check if documented structure has changed
return $this->sendResponse(new CheckinSuccessResource($checkinResponse), 201);
} catch (CheckInCollisionException $exception) {
return $this->sendError([
Expand Down
Loading

0 comments on commit 0fea4e2

Please sign in to comment.