diff --git a/app/Http/Controllers/BreathingExerciseController.php b/app/Http/Controllers/BreathingExerciseController.php index eed8d7f..aa3dea2 100644 --- a/app/Http/Controllers/BreathingExerciseController.php +++ b/app/Http/Controllers/BreathingExerciseController.php @@ -118,18 +118,5 @@ public function completeSession(Request $request) return response()->json(['message' => 'Session completed', 'session' => $session]); } - public function profile() - { - $user = auth()->user(); - $templates = BreathingTemplate::where('user_id', $user->id)->get(); - $sessions = UserBreathingSession::with('template')->where('user_id', $user->id)->get(); - - return response()->json([ - 'email' => $user->email, - 'name' => $user->first_name . ' ' . $user->last_name, - 'xp' => $user->xp, - 'templates' => $templates, - 'sessions' => $sessions - ]); - } + } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 0fc6464..181de5c 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -17,6 +17,9 @@ use Illuminate\Validation\Rule; use Laravel\Socialite\Facades\Socialite; use Illuminate\Support\Facades\Http; +use App\Models\BreathingExercise; +use App\Models\BreathingTemplate; +use App\Models\UserBreathingSession; class UserController extends Controller { public function login(Request $request) @@ -72,28 +75,70 @@ public function register(Request $request) 'mobile' => ['string', new MobileNumber, 'nullable', 'unique:users', 'required_without:email'], ]); - if(isset($data['mobile'])){ - $data['mobile'] = MobileNumberHelper::formatMobile($data['mobile']); - } + if(isset($data['mobile'])){ + $data['mobile'] = MobileNumberHelper::formatMobile($data['mobile']); + } if (!isset($data['password'])) { - $data['password'] = Hash::make(config('app.default_password')); // Hash default password + $data['password'] = Hash::make(config('app.default_password')); } else { - $data['password'] = Hash::make($data['password']); // Hash provided password + $data['password'] = Hash::make($data['password']); } - // return response()->json([ - // "salam" - // ]); + $user = User::create($data); - $token = $user->createToken('token', ['user'])->plainTextToken; + // Generate verification code + $code = rand(100000, 999999); + + // Save verification record + \DB::table('email_verifications')->insert([ + 'user_id' => $user->id, + 'code' => $code, + 'expires_at' => now()->addMinutes(15), + 'created_at' => now(), + 'updated_at' => now(), + ]); + + // Send verification email + \Mail::to($user->email)->send(new \App\Mail\VerifyEmailCodeMail($code)); return response()->json([ + 'message' => 'User registered successfully. Verification code sent to email.', 'user' => $user, - 'token' => $token ]); } +public function verifyEmailCode(Request $request) +{ + $request->validate([ + 'email' => 'required|email', + 'code' => 'required|string', + ]); + + $user = User::where('email', $request->email)->first(); + if (!$user) { + return response()->json(['message' => 'User not found'], 404); + } + + $record = \DB::table('email_verifications') + ->where('user_id', $user->id) + ->where('code', $request->code) + ->where('expires_at', '>', now()) + ->first(); + + if (!$record) { + return response()->json(['message' => 'Invalid or expired code'], 400); + } + + // Mark email as verified + $user->update(['email_verified_at' => now()]); + + // Delete verification record + \DB::table('email_verifications')->where('id', $record->id)->delete(); + + return response()->json(['message' => 'Email verified successfully']); +} + public function loginOTP(Request $request) { $data = $request->validate([ @@ -225,6 +270,24 @@ public function updateProfile(Request $request) 'message' => 'user updated' ]); } + + public function profile() + { + $user = auth()->user(); + $templates = BreathingTemplate::where('user_id', $user->id)->get(); + $sessions = UserBreathingSession::with('template')->where('user_id', $user->id)->get(); + + return response()->json([ + 'email' => $user->email, + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'mobile'=> $user->mobile, + 'birthday' => $user ->birthday, + 'xp' => $user->xp, + 'templates' => $templates, + 'sessions' => $sessions + ]); + } public function status(Request $request) { $data = $request->validate([ diff --git a/app/Mail/VerifyEmailCodeMail.php b/app/Mail/VerifyEmailCodeMail.php new file mode 100644 index 0000000..714a4dc --- /dev/null +++ b/app/Mail/VerifyEmailCodeMail.php @@ -0,0 +1,29 @@ +code = $code; + } + + public function build() + { + return $this->subject('Your Email Verification Code') + ->view('emails.verify-code') + ->with(['code' => $this->code]); + } +} \ No newline at end of file diff --git a/database/migrations/2025_08_27_033553_create_email_verifications_table.php b/database/migrations/2025_08_27_033553_create_email_verifications_table.php new file mode 100644 index 0000000..dce36b5 --- /dev/null +++ b/database/migrations/2025_08_27_033553_create_email_verifications_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->string('code'); + $table->timestamp('expires_at'); + $table->timestamps(); + }); + } + + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('email_verifications'); + } +}; diff --git a/database/migrations/2025_08_27_043929_add_email_verified_at_to_users_table.php b/database/migrations/2025_08_27_043929_add_email_verified_at_to_users_table.php new file mode 100644 index 0000000..e95d90a --- /dev/null +++ b/database/migrations/2025_08_27_043929_add_email_verified_at_to_users_table.php @@ -0,0 +1,25 @@ +timestamp('email_verified_at')->nullable()->after('email'); + }); +} + +public function down() +{ + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('email_verified_at'); + }); +} +}; diff --git a/database/migrations/2025_08_27_085130_create_user_mood.php b/database/migrations/2025_08_27_085130_create_user_mood.php new file mode 100644 index 0000000..c783cc3 --- /dev/null +++ b/database/migrations/2025_08_27_085130_create_user_mood.php @@ -0,0 +1,27 @@ +id(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_mood'); + } +}; diff --git a/resources/views/emails/verify-code.blade.php b/resources/views/emails/verify-code.blade.php new file mode 100644 index 0000000..96fad4a --- /dev/null +++ b/resources/views/emails/verify-code.blade.php @@ -0,0 +1,9 @@ + + + +

Your Verification Code

+

Use the code below to verify your email:

+

{{ $code }}

+

This code will expire in 15 minutes.

+ + diff --git a/routes/api.php b/routes/api.php index d3f99ee..04125a4 100644 --- a/routes/api.php +++ b/routes/api.php @@ -12,7 +12,7 @@ Route::prefix('auth')->controller(UserController::class)->group(function () { Route::middleware(['throttle:3,1'])->post('login-otp', 'loginOTP'); Route::post('check-otp', 'checkOTP'); - + Route::post('verify-email-code','verifyEmailCode'); Route::post('login', 'login'); Route::post('register', 'register'); @@ -30,21 +30,29 @@ Route::middleware('abilities:admin')->post('/{name}', 'update'); Route::put('/{name}/try', [UserController::class, 'try']); - + Route::middleware('abilities:admin')->post('/{name}/products', [ProductController::class, 'store']); Route::middleware('abilities:admin')->patch('/{name}/products/{id}', [ProductController::class, 'update']); Route::middleware('abilities:admin')->delete('/{name}/products/{id}', [ProductController::class, 'delete']); Route::put('/{name}/products/{id}/subscribe', [ProductController::class, 'subscribe']); - }); - Route::post('breathing-exercises', [BreathingExerciseController::class, 'store']); - Route::get('profile', [BreathingExerciseController::class, 'profile']); + + + + }); + // --------- user profile + Route::post('/profile', [UserController::class, 'updateProfile']); + Route::get('/profile', [UserController::class, 'profile']); + + + // Route::post('breathing-exercises', [BreathingExerciseController::class, 'store']); + // Route::get('profile', [BreathingExerciseController::class, 'profile']); // ------------------------------------------------ Route::post('breathing-templates', [BreathingExerciseController::class, 'createTemplate']); Route::get('breathing-templates', [BreathingExerciseController::class, 'getTemplates']); Route::post('breathing-complete', [BreathingExerciseController::class, 'completeSession']); - Route::get('profile', [BreathingExerciseController::class, 'profile']); + // Route::get('profile', [BreathingExerciseController::class, 'profile']); Route::put('breathing-templates/{id}', [BreathingExerciseController::class, 'updateTemplate']); Route::delete('breathing-templates/{id}', [BreathingExerciseController::class, 'deleteTemplate']); Route::get('user-templates', [BreathingExerciseController::class, 'getUserTemplates']);