validate([ 'name' => ['required', 'string', 'min:3', 'max:255'], 'email' => ['required', 'email', 'max:255', 'unique:users,email'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); $user = User::query()->create([ 'name' => $validated['name'], 'email' => $validated['email'], 'password' => Hash::make($validated['password']), 'role' => 'user', ]); return response()->json([ 'success' => true, 'message' => 'Success', 'data' => [ 'user' => $this->userPayload($user), ], ], 201); } public function login(Request $request): JsonResponse { $validated = $request->validate([ 'email' => ['required', 'email'], 'password' => ['required', 'string'], ]); $user = User::query()->where('email', $validated['email'])->first(); if (! $user || ! Hash::check($validated['password'], $user->password)) { throw ValidationException::withMessages([ 'email' => ['The provided credentials are incorrect.'], ]); } $token = PersonalAccessToken::issueFor($user); return response()->json([ 'success' => true, 'message' => 'Success', 'data' => [ 'token_type' => 'Bearer', 'token' => $token, 'user' => $this->userPayload($user), ], ]); } public function profile(Request $request): JsonResponse { return response()->json([ 'success' => true, 'message' => 'Success', 'data' => [ 'user' => $this->userPayload($request->user()), ], ]); } public function logout(Request $request): JsonResponse { $request->attributes->get('access_token')?->delete(); return response()->json([ 'success' => true, 'message' => 'Success', ]); } public function forgotPassword(Request $request): JsonResponse { $validated = $request->validate([ 'email' => ['required', 'email', 'exists:users,email'], ]); $plainTextToken = Str::random(64); DB::table('password_reset_tokens')->updateOrInsert( ['email' => $validated['email']], [ 'token' => Hash::make($plainTextToken), 'created_at' => now(), ], ); Log::info('Password reset token generated for Explore Lumajang.', [ 'email' => $validated['email'], 'token' => $plainTextToken, ]); return response()->json([ 'success' => true, 'message' => 'Success', 'data' => [ 'reset_token' => $plainTextToken, ], ]); } public function resetPassword(Request $request): JsonResponse { $validated = $request->validate([ 'email' => ['required', 'email', 'exists:users,email'], 'token' => ['required', 'string'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); $resetToken = DB::table('password_reset_tokens') ->where('email', $validated['email']) ->first(); if (! $resetToken || ! Hash::check($validated['token'], $resetToken->token)) { throw ValidationException::withMessages([ 'token' => ['Token reset password tidak valid.'], ]); } if ($resetToken->created_at && Carbon::parse($resetToken->created_at)->lt(now()->subMinutes(60))) { throw ValidationException::withMessages([ 'token' => ['Token reset password sudah kedaluwarsa.'], ]); } User::query() ->where('email', $validated['email']) ->update([ 'password' => Hash::make($validated['password']), 'remember_token' => Str::random(60), ]); DB::table('password_reset_tokens') ->where('email', $validated['email']) ->delete(); return response()->json([ 'success' => true, 'message' => 'Password berhasil diubah.', ]); } /** * @return array{id:int,name:string,email:string,created_at:string|null} */ private function userPayload(User $user): array { return [ 'id' => $user->id, 'name' => $user->name, 'email' => $user->email, 'role' => $user->role, 'created_at' => $user->created_at?->toISOString(), ]; } }