added add user function with verify
All checks were successful
Build & Push Docker Image / docker (push) Successful in 1m49s

This commit is contained in:
Ahmed Darrazi 2025-12-18 20:38:09 +01:00
parent 6fba63daf5
commit ed003be546
14 changed files with 57 additions and 36 deletions

View File

@ -4,39 +4,42 @@ namespace App\Http\Controllers\Auth;
use App\Enums\UserType; use App\Enums\UserType;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Auth\Events\Verified; use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest; use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
class VerifyEmailController extends Controller class VerifyEmailController extends Controller
{ {
/** /**
* Mark the authenticated user's email address as verified. * Mark the user's email address as verified via signed link.
*/ */
public function __invoke(EmailVerificationRequest $request): RedirectResponse public function __invoke(Request $request): RedirectResponse
{ {
$adminDashboard = route('dashboard', absolute: false) . '?verified=1'; $user = User::find($request->route('id'));
$studentDashboard = route('student.index', ['tab' => 'courses'], absolute: false) . '?verified=1';
if ($request->user()->hasVerifiedEmail()) { if (!$user) {
if ($request->user()->role === UserType::STUDENT->value) { abort(404);
return redirect()->intended($studentDashboard);
} else {
return redirect()->intended($adminDashboard);
}
} }
if ($request->user()->markEmailAsVerified()) { if (!hash_equals(sha1($user->getEmailForVerification()), (string) $request->route('hash'))) {
/** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */ abort(403);
$user = $request->user(); }
if (!$user->hasVerifiedEmail()) {
$user->markEmailAsVerified();
event(new Verified($user)); event(new Verified($user));
} }
if ($request->user()->role === UserType::STUDENT->value) { if (Auth::check()) {
return redirect()->intended($studentDashboard); $redirect = Auth::user()->role === UserType::STUDENT->value
} else { ? route('student.index', ['tab' => 'courses'], absolute: false)
return redirect()->intended($adminDashboard); : route('dashboard', absolute: false);
return redirect()->intended($redirect . '?verified=1');
} }
return redirect()->route('login')->with('status', __('auth.email_verified_successfully'));
} }
} }

View File

@ -4,12 +4,11 @@ namespace App\Services;
use App\Enums\UserType; use App\Enums\UserType;
use App\Models\User; use App\Models\User;
use App\Notifications\ResetPasswordNotification; use App\Notifications\VerifyEmailNotification;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str; use Illuminate\Support\Str;
class UserService class UserService
@ -51,10 +50,8 @@ class UserService
'password' => Hash::make(Str::random(32)), 'password' => Hash::make(Str::random(32)),
]); ]);
$token = Password::createToken($user); DB::afterCommit(function () use ($user) {
$user->notify(new VerifyEmailNotification());
DB::afterCommit(function () use ($user, $token) {
$user->notify(new ResetPasswordNotification($token));
}); });
return $user; return $user;

View File

