MIF_E31231430/app/Http/Controllers/Auth/AuthController.php

279 lines
9.4 KiB
PHP

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Mail\RegisterOtpMail;
use App\Models\LoginHistory;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use Throwable;
class AuthController extends Controller
{
private const PENDING_REGISTRATION_SESSION_KEY = 'auth.pending_registration';
public function showLogin(): View
{
return view('auth.login');
}
public function showRegister(): View
{
return view('auth.register');
}
public function showVerifyRegisterOtp(Request $request): View|RedirectResponse
{
$pendingRegistration = $this->pendingRegistrationData($request);
if ($pendingRegistration === null) {
return redirect()
->route('register')
->withErrors(['email' => 'Silakan isi formulir registrasi terlebih dahulu.']);
}
return view('auth.verify-register-otp', [
'pendingEmail' => $pendingRegistration['email'],
'expiresAt' => $pendingRegistration['otp_expires_at'],
]);
}
public function login(Request $request): RedirectResponse
{
$request->validate([
'login' => ['required', 'string'],
'password' => ['required', 'string'],
]);
$loginInput = $request->input('login');
$field = filter_var($loginInput, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
if (! Auth::attempt([$field => $loginInput, 'password' => $request->input('password')], $request->boolean('remember'))) {
throw ValidationException::withMessages([
'login' => 'Email/username atau password tidak valid.',
]);
}
$request->session()->regenerate();
/** @var User $user */
$user = $request->user();
$this->recordLoginHistory($request, $user, $loginInput);
return $this->redirectAfterLogin($request, $user)
->with('status', 'Login berhasil.');
}
public function register(Request $request): RedirectResponse
{
$validated = $request->validate([
'name' => ['required', 'string', 'max:100'],
'username' => ['required', 'string', 'max:50', 'alpha_dash', 'unique:users,username'],
'email' => ['required', 'email', 'max:100', 'unique:users,email'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
try {
$this->issueRegistrationOtp($request, [
'name' => $validated['name'],
'username' => $validated['username'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
} catch (Throwable) {
return back()
->withInput($request->except(['password', 'password_confirmation']))
->withErrors(['email' => 'Kode OTP gagal dikirim ke email. Pastikan konfigurasi Gmail SMTP sudah aktif lalu coba lagi.']);
}
return redirect()
->route('register.verify')
->with('status', 'Kode OTP sudah dikirim ke email Anda. Masukkan 6 digit OTP untuk menyelesaikan registrasi.');
}
public function verifyRegisterOtp(Request $request): RedirectResponse
{
$validated = $request->validate([
'otp' => ['required', 'digits:6'],
]);
$pendingRegistration = $this->pendingRegistrationData($request);
if ($pendingRegistration === null) {
return redirect()
->route('register')
->withErrors(['email' => 'Sesi OTP registrasi sudah habis. Silakan daftar ulang.']);
}
if (now()->timestamp > $pendingRegistration['otp_expires_at']) {
return back()->withErrors([
'otp' => 'Kode OTP sudah kedaluwarsa. Silakan kirim ulang OTP.',
]);
}
if (! Hash::check($validated['otp'], $pendingRegistration['otp_hash'])) {
throw ValidationException::withMessages([
'otp' => 'Kode OTP tidak valid.',
]);
}
if (
User::query()->where('email', '=', $pendingRegistration['email'])->exists()
|| User::query()->where('username', '=', $pendingRegistration['username'])->exists()
) {
$this->clearPendingRegistration($request);
return redirect()
->route('register')
->withErrors(['email' => 'Email atau username sudah digunakan. Silakan registrasi ulang dengan data yang berbeda.']);
}
$user = User::create([
'name' => $pendingRegistration['name'],
'username' => $pendingRegistration['username'],
'email' => $pendingRegistration['email'],
'password' => $pendingRegistration['password'],
'role' => 'user',
'member_level' => 'tanpa_member',
'email_verified_at' => now(),
]);
$this->clearPendingRegistration($request);
Auth::login($user);
$request->session()->regenerate();
$this->recordLoginHistory($request, $user, $user->email);
return redirect()
->route('profile')
->with('status', 'Registrasi berhasil. Email sudah terverifikasi dan Anda sudah masuk ke akun.');
}
public function resendRegisterOtp(Request $request): RedirectResponse
{
$pendingRegistration = $this->pendingRegistrationData($request);
if ($pendingRegistration === null) {
return redirect()
->route('register')
->withErrors(['email' => 'Sesi OTP registrasi sudah habis. Silakan daftar ulang.']);
}
if (
isset($pendingRegistration['otp_sent_at'])
&& (now()->timestamp - (int) $pendingRegistration['otp_sent_at']) < 60
) {
return back()->withErrors([
'otp' => 'Tunggu sebentar sebelum meminta OTP baru.',
]);
}
try {
$this->issueRegistrationOtp($request, [
'name' => $pendingRegistration['name'],
'username' => $pendingRegistration['username'],
'email' => $pendingRegistration['email'],
'password' => $pendingRegistration['password'],
]);
} catch (Throwable) {
return back()->withErrors([
'otp' => 'OTP gagal dikirim ulang. Periksa konfigurasi email lalu coba lagi.',
]);
}
return back()->with('status', 'OTP baru sudah dikirim ke email Anda.');
}
public function logout(Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('landing')->with('status', 'Berhasil logout.');
}
private function redirectAfterLogin(Request $request, User $user): RedirectResponse
{
if ($user->isAdmin()) {
$request->session()->forget('url.intended');
return redirect()->route('dashboard');
}
$hasUnreadAdminNotification = $user->penyewaans()
->whereNotNull('admin_catatan')
->whereNull('admin_catatan_read_at')
->exists();
if ($hasUnreadAdminNotification) {
$request->session()->forget('url.intended');
return redirect()
->route('profile')
->with('status', 'Ada notifikasi baru dari admin pada pengajuan sewa Anda.');
}
$intendedUrl = $request->session()->pull('url.intended');
if ($intendedUrl) {
return redirect()->to($intendedUrl);
}
return redirect()->route('profile');
}
private function recordLoginHistory(Request $request, User $user, string $loginIdentifier): void
{
LoginHistory::create([
'user_id' => $user->id,
'login_identifier' => $loginIdentifier,
'ip_address' => $request->ip(),
'user_agent' => (string) $request->userAgent(),
'login_at' => now(),
]);
}
private function issueRegistrationOtp(Request $request, array $registrationData): void
{
$otp = (string) random_int(100000, 999999);
Mail::to($registrationData['email'])->send(new RegisterOtpMail(
otp: $otp,
recipientName: $registrationData['name']
));
$request->session()->put(self::PENDING_REGISTRATION_SESSION_KEY, [
'name' => $registrationData['name'],
'username' => $registrationData['username'],
'email' => $registrationData['email'],
'password' => $registrationData['password'],
'otp_hash' => Hash::make($otp),
'otp_expires_at' => now()->addMinutes(10)->timestamp,
'otp_sent_at' => now()->timestamp,
]);
}
private function pendingRegistrationData(Request $request): ?array
{
$pendingRegistration = $request->session()->get(self::PENDING_REGISTRATION_SESSION_KEY);
return is_array($pendingRegistration) ? $pendingRegistration : null;
}
private function clearPendingRegistration(Request $request): void
{
$request->session()->forget(self::PENDING_REGISTRATION_SESSION_KEY);
}
}