diff --git a/RestroHub-FrontEnd/src/pages/public/Login.jsx b/RestroHub-FrontEnd/src/pages/public/Login.jsx index edf9e2ec..1c9b2e97 100644 --- a/RestroHub-FrontEnd/src/pages/public/Login.jsx +++ b/RestroHub-FrontEnd/src/pages/public/Login.jsx @@ -186,7 +186,7 @@ const Login = () => { } } catch (err) { toast.error( - err.response?.data?.message || "Invalid username or password" + err.response?.data?.message || "Invalid email or password" ); } finally { setIsLoading(false); @@ -327,7 +327,7 @@ const handleGoogleLogin = async (credentialResponse) => { value={formik.values.username} onChange={formik.handleChange} onBlur={formik.handleBlur} - className={inputClass("username")} + className={inputClass("email")} /> diff --git a/RestroHub-FrontEnd/src/services/common/api.js b/RestroHub-FrontEnd/src/services/common/api.js index e51eff02..d8bfac39 100644 --- a/RestroHub-FrontEnd/src/services/common/api.js +++ b/RestroHub-FrontEnd/src/services/common/api.js @@ -1,8 +1,7 @@ import axios from "axios"; const api = axios.create({ - baseURL: - import.meta.env.VITE_API_BASE_URL || "http://localhost:8181/restroly", + baseURL: import.meta.env.VITE_API_BASE_URL || "http://localhost:8181/restroly", }); // Add interceptor diff --git a/RestroHub-FrontEnd/src/services/public/ApiService.js b/RestroHub-FrontEnd/src/services/public/ApiService.js index a09f061e..09787c45 100644 --- a/RestroHub-FrontEnd/src/services/public/ApiService.js +++ b/RestroHub-FrontEnd/src/services/public/ApiService.js @@ -3,7 +3,7 @@ // Handles all API calls for the application // ============================================ -const API_BASE_URL = 'http://localhost:8181/restroly'; +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8181/restroly'; // Helper function for making API requests const apiRequest = async (endpoint, options = {}) => { diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java index 2000c370..70b562c8 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java @@ -81,7 +81,7 @@ public ResponseEntity> register( "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "tokenType": "Bearer", "expiresIn": 86400, - "username": "admin", + "email": "admin@restroly.com", "roles": ["ROLE_ADMIN"] }, "timestamp": "2024-01-15T10:30:00" @@ -107,7 +107,7 @@ public ResponseEntity> register( { "status": 401, "error": "UNAUTHORIZED", - "message": "Invalid username or password", + "message": "Invalid email or password", "path": "/api/v1/auth/login", "timestamp": "2024-01-15T10:30:00", "traceId": "abc123" @@ -124,7 +124,7 @@ public ResponseEntity> register( schema = @Schema(implementation = LoginRequest.class), examples = @ExampleObject(value = """ { - "username": "admin", + "email": "admin@restroly.com", "password": "admin123" } """) @@ -133,7 +133,7 @@ public ResponseEntity> register( public ResponseEntity> login( @Valid @RequestBody LoginRequest loginRequest) { - log.info("Login request received for user: {}", loginRequest.getUsername()); + log.info("Login request received for user: {}", loginRequest.getEmail()); AuthResponse authResponse = authService.login(loginRequest); diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java index cc13d913..aa77b5db 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java @@ -28,8 +28,8 @@ public class AuthResponse { @Schema(description = "Token expiration time in seconds", example = "86400") private Long expiresIn; - @Schema(description = "Username of authenticated user", example = "admin") - private String username; + @Schema(description = "Email of authenticated user", example = "admin@restroly.com") + private String email; @Schema(description = "User roles", example = "[\"ROLE_ADMIN\"]") private List roles; diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/LoginRequest.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/LoginRequest.java index f5f902c7..a4c52f38 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/LoginRequest.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/LoginRequest.java @@ -12,10 +12,10 @@ @Schema(description = "Login request payload") public class LoginRequest { - @NotBlank(message = "Username is required") - @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters") - @Schema(description = "Username", example = "admin", requiredMode = Schema.RequiredMode.REQUIRED) - private String username; + @NotBlank(message = "Email is required") + @jakarta.validation.constraints.Email(message = "Invalid email format") + @Schema(description = "Email", example = "admin@restroly.com", requiredMode = Schema.RequiredMode.REQUIRED) + private String email; @NotBlank(message = "Password is required") @Size(min = 6, max = 100, message = "Password must be between 6 and 100 characters") diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java index b84736aa..37fa9702 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java @@ -44,12 +44,12 @@ public class AuthServiceImpl implements AuthService { @Override public AuthResponse login(LoginRequest loginRequest) { - log.info("Login attempt for user: {}", loginRequest.getUsername()); + log.info("Login attempt for user: {}", loginRequest.getEmail()); try { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( - loginRequest.getUsername(), + loginRequest.getEmail(), loginRequest.getPassword() ) ); @@ -64,22 +64,22 @@ public AuthResponse login(LoginRequest loginRequest) { .map(GrantedAuthority::getAuthority) .collect(Collectors.toList()); - log.info("User {} logged in successfully", loginRequest.getUsername()); + log.info("User {} logged in successfully", loginRequest.getEmail()); return AuthResponse.builder() .accessToken(accessToken) .refreshToken(refreshToken) .tokenType("Bearer") .expiresIn(jwtTokenProvider.getExpirationInSeconds()) - .username(userDetails.getUsername()) + .email(userDetails.getUsername()) .roles(roles) .build(); } catch (BadCredentialsException ex) { - log.warn("Failed login attempt for user: {}", loginRequest.getUsername()); + log.warn("Failed login attempt for user: {}", loginRequest.getEmail()); throw new BadCredentialsException("Invalid username or password"); } catch (AuthenticationException ex) { - log.error("Authentication error for user {}: {}", loginRequest.getUsername(), ex.getMessage()); + log.error("Authentication error for user {}: {}", loginRequest.getEmail(), ex.getMessage()); throw new BusinessException("Authentication failed: " + ex.getMessage()); } } @@ -116,7 +116,7 @@ public AuthResponse refreshToken(RefreshTokenRequest refreshTokenRequest) { .refreshToken(newRefreshToken) .tokenType("Bearer") .expiresIn(jwtTokenProvider.getExpirationInSeconds()) - .username(username) + .email(username) .roles(roles) .build(); } diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/config/DataInitializer.java b/RestroHub/src/main/java/com/restroly/qrmenu/config/DataInitializer.java new file mode 100644 index 00000000..26054886 --- /dev/null +++ b/RestroHub/src/main/java/com/restroly/qrmenu/config/DataInitializer.java @@ -0,0 +1,69 @@ +package com.restroly.qrmenu.config; + +import com.restroly.qrmenu.user.entity.Role; +import com.restroly.qrmenu.user.entity.User; +import com.restroly.qrmenu.user.repository.RoleRepository; +import com.restroly.qrmenu.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; + +@Component +@RequiredArgsConstructor +@Slf4j +public class DataInitializer implements CommandLineRunner { + + private final UserRepository userRepository; + private final RoleRepository roleRepository; + private final PasswordEncoder passwordEncoder; + + @Override + @Transactional + public void run(String... args) { + log.info("Initializing default roles and users..."); + + // Initialize Roles + Role adminRole = createRoleIfNotFound("ROLE_ADMIN", "Administrator role"); + Role userRole = createRoleIfNotFound("ROLE_USER", "Standard user role"); + Role ownerRole = createRoleIfNotFound("ROLE_RESTAURANT_OWNER", "Restaurant owner role"); + + // Initialize Users + createUserIfNotFound("admin@restroly.com", "admin123", "System Admin", Arrays.asList(adminRole, userRole)); + createUserIfNotFound("owner@restroly.com", "owner123", "Restaurant Owner", Arrays.asList(ownerRole, userRole)); + createUserIfNotFound("user@restroly.com", "user123", "Normal User", Arrays.asList(userRole)); + + log.info("Data initialization completed."); + } + + private Role createRoleIfNotFound(String name, String description) { + return roleRepository.findByName(name) + .orElseGet(() -> { + Role role = Role.builder() + .name(name) + .description(description) + .isActive(true) + .build(); + return roleRepository.save(role); + }); + } + + private void createUserIfNotFound(String email, String password, String name, List roles) { + if (!userRepository.existsByEmail(email)) { + User user = User.builder() + .email(email) + .password(passwordEncoder.encode(password)) + .name(name) + .roles(roles) + .isActive(true) + .build(); + userRepository.save(user); + log.info("Created default user: {}", email); + } + } +} diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/security/CustomUserDetailsService.java b/RestroHub/src/main/java/com/restroly/qrmenu/security/CustomUserDetailsService.java index 0eae83ac..aac59a52 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/security/CustomUserDetailsService.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/security/CustomUserDetailsService.java @@ -1,5 +1,6 @@ package com.restroly.qrmenu.security; +import com.restroly.qrmenu.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -8,9 +9,9 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; + import com.restroly.qrmenu.security.exception.UserDisabledException; import com.restroly.qrmenu.security.exception.UserLockedException; - import java.util.List; import java.util.stream.Collectors; @@ -19,8 +20,9 @@ @RequiredArgsConstructor public class CustomUserDetailsService implements UserDetailsService { + private final UserRepository userRepository; private final com.restroly.qrmenu.user.repository.UserRepository userRepository; - + @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { @@ -31,6 +33,7 @@ public UserDetails loadUserByUsername(String email) log.warn("User not found with email: {}", email); return new UsernameNotFoundException("User not found with email: " + email); }); + // Check if the user account is active if (!user.isActive()) { log.warn("User account is inactive: {}", email); @@ -42,7 +45,6 @@ public UserDetails loadUserByUsername(String email) log.warn("User account is locked: {}", email); throw new UserLockedException("User account is locked"); } - List authorities = user.getRoles().stream() .map(role -> new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toList()); @@ -52,9 +54,9 @@ public UserDetails loadUserByUsername(String email) return org.springframework.security.core.userdetails.User.builder() .username(user.getEmail()) .password(user.getPassword()) + .authorities(authorities) .disabled(!user.isActive()) .accountLocked(user.isLocked()) - .authorities(authorities) .build(); } }