Closes Module 1: 9 auth endpoints under /api/v1/auth, OTP via SMS (Semaphore + log + fake drivers), role middleware, role + admin seeders, 27 feature tests passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
1.6 KiB
PHP
51 lines
1.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V1\Auth;
|
|
|
|
use App\Http\Controllers\Api\V1\ApiController;
|
|
use App\Http\Requests\Auth\LoginRequest;
|
|
use App\Http\Resources\UserResource;
|
|
use App\Models\User;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Support\Facades\Hash;
|
|
|
|
class LoginController extends ApiController
|
|
{
|
|
public function __invoke(LoginRequest $request): JsonResponse
|
|
{
|
|
$data = $request->validated();
|
|
|
|
$user = User::query()
|
|
->when($data['email'] ?? null, fn ($q, $email) => $q->where('email', $email))
|
|
->when($data['phone'] ?? null, fn ($q, $phone) => $q->where('phone', $phone))
|
|
->first();
|
|
|
|
if (! $user || ! Hash::check($data['password'], $user->password)) {
|
|
return $this->fail('Invalid credentials', null, 401);
|
|
}
|
|
|
|
if ($user->status === User::STATUS_SUSPENDED) {
|
|
return $this->forbidden('Account suspended');
|
|
}
|
|
|
|
if ($user->status === User::STATUS_PENDING) {
|
|
return $this->fail(
|
|
'Account pending verification. Verify the OTP sent during registration.',
|
|
['account' => ['Verify your phone before logging in.']],
|
|
403,
|
|
);
|
|
}
|
|
|
|
$user->forceFill(['last_login_at' => now()])->save();
|
|
|
|
$deviceName = $data['device_name'] ?? $request->userAgent() ?? 'unknown';
|
|
$token = $user->createToken($deviceName);
|
|
|
|
return $this->ok([
|
|
'user' => new UserResource($user),
|
|
'token' => $token->plainTextToken,
|
|
'token_type' => 'Bearer',
|
|
], 'Login successful');
|
|
}
|
|
}
|