LearnMood/app/Http/Controllers/Auth/OtpPasswordController.php

205 lines
7.1 KiB
PHP

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Models\User;
use App\Models\PasswordOtp;
use Carbon\Carbon;
use Illuminate\Support\Facades\Hash;
class OtpPasswordController extends Controller
{
/**
* Tampilkan form request OTP
*/
public function showRequestForm()
{
return view('auth.forgot-password-otp');
}
/**
* Kirim OTP ke email
*/
public function sendOtp(Request $request)
{
$request->validate([
'email' => 'required|email|exists:users,email',
], [
'email.exists' => 'Email tidak terdaftar dalam sistem.'
]);
$user = User::where('email', $request->email)->first();
// Generate OTP 6 digit
$otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
// Simpan OTP ke database (expired 5 menit)
PasswordOtp::updateOrCreate(
['email' => $request->email],
[
'otp' => $otp,
'expires_at' => Carbon::now()->addMinutes(5),
]
);
// Kirim email OTP
try {
$this->sendOtpEmail($user->email, $user->name, $otp);
return redirect()->route('password.otp.verify.form', ['email' => $request->email])
->with('success', 'Kode OTP telah dikirim ke email Anda. Cek inbox/spam folder.');
} catch (\Exception $e) {
// Log error
\Log::error('OTP Email error: ' . $e->getMessage());
// Untuk development, tampilkan OTP di session
if (app()->environment('local')) {
return redirect()->route('password.otp.verify.form', ['email' => $request->email])
->with('dev_otp', $otp)
->with('success', '🔧 Mode Development - Kode OTP Anda:');
}
return back()->with('error', 'Gagal mengirim email. Silakan coba lagi.');
}
}
/**
* Tampilkan form verifikasi OTP
*/
public function showVerifyForm(Request $request)
{
return view('auth.verify-otp', ['email' => $request->email]);
}
/**
* Verifikasi OTP
*/
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|string|size:6',
]);
$otpRecord = PasswordOtp::where('email', $request->email)
->where('otp', $request->otp)
->first();
if (!$otpRecord) {
return back()->with('error', 'Kode OTP tidak valid.');
}
// Cek apakah OTP sudah expired
if (Carbon::now()->gt($otpRecord->expires_at)) {
$otpRecord->delete();
return back()->with('error', 'Kode OTP sudah kadaluarsa. Silakan minta kode baru.');
}
// OTP valid, arahkan ke form reset password
return redirect()->route('password.otp.reset.form', [
'email' => $request->email,
'otp' => $request->otp
])->with('success', 'Kode valid. Silakan buat password baru.');
}
/**
* Tampilkan form reset password
*/
public function showResetForm(Request $request)
{
return view('auth.reset-password-otp', [
'email' => $request->email,
'otp' => $request->otp
]);
}
/**
* Reset password dengan OTP
*/
public function resetPassword(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|string|size:6',
'password' => 'required|string|min:8|confirmed',
]);
// Verifikasi OTP sekali lagi
$otpRecord = PasswordOtp::where('email', $request->email)
->where('otp', $request->otp)
->first();
if (!$otpRecord || Carbon::now()->gt($otpRecord->expires_at)) {
return back()->with('error', 'Kode OTP tidak valid atau sudah kadaluarsa.');
}
// Update password user
$user = User::where('email', $request->email)->first();
$user->password = Hash::make($request->password);
$user->save();
// Hapus data OTP
$otpRecord->delete();
return redirect()->route('login')
->with('success', 'Password berhasil direset. Silakan login dengan password baru.');
}
/**
* Kirim email OTP
*/
private function sendOtpEmail($toEmail, $toName, $otp)
{
$subject = "Kode OTP Reset Password - LearnMood";
$message = "
<html>
<head>
<title>Kode OTP Reset Password</title>
</head>
<body style='font-family: Arial, sans-serif; background-color: #f4f4f4; padding: 20px;'>
<div style='max-width: 500px; margin: 0 auto; background-color: white; border-radius: 10px; padding: 30px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);'>
<div style='text-align: center; margin-bottom: 25px;'>
<h1 style='color: #4A90E2; margin: 0;'>Learn<span style='color: #333;'>Mood</span></h1>
</div>
<p style='font-size: 16px; color: #333;'>Halo <strong>{$toName}</strong>,</p>
<p style='font-size: 14px; color: #666;'>Kami menerima permintaan reset password untuk akun Anda. Gunakan kode OTP berikut untuk melanjutkan:</p>
<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; text-align: center; border-radius: 8px; margin: 25px 0;'>
<p style='margin: 0; font-size: 14px; color: rgba(255,255,255,0.9);'>Kode Verifikasi OTP</p>
<h1 style='font-size: 48px; letter-spacing: 8px; color: white; margin: 10px 0; font-family: monospace;'>{$otp}</h1>
</div>
<div style='background-color: #f8f9fa; padding: 15px; border-radius: 8px; margin: 20px 0;'>
<p style='margin: 0; font-size: 13px; color: #666;'>
<strong>⏰ Berlaku selama:</strong> 5 menit<br>
<strong>📧 Email:</strong> {$toEmail}
</p>
</div>
<p style='font-size: 13px; color: #999; border-top: 1px solid #eee; padding-top: 20px; margin-top: 20px;'>
Jika Anda tidak meminta reset password, abaikan email ini.
</p>
<p style='font-size: 12px; color: #999; text-align: center; margin-top: 20px;'>
&copy; " . date('Y') . " LearnMood. All rights reserved.
</p>
</div>
</body>
</html>
";
$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";
$headers .= 'From: LearnMood <' . env('MAIL_FROM_ADDRESS') . '>' . "\r\n";
$headers .= 'Reply-To: ' . env('MAIL_FROM_ADDRESS') . "\r\n";
$headers .= 'X-Mailer: PHP/' . phpversion();
mail($toEmail, $subject, $message, $headers);
}
}