@ -1,6 +1,7 @@
import DeleteModal from '@/components/inertia/delete-modal'; import DeleteModal from '@/components/inertia/delete-modal';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { ColumnDef } from '@tanstack/react-table'; import { ColumnDef } from '@tanstack/react-table';
import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react'; import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react';
import EditForm from './edit-form'; import EditForm from './edit-form';
@ -44,6 +45,15 @@ const TableColumn = (translate: LanguageTranslations): ColumnDef<User>[] => {
</div> </div>
), ),
}, },
{
accessorKey: 'email_verified_at',
header: table.verified,
cell: ({ row }) => (
<Badge variant={row.original.email_verified_at ? 'default' : 'secondary'}>
{row.original.email_verified_at ? common.verified : common.not_verified}
</Badge>
),
},
{ {
accessorKey: 'role', accessorKey: 'role',
header: table.role, header: table.role,

View File

@ -41,6 +41,8 @@ interface CommonLang {
off: string; off: string;
free: string; free: string;
completed: string; completed: string;
verified: string;
not_verified: string;
// Common Actions & Account Management // Common Actions & Account Management
search: string; search: string;

View File

@ -36,6 +36,7 @@ interface TableLang {
category_child: string; category_child: string;
meta_description: string; meta_description: string;
meta_keywords: string; meta_keywords: string;
verified: string;
// Table Action Labels // Table Action Labels
action: string; action: string;

View File

@ -25,6 +25,10 @@ Route::post('password-reset', [NewPasswordController::class, 'store'])
->name('password.store') ->name('password.store')
->middleware('checkSmtp'); ->middleware('checkSmtp');
Route::get('verify-email/{id}/{hash}', VerifyEmailController::class)
->middleware(['signed', 'throttle:6,1'])
->name('verification.verify');
Route::middleware(['guest', 'authConfig'])->group(function () { Route::middleware(['guest', 'authConfig'])->group(function () {
Route::get('register', [RegisteredUserController::class, 'create']) Route::get('register', [RegisteredUserController::class, 'create'])
->name('register'); ->name('register');
@ -44,10 +48,6 @@ Route::middleware('auth')->group(function () {
Route::get('verify-email', EmailVerificationPromptController::class) Route::get('verify-email', EmailVerificationPromptController::class)
->name('verification.notice'); ->name('verification.notice');
Route::get('verify-email/{id}/{hash}', VerifyEmailController::class)
->middleware(['signed', 'throttle:6,1'])
->name('verification.verify');
Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
->middleware(['checkSmtp', 'throttle:6,1']) ->middleware(['checkSmtp', 'throttle:6,1'])
->name('verification.send'); ->name('verification.send');

View File

@ -53,4 +53,5 @@ return [
'verify_title' => 'Verify email', 'verify_title' => 'Verify email',
'verify_description' => 'Please verify your email address by clicking on the link we just emailed to you.', 'verify_description' => 'Please verify your email address by clicking on the link we just emailed to you.',
'verification_sent' => 'A new verification link has been sent to the email address you provided during registration.', 'verification_sent' => 'A new verification link has been sent to the email address you provided during registration.',
'email_verified_successfully' => 'Your email has been verified. You can now sign in.',
]; ];

View File

@ -55,6 +55,8 @@ return [
'off' => 'Off', 'off' => 'Off',
'free' => 'Free', 'free' => 'Free',
'completed' => 'Completed', 'completed' => 'Completed',
'verified' => 'Verified',
'not_verified' => 'Not Verified',
// Common Actions & Account Management // Common Actions & Account Management
'search' => 'Search', 'search' => 'Search',

View File

@ -251,8 +251,8 @@ return [
'update_user' => 'Update User', 'update_user' => 'Update User',
'select_approval_status' => 'Select the approval status', 'select_approval_status' => 'Select the approval status',
'invite_user' => 'Invite User', 'invite_user' => 'Invite User',
'invite_user_description' => 'Add a user and email them a password reset link to join.', 'invite_user_description' => 'Add a user and email them a verification link.',
'user_invited' => 'User invited successfully. A password reset link has been sent.', 'user_invited' => 'User invited successfully. A verification email has been sent.',
// Course Progress // Course Progress
'course_progress' => 'Course Progress', 'course_progress' => 'Course Progress',

View File

@ -48,6 +48,7 @@ return [
'category_child' => 'Category Child', 'category_child' => 'Category Child',
'meta_description' => 'Meta Description', 'meta_description' => 'Meta Description',
'meta_keywords' => 'Meta Keywords', 'meta_keywords' => 'Meta Keywords',
'verified' => 'Verified',
// Table Action Labels // Table Action Labels
'action' => 'Action', 'action' => 'Action',

View File

@ -92,6 +92,7 @@ return [
'verify_title' => 'Verify email', 'verify_title' => 'Verify email',
'verify_description' => 'Please verify your email address by clicking on the link we just emailed to you.', 'verify_description' => 'Please verify your email address by clicking on the link we just emailed to you.',
'verification_sent' => 'A new verification link has been sent to the email address you provided during registration.', 'verification_sent' => 'A new verification link has been sent to the email address you provided during registration.',
'email_verified_successfully' => 'Your email has been verified. You can now sign in.',
] ]
], ],
]; ];

View File

@ -66,6 +66,8 @@ return [
'off' => 'Off', 'off' => 'Off',
'free' => 'Free', 'free' => 'Free',
'completed' => 'Completed', 'completed' => 'Completed',
'verified' => 'Verified',
'not_verified' => 'Not Verified',
] ]
], ],

View File

@ -394,8 +394,8 @@ return [
'update_user' => 'Update User', 'update_user' => 'Update User',
'select_approval_status' => 'Select the approval status', 'select_approval_status' => 'Select the approval status',
'invite_user' => 'Invite User', 'invite_user' => 'Invite User',
'invite_user_description' => 'Add a user and email them a password reset link to join.', 'invite_user_description' => 'Add a user and email them a verification link.',
'user_invited' => 'User invited successfully. A password reset link has been sent.', 'user_invited' => 'User invited successfully. A verification email has been sent.',
] ]
], ],

View File

@ -58,6 +58,7 @@ return [
'category_child' => 'Category Child', 'category_child' => 'Category Child',
'meta_description' => 'Meta Description', 'meta_description' => 'Meta Description',
'meta_keywords' => 'Meta Keywords', 'meta_keywords' => 'Meta Keywords',
'verified' => 'Verified',
] ]
], ],