feat: add verify with link and back to app
This commit is contained in:
@@ -139,44 +139,63 @@ public function login(Request $request)
|
||||
'token' => $token,
|
||||
]);
|
||||
}
|
||||
public function resendEmailVerification(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email',
|
||||
]);
|
||||
public function resendEmailVerification(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'email' => 'required|email',
|
||||
'method' => 'string|in:code,link|nullable',
|
||||
'package_name' => 'string|required',
|
||||
]);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
$method = $data['method'] ?? 'code';
|
||||
$packageNameValue = $data['package_name'];
|
||||
unset($data['package_name']);
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['message' => 'User not found'], 404);
|
||||
$user = User::where('email', $data['email'])->first();
|
||||
// return response()->json(['message' => $user]);
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['message' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
if ($user->email_verified_at) {
|
||||
return response()->json(['message' => 'Email is already verified'], 400);
|
||||
}
|
||||
|
||||
$code = rand(1000, 9999);
|
||||
$token = Str::random(64);
|
||||
|
||||
\DB::table('email_verifications')->where('user_id', $user->id)->delete();
|
||||
|
||||
\DB::table('email_verifications')->insert([
|
||||
'user_id' => $user->id,
|
||||
'code' => $code,
|
||||
'token' => $token,
|
||||
'expires_at' => now()->addMinutes(15),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
try {
|
||||
if ($method === 'code') {
|
||||
\Mail::to($user->email)->send(new \App\Mail\VerifyEmailCodeMail($code));
|
||||
} else {
|
||||
$verificationUrl = url("api/auth/verify-email-link/{$token}?package={$packageNameValue}");
|
||||
\Mail::to($user->email)->send(new \App\Mail\VerifyEmailLinkMail($verificationUrl));
|
||||
}
|
||||
|
||||
if ($user->email_verified_at) {
|
||||
return response()->json(['message' => 'Email is already verified'], 400);
|
||||
}
|
||||
|
||||
// Generate new code
|
||||
$code = rand(1000, 9999);
|
||||
|
||||
// Delete previous codes
|
||||
} catch (\Exception $e) {
|
||||
// rollback verification
|
||||
\DB::table('email_verifications')->where('user_id', $user->id)->delete();
|
||||
|
||||
// Insert new verification code
|
||||
\DB::table('email_verifications')->insert([
|
||||
'user_id' => $user->id,
|
||||
'code' => $code,
|
||||
'expires_at' => now()->addMinutes(15),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
// Send email
|
||||
\Mail::to($user->email)->send(new \App\Mail\VerifyEmailCodeMail($code));
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Verification email resent successfully'
|
||||
]);
|
||||
'error' => $e->getMessage(),
|
||||
'message' => 'ارسال ایمیل با مشکل مواجه شد، لطفا دوباره تلاش کنید'
|
||||
], 400);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Verification email resent successfully']);
|
||||
}
|
||||
|
||||
public function register(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
@@ -186,11 +205,16 @@ public function register(Request $request)
|
||||
'password' => 'string|nullable|required_with:email',
|
||||
'mobile' => ['string', new MobileNumber, 'nullable', 'required_without:email'],
|
||||
'fcm_token' => 'string|nullable',
|
||||
'package_name' => 'string|required'
|
||||
'package_name' => 'string|required',
|
||||
'method' => 'string|in:code,link|nullable'
|
||||
]);
|
||||
|
||||
$packageNameValue = $data['package_name'];
|
||||
unset($data['package_name']);
|
||||
$method = $data['method'] ?? 'code';
|
||||
unset($data['method']);
|
||||
|
||||
// Extract and remove package_name
|
||||
$packageNameValue = $data['package_name'];
|
||||
unset($data['package_name']);
|
||||
|
||||
if (isset($data['mobile'])) {
|
||||
$data['mobile'] = MobileNumberHelper::formatMobile($data['mobile']);
|
||||
@@ -203,21 +227,35 @@ public function register(Request $request)
|
||||
return response()->json(['message' => 'Email already exists'], 400);
|
||||
}
|
||||
|
||||
$code = rand(1000, 9999);
|
||||
$token = Str::random(64);
|
||||
// ✅ Check if email can receive a message before creating the user
|
||||
if (isset($data['email'])) {
|
||||
$code = rand(1000, 9999);
|
||||
// $code = rand(1000, 9999);
|
||||
try {
|
||||
if ($method === 'code') {
|
||||
\Mail::to($data['email'])->send(new \App\Mail\VerifyEmailCodeMail($code));
|
||||
} else {
|
||||
$verificationUrl = url("api/auth/verify-email-link/{$token}?package={$packageNameValue}");
|
||||
\Mail::to($data['email'])->send(new \App\Mail\VerifyEmailLinkMail($verificationUrl));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'error' => $e->getMessage(),
|
||||
'message' => 'لطفا آدرس ایمیل خود را بررسی کنید و مجدد تلاش کنید'
|
||||
], 400);
|
||||
}
|
||||
// try {
|
||||
// // Try sending email
|
||||
// \Mail::to($data['email'])->send(new \App\Mail\VerifyEmailCodeMail($code));
|
||||
// } catch (\Exception $e) {
|
||||
// return response()->json([
|
||||
// 'code' => $e->getCode(),
|
||||
// 'error' => $e->getMessage(),
|
||||
// 'message' => 'لطفا آدرس ایمیل خود را بررسی کنید و مجدد تلاش کنید',
|
||||
|
||||
try {
|
||||
// Try sending email
|
||||
\Mail::to($data['email'])->send(new \App\Mail\VerifyEmailCodeMail($code));
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'code' => $e->getCode(),
|
||||
'error' => $e->getMessage(),
|
||||
'message' => 'لطفا آدرس ایمیل خود را بررسی کنید و مجدد تلاش کنید',
|
||||
|
||||
], 400);
|
||||
}
|
||||
// ], 400);
|
||||
// }
|
||||
}
|
||||
|
||||
// Hash password
|
||||
@@ -237,15 +275,17 @@ public function register(Request $request)
|
||||
}
|
||||
|
||||
// Save email verification code after user is created
|
||||
if (isset($data['email'])) {
|
||||
\DB::table('email_verifications')->insert([
|
||||
'user_id' => $user->id,
|
||||
'code' => $code,
|
||||
// if (isset($data['email'])) {
|
||||
\DB::table('email_verifications')->where('user_id', $user->id)->delete();
|
||||
\DB::table('email_verifications')->insert([
|
||||
'user_id' => $user->id,
|
||||
'code' => $code,
|
||||
'token' => $token,
|
||||
'expires_at' => now()->addMinutes(15),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
]);
|
||||
// }
|
||||
|
||||
// Create token
|
||||
$token = $user->createToken('token', $user->is_admin ? ['admin'] : ['user'])->plainTextToken;
|
||||
@@ -286,6 +326,44 @@ public function verifyEmailCode(Request $request)
|
||||
$token = $user->createToken('token', $user->is_admin ? ['admin'] : ['user'])->plainTextToken;
|
||||
return response()->json(['message' => 'Email verified successfully', 'token' => $token]);
|
||||
}
|
||||
public function verifyEmailLink(Request $request, $token)
|
||||
{
|
||||
$record = \DB::table('email_verifications')
|
||||
->where('token', $token)
|
||||
->where('expires_at', '>', now())
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
return response()->json(['message' => 'Invalid or expired token'], 400);
|
||||
}
|
||||
|
||||
$user = User::find($record->user_id);
|
||||
if (!$user) {
|
||||
return response()->json(['message' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
$user->update(['email_verified_at' => now()]);
|
||||
|
||||
\DB::table('email_verifications')->where('id', $record->id)->delete();
|
||||
|
||||
// Optionally redirect to app deep link
|
||||
$packageName = $request->query('package');
|
||||
if ($packageName && $package = \App\Models\PackageName::where('name', $packageName)->first()) {
|
||||
if ($package->web_app_url) {
|
||||
return redirect()->away($package->web_app_url . '?verified=1');
|
||||
}
|
||||
}
|
||||
|
||||
// $packageName = $request->query('package') ?? 'default';
|
||||
$deepLink = "approagency://{$packageName}?verified=1";
|
||||
|
||||
return view('emails.verified', [
|
||||
'status' => 'success',
|
||||
'message' => 'ایمیل شما با موفقیت تایید شد',
|
||||
'deepLink' => $deepLink,
|
||||
]);
|
||||
}
|
||||
|
||||
public function loginOTP(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class VerifyEmailLinkMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $verificationUrl;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($verificationUrl)
|
||||
{
|
||||
$this->verificationUrl = $verificationUrl;
|
||||
}
|
||||
public function build()
|
||||
{
|
||||
return $this->subject('Verify Your Email Address')
|
||||
->markdown('emails.verify-link')
|
||||
->with([
|
||||
'verificationUrl' => $this->verificationUrl,
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Verify Email Link Mail',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.verify-link',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('email_verifications', function (Blueprint $table) {
|
||||
$table->string('token')->nullable()->after('code')->unique();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('email_verifications', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>ایمیل تایید شد</title>
|
||||
<style>
|
||||
/* Reset default margins/paddings */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Tahoma', sans-serif;
|
||||
direction: rtl;
|
||||
text-align: center;
|
||||
background-color: #f5f6fa;
|
||||
color: #333;
|
||||
padding: 50px 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 40px 30px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: #0288d1;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
color: #555;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 15px 30px;
|
||||
font-size: 18px;
|
||||
background: linear-gradient(135deg, #0288d1, #26c6da);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: linear-gradient(135deg, #026aa7, #00acc1);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.25);
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const deepLink = "{{ $deepLink }}";
|
||||
if(deepLink) {
|
||||
setTimeout(function() {
|
||||
window.location.href = deepLink;
|
||||
}, 3000); // ۳ ثانیه = 3000 میلیثانیه
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>{{ $message }}</h1>
|
||||
|
||||
@if($deepLink)
|
||||
<p>ایمیل شما با موفقیت تایید شد.</p>
|
||||
<a href="{{ $deepLink }}" class="button">بازگشت به اپلیکیشن</a>
|
||||
<p class="info">در حال انتقال خودکار به اپلیکیشن در ۳ ثانیه...</p>
|
||||
@endif
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,85 @@
|
||||
@component('mail::message')
|
||||
# تایید ایمیل شما
|
||||
|
||||
برای تایید آدرس ایمیل خود روی دکمه زیر کلیک کنید:
|
||||
|
||||
@php
|
||||
// Extract package name from query string
|
||||
$parsedUrl = parse_url($verificationUrl);
|
||||
parse_str($parsedUrl['query'] ?? '', $queryParams);
|
||||
$packageName = $queryParams['package'] ?? 'default';
|
||||
|
||||
// Create deep link after verification
|
||||
$deepLink = "approagency://{$packageName}?verified=1";
|
||||
@endphp
|
||||
|
||||
@component('mail::button', ['url' => $verificationUrl, 'style' => 'background-color: #0288d1; padding: 12px 24px; font-size: 18px; border-radius: 5px; text-decoration: none; color: white; text-align: center; display: inline-block;'])
|
||||
تایید ایمیل
|
||||
@endcomponent
|
||||
|
||||
---
|
||||
|
||||
با تشکر،
|
||||
{{ config('app.name') }}
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
/* Body and general email structure */
|
||||
body {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
font-family: 'Tahoma', sans-serif;
|
||||
background-color: #f5f6fa;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 40px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 24px;
|
||||
color: #0288d1;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 16px;
|
||||
color: #555;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
background-color: #0288d1;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
margin: 20px 0;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transition: background-color 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #026aa7;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
@endcomponent
|
||||
@@ -35,6 +35,7 @@
|
||||
Route::middleware(['throttle:3,1', 'auto-ban'])->post('login-otp', 'loginOTP');
|
||||
Route::post('check-otp', 'checkOTP');
|
||||
Route::post('verify-email-code','verifyEmailCode');
|
||||
Route::get('verify-email-link/{token}', [UserController::class, 'verifyEmailLink'])->name('verify.email.link');
|
||||
Route::post('login', 'login');
|
||||
Route::post('register', 'register');
|
||||
Route::post('resend-email-verification', [UserController::class, 'resendEmailVerification']);
|
||||
|
||||
Reference in New Issue
Block a user