- Timestamp:
- 02/09/26 21:36:40 (5 months ago)
- Branches:
- master
- Children:
- 8615885
- Parents:
- 140d098
- Location:
- backend
- Files:
-
- 2 added
- 1 deleted
- 12 edited
-
env (deleted)
-
pom.xml (modified) (1 diff)
-
src/main/java/com/trekr/backend/BackendApplication.java (modified) (3 diffs)
-
src/main/java/com/trekr/backend/config/SecurityConfig.java (modified) (3 diffs)
-
src/main/java/com/trekr/backend/controller/AuthController.java (modified) (3 diffs)
-
src/main/java/com/trekr/backend/dto/auth/AuthResponse.java (modified) (2 diffs)
-
src/main/java/com/trekr/backend/dto/auth/LoginRequest.java (modified) (1 diff)
-
src/main/java/com/trekr/backend/dto/auth/RegisterRequest.java (modified) (1 diff)
-
src/main/java/com/trekr/backend/entity/User.java (modified) (1 diff)
-
src/main/java/com/trekr/backend/resources/application.properties (modified) (1 diff)
-
src/main/java/com/trekr/backend/security/UserPrincipal.java (modified) (2 diffs)
-
src/main/java/com/trekr/backend/service/AuthService.java (modified) (3 diffs)
-
src/main/resources/application.properties (modified) (1 diff)
-
src/test/resources/application.properties (added)
-
src/test/resources/schema.sql (added)
Legend:
- Unmodified
- Added
- Removed
-
backend/pom.xml
r140d098 r3ebe47c 99 99 <scope>test</scope> 100 100 </dependency> 101 102 <dependency> 103 <groupId>com.h2database</groupId> 104 <artifactId>h2</artifactId> 105 <scope>test</scope> 106 </dependency> 101 107 </dependencies> 102 108 -
backend/src/main/java/com/trekr/backend/BackendApplication.java
r140d098 r3ebe47c 12 12 // This ensures system properties are available for ${} placeholder resolution 13 13 Dotenv dotenv = loadEnvironmentVariables(); 14 14 15 15 // Create SpringApplication instance 16 16 SpringApplication app = new SpringApplication(BackendApplication.class); 17 17 18 18 // Set system properties and default properties from .env file 19 19 if (dotenv != null) { 20 20 java.util.Map<String, Object> defaultProps = new java.util.HashMap<>(); 21 21 22 22 dotenv.entries().forEach(entry -> { 23 23 String key = entry.getKey(); … … 30 30 } 31 31 }); 32 32 33 33 // Set all default properties at once 34 34 if (!defaultProps.isEmpty()) { … … 36 36 } 37 37 } 38 38 39 39 app.run(args); 40 40 } 41 41 42 42 private static Dotenv loadEnvironmentVariables() { 43 43 Dotenv dotenv = null; 44 try { 45 // Try current directory (backend/) 46 dotenv = Dotenv.configure() 47 .directory("./") 48 .ignoreIfMissing() 49 .load(); 50 } catch (Exception e) { 51 // Try parent directory if current doesn't work 44 45 // Prefer a standard ".env" file, but also support the existing "env" file. 46 // We search multiple locations so it works whether the app is launched from: 47 // - backend/ (common in IDE) 48 // - repo root (common when running a built JAR) 49 String[][] candidates = new String[][] { 50 { "./", ".env" }, 51 { "./", "env" }, 52 { "./backend", ".env" }, 53 { "./backend", "env" }, 54 { "../", ".env" }, 55 { "../", "env" }, 56 { "../backend", ".env" }, 57 { "../backend", "env" }, 58 }; 59 60 for (String[] candidate : candidates) { 52 61 try { 53 dotenv = Dotenv.configure() 54 .directory("../") 62 Dotenv loaded = Dotenv.configure() 63 .directory(candidate[0]) 64 .filename(candidate[1]) 55 65 .ignoreIfMissing() 56 66 .load(); 57 } catch (Exception e2) { 58 System.err.println("WARNING: Could not load .env file. Make sure .env exists in backend/ directory."); 59 System.err.println("Error: " + e2.getMessage()); 60 return null; 67 if (loaded != null && !loaded.entries().isEmpty()) { 68 dotenv = loaded; 69 break; 70 } 71 } catch (Exception ignored) { 72 // keep searching 61 73 } 62 74 } 63 64 // Verify critical properties are loaded 65 if (dotenv != null) { 66 String url = dotenv.get("SPRING_DATASOURCE_URL"); 67 String username = dotenv.get("SPRING_DATASOURCE_USERNAME"); 68 String password = dotenv.get("SPRING_DATASOURCE_PASSWORD"); 69 70 if (url != null && !url.trim().isEmpty()) { 71 System.out.println("✓ Database URL loaded from .env"); 72 } else { 73 System.err.println("✗ ERROR: SPRING_DATASOURCE_URL not found in .env file!"); 74 } 75 if (username != null && !username.trim().isEmpty()) { 76 System.out.println("✓ Database username loaded from .env"); 77 } else { 78 System.err.println("✗ ERROR: SPRING_DATASOURCE_USERNAME not found in .env file!"); 79 } 80 if (password != null && !password.trim().isEmpty()) { 81 System.out.println("✓ Database password loaded from .env"); 82 } else { 83 System.err.println("✗ ERROR: SPRING_DATASOURCE_PASSWORD not found in .env file!"); 84 } 85 } 86 75 87 76 return dotenv; 88 77 } -
backend/src/main/java/com/trekr/backend/config/SecurityConfig.java
r140d098 r3ebe47c 3 3 import org.springframework.context.annotation.Bean; 4 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.security.config.Customizer; 5 6 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 7 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; … … 8 9 import org.springframework.security.crypto.password.PasswordEncoder; 9 10 import org.springframework.security.web.SecurityFilterChain; 11 import org.springframework.web.cors.CorsConfiguration; 12 import org.springframework.web.cors.CorsConfigurationSource; 13 import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 14 15 import java.util.List; 10 16 11 17 @Configuration … … 17 23 http 18 24 .csrf(AbstractHttpConfigurer::disable) 25 .cors(Customizer.withDefaults()) 19 26 .authorizeHttpRequests(auth -> auth 20 27 .requestMatchers("/api/auth/**").permitAll() 21 .anyRequest().authenticated() 22 ); 28 .anyRequest().authenticated()); 23 29 return http.build(); 30 } 31 32 @Bean 33 public CorsConfigurationSource corsConfigurationSource() { 34 CorsConfiguration config = new CorsConfiguration(); 35 config.setAllowedOrigins(List.of( 36 "http://localhost:5173", 37 "http://127.0.0.1:5173")); 38 config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); 39 config.setAllowedHeaders(List.of("Authorization", "Content-Type")); 40 41 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 42 source.registerCorsConfiguration("/**", config); 43 return source; 24 44 } 25 45 -
backend/src/main/java/com/trekr/backend/controller/AuthController.java
r140d098 r3ebe47c 6 6 import com.trekr.backend.service.AuthService; 7 7 import jakarta.validation.Valid; 8 import lombok.RequiredArgsConstructor;9 8 import org.springframework.http.HttpStatus; 10 9 import org.springframework.http.ResponseEntity; … … 13 12 @RestController 14 13 @RequestMapping("/api/auth") 15 @RequiredArgsConstructor16 14 public class AuthController { 17 15 18 16 private final AuthService authService; 19 17 18 public AuthController(AuthService authService) { 19 this.authService = authService; 20 } 21 20 22 @PostMapping("/register") 21 23 public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request) { … … 23 25 return ResponseEntity.status(HttpStatus.CREATED).body(response); 24 26 } 25 27 26 28 @PostMapping("/login") 27 29 public ResponseEntity<AuthResponse> login(@Valid @RequestBody LoginRequest request) { -
backend/src/main/java/com/trekr/backend/dto/auth/AuthResponse.java
r140d098 r3ebe47c 1 1 package com.trekr.backend.dto.auth; 2 2 3 import lombok.AllArgsConstructor;4 import lombok.Data;5 import lombok.NoArgsConstructor;6 7 @Data8 @NoArgsConstructor9 @AllArgsConstructor10 3 public class AuthResponse { 11 4 private String token; … … 14 7 private String username; 15 8 private String email; 9 10 public AuthResponse() { 11 } 12 13 public AuthResponse(String token, String type, Long userId, String username, String email) { 14 this.token = token; 15 this.type = type; 16 this.userId = userId; 17 this.username = username; 18 this.email = email; 19 } 20 21 public String getToken() { 22 return token; 23 } 24 25 public void setToken(String token) { 26 this.token = token; 27 } 28 29 public String getType() { 30 return type; 31 } 32 33 public void setType(String type) { 34 this.type = type; 35 } 36 37 public Long getUserId() { 38 return userId; 39 } 40 41 public void setUserId(Long userId) { 42 this.userId = userId; 43 } 44 45 public String getUsername() { 46 return username; 47 } 48 49 public void setUsername(String username) { 50 this.username = username; 51 } 52 53 public String getEmail() { 54 return email; 55 } 56 57 public void setEmail(String email) { 58 this.email = email; 59 } 16 60 } -
backend/src/main/java/com/trekr/backend/dto/auth/LoginRequest.java
r140d098 r3ebe47c 2 2 3 3 import jakarta.validation.constraints.NotBlank; 4 import lombok.Data;5 4 6 @Data7 5 public class LoginRequest { 8 6 9 7 @NotBlank(message = "Username or email is required") 10 8 private String usernameOrEmail; 11 9 12 10 @NotBlank(message = "Password is required") 13 11 private String password; 12 13 public LoginRequest() { 14 } 15 16 public String getUsernameOrEmail() { 17 return usernameOrEmail; 18 } 19 20 public void setUsernameOrEmail(String usernameOrEmail) { 21 this.usernameOrEmail = usernameOrEmail; 22 } 23 24 public String getPassword() { 25 return password; 26 } 27 28 public void setPassword(String password) { 29 this.password = password; 30 } 14 31 } -
backend/src/main/java/com/trekr/backend/dto/auth/RegisterRequest.java
r140d098 r3ebe47c 4 4 import jakarta.validation.constraints.NotBlank; 5 5 import jakarta.validation.constraints.Size; 6 import lombok.Data;7 6 8 @Data9 7 public class RegisterRequest { 10 8 11 9 @NotBlank(message = "Email is required") 12 10 @Email(message = "Email must be valid") 13 11 private String email; 14 12 15 13 @NotBlank(message = "Username is required") 16 14 @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters") 17 15 private String username; 18 16 19 17 @NotBlank(message = "Password is required") 20 18 @Size(min = 6, message = "Password must be at least 6 characters") 21 19 private String password; 20 21 public RegisterRequest() { 22 } 23 24 public String getEmail() { 25 return email; 26 } 27 28 public void setEmail(String email) { 29 this.email = email; 30 } 31 32 public String getUsername() { 33 return username; 34 } 35 36 public void setUsername(String username) { 37 this.username = username; 38 } 39 40 public String getPassword() { 41 return password; 42 } 43 44 public void setPassword(String password) { 45 this.password = password; 46 } 22 47 } -
backend/src/main/java/com/trekr/backend/entity/User.java
r140d098 r3ebe47c 2 2 3 3 import jakarta.persistence.*; 4 import lombok.AllArgsConstructor;5 import lombok.Getter;6 import lombok.NoArgsConstructor;7 import lombok.Setter;8 4 9 5 @Entity 10 6 @Table(name = "USERS", schema = "trekr") 11 @Getter12 @Setter13 @NoArgsConstructor14 @AllArgsConstructor15 7 public class User { 16 8 17 9 @Id 18 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq") 19 @SequenceGenerator(name = "user_seq", sequenceName = "user_id_seq", allocationSize = 1) 10 @GeneratedValue(strategy = GenerationType.IDENTITY) 20 11 @Column(name = "user_id") 21 12 private Long userId; 22 13 23 14 @Column(name = "email", nullable = false, unique = true) 24 15 private String email; 25 16 26 17 @Column(name = "username", nullable = false, unique = true) 27 18 private String username; 28 19 29 20 @Column(name = "password", nullable = false) 30 21 private String password; 22 23 public User() { 24 } 25 26 public User(Long userId, String email, String username, String password) { 27 this.userId = userId; 28 this.email = email; 29 this.username = username; 30 this.password = password; 31 } 32 33 public Long getUserId() { 34 return userId; 35 } 36 37 public void setUserId(Long userId) { 38 this.userId = userId; 39 } 40 41 public String getEmail() { 42 return email; 43 } 44 45 public void setEmail(String email) { 46 this.email = email; 47 } 48 49 public String getUsername() { 50 return username; 51 } 52 53 public void setUsername(String username) { 54 this.username = username; 55 } 56 57 public String getPassword() { 58 return password; 59 } 60 61 public void setPassword(String password) { 62 this.password = password; 63 } 31 64 } -
backend/src/main/java/com/trekr/backend/resources/application.properties
r140d098 r3ebe47c 1 # application.properties 1 ### NOTE 2 ### This file is intentionally NOT used by Spring Boot. 3 ### The active config is in: src/main/resources/application.properties 2 4 3 # General app config4 spring.application.name=trekr5 6 #spring.datasource.hikari.data-source-properties.useServerPrepStmts=false7 #spring.datasource.hikari.data-source-properties.prepareThreshold=08 9 logging.level.org.springframework.security=DEBUG10 11 # JPA & Hibernate12 spring.jpa.hibernate.ddl-auto=update13 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect14 spring.jpa.show-sql=false15 spring.jpa.properties.hibernate.format_sql=true16 spring.jpa.properties.hibernate.default_schema=trekr17 18 #logging.level.org.hibernate.SQL=DEBUG19 #logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE20 21 22 # HikariCP settings23 spring.datasource.hikari.pool-name=trekrHikariPool24 25 # Maximum connections in the pool26 # Rule of thumb: (CPU cores × 2) + effective_spindle_count27 # For most apps: 10-20 is good28 spring.datasource.hikari.maximum-pool-size=2029 30 # Minimum idle connections always ready31 # Keep at least 5 ready for quick response32 spring.datasource.hikari.minimum-idle=533 34 # How long to wait for a connection (before blocking the request) - 30sec35 spring.datasource.hikari.connection-timeout=3000036 37 # How long a connection can sit idle (before being closed) - 10min38 spring.datasource.hikari.idle-timeout=60000039 40 # Maximum lifetime of a connection (before being replaced with a new one) - 30min41 # Prevents stale connections42 spring.datasource.hikari.max-lifetime=180000043 44 45 # JWT Configuration (supports JWT_SECRET or JWT_CONFIG_SECRET from .env)46 jwt.secret=${JWT_SECRET:${JWT_CONFIG_SECRET:dev-fallback-secret-min-32-chars-required}}47 48 # Base API path49 api.base.path=/api50 api.admin.path=/api/admin51 52 #spring.profiles.active=dev53 54 # Max file size55 server.tomcat.max-part-count=5056 57 logging.level.org.springframework.web=DEBUG58 logging.level.org.apache.coyote.http11=DEBUG59 60 61 # PostgreSQL connection62 spring.datasource.driver-class-name=org.postgresql.Driver63 spring.datasource.url=${SPRING_DATASOURCE_URL}64 spring.datasource.username=${SPRING_DATASOURCE_USERNAME}65 spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}66 -
backend/src/main/java/com/trekr/backend/security/UserPrincipal.java
r140d098 r3ebe47c 2 2 3 3 import lombok.Getter; 4 import lombok.RequiredArgsConstructor;5 4 6 5 import java.io.Serializable; … … 11 10 */ 12 11 @Getter 13 @RequiredArgsConstructor14 12 public class UserPrincipal implements Serializable { 13 private static final long serialVersionUID = 1L; 14 15 15 private final Long userId; 16 16 private final String username; 17 17 private final String email; 18 19 public UserPrincipal(Long userId, String username, String email) { 20 this.userId = userId; 21 this.username = username; 22 this.email = email; 23 } 18 24 } -
backend/src/main/java/com/trekr/backend/service/AuthService.java
r140d098 r3ebe47c 6 6 import com.trekr.backend.entity.User; 7 7 import com.trekr.backend.repository.UserRepository; 8 import lombok.RequiredArgsConstructor;9 8 import org.springframework.security.crypto.password.PasswordEncoder; 10 9 import org.springframework.stereotype.Service; … … 12 11 13 12 @Service 14 @RequiredArgsConstructor15 13 public class AuthService { 16 14 … … 18 16 private final PasswordEncoder passwordEncoder; 19 17 private final JwtService jwtService; 18 19 public AuthService(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtService jwtService) { 20 this.userRepository = userRepository; 21 this.passwordEncoder = passwordEncoder; 22 this.jwtService = jwtService; 23 } 20 24 21 25 @Transactional -
backend/src/main/resources/application.properties
r140d098 r3ebe47c 1 spring.application.name=backend 1 spring.application.name=trekr 2 3 # ============================ 4 # JPA & Hibernate 5 # ============================ 6 spring.jpa.hibernate.ddl-auto=update 7 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect 8 spring.jpa.show-sql=false 9 spring.jpa.properties.hibernate.format_sql=true 10 spring.jpa.properties.hibernate.default_schema=trekr 11 spring.jpa.properties.hibernate.hbm2ddl.create_namespaces=true 12 13 # ============================ 14 # PostgreSQL connection (loaded from backend/.env or backend/env) 15 # ============================ 16 spring.datasource.driver-class-name=org.postgresql.Driver 17 spring.datasource.url=${SPRING_DATASOURCE_URL} 18 spring.datasource.username=${SPRING_DATASOURCE_USERNAME} 19 spring.datasource.password=${SPRING_DATASOURCE_PASSWORD} 20 21 # ============================ 22 # JWT Configuration (supports JWT_SECRET or JWT_CONFIG_SECRET) 23 # ============================ 24 jwt.secret=${JWT_SECRET:${JWT_CONFIG_SECRET:dev-fallback-secret-min-32-chars-required}} 25 26 # ============================ 27 # Logging 28 # ============================ 29 logging.level.org.springframework.security=DEBUG 30 31 # ============================ 32 # HikariCP 33 # ============================ 34 spring.datasource.hikari.pool-name=trekrHikariPool 35 spring.datasource.hikari.maximum-pool-size=20 36 spring.datasource.hikari.minimum-idle=5 37 spring.datasource.hikari.connection-timeout=30000 38 spring.datasource.hikari.idle-timeout=600000 39 spring.datasource.hikari.max-lifetime=1800000 40 41 # Max multipart parts 42 server.tomcat.max-part-count=50
Note:
See TracChangeset
for help on using the changeset viewer.
