Changeset 3ebe47c for backend


Ignore:
Timestamp:
02/09/26 21:36:40 (5 months ago)
Author:
Andrej <asumanovski@…>
Branches:
master
Children:
8615885
Parents:
140d098
Message:

Made authentication on frontend and backend

Location:
backend
Files:
2 added
1 deleted
12 edited

Legend:

Unmodified
Added
Removed
  • backend/pom.xml

    r140d098 r3ebe47c  
    9999            <scope>test</scope>
    100100        </dependency>
     101
     102        <dependency>
     103            <groupId>com.h2database</groupId>
     104            <artifactId>h2</artifactId>
     105            <scope>test</scope>
     106        </dependency>
    101107    </dependencies>
    102108
  • backend/src/main/java/com/trekr/backend/BackendApplication.java

    r140d098 r3ebe47c  
    1212        // This ensures system properties are available for ${} placeholder resolution
    1313        Dotenv dotenv = loadEnvironmentVariables();
    14        
     14
    1515        // Create SpringApplication instance
    1616        SpringApplication app = new SpringApplication(BackendApplication.class);
    17        
     17
    1818        // Set system properties and default properties from .env file
    1919        if (dotenv != null) {
    2020            java.util.Map<String, Object> defaultProps = new java.util.HashMap<>();
    21            
     21
    2222            dotenv.entries().forEach(entry -> {
    2323                String key = entry.getKey();
     
    3030                }
    3131            });
    32            
     32
    3333            // Set all default properties at once
    3434            if (!defaultProps.isEmpty()) {
     
    3636            }
    3737        }
    38        
     38
    3939        app.run(args);
    4040    }
    41    
     41
    4242    private static Dotenv loadEnvironmentVariables() {
    4343        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) {
    5261            try {
    53                 dotenv = Dotenv.configure()
    54                         .directory("../")
     62                Dotenv loaded = Dotenv.configure()
     63                        .directory(candidate[0])
     64                        .filename(candidate[1])
    5565                        .ignoreIfMissing()
    5666                        .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
    6173            }
    6274        }
    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
    8776        return dotenv;
    8877    }
  • backend/src/main/java/com/trekr/backend/config/SecurityConfig.java

    r140d098 r3ebe47c  
    33import org.springframework.context.annotation.Bean;
    44import org.springframework.context.annotation.Configuration;
     5import org.springframework.security.config.Customizer;
    56import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    67import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
     
    89import org.springframework.security.crypto.password.PasswordEncoder;
    910import org.springframework.security.web.SecurityFilterChain;
     11import org.springframework.web.cors.CorsConfiguration;
     12import org.springframework.web.cors.CorsConfigurationSource;
     13import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
     14
     15import java.util.List;
    1016
    1117@Configuration
     
    1723        http
    1824                .csrf(AbstractHttpConfigurer::disable)
     25                .cors(Customizer.withDefaults())
    1926                .authorizeHttpRequests(auth -> auth
    2027                        .requestMatchers("/api/auth/**").permitAll()
    21                         .anyRequest().authenticated()
    22                 );
     28                        .anyRequest().authenticated());
    2329        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;
    2444    }
    2545
  • backend/src/main/java/com/trekr/backend/controller/AuthController.java

    r140d098 r3ebe47c  
    66import com.trekr.backend.service.AuthService;
    77import jakarta.validation.Valid;
    8 import lombok.RequiredArgsConstructor;
    98import org.springframework.http.HttpStatus;
    109import org.springframework.http.ResponseEntity;
     
    1312@RestController
    1413@RequestMapping("/api/auth")
    15 @RequiredArgsConstructor
    1614public class AuthController {
    17    
     15
    1816    private final AuthService authService;
    19    
     17
     18    public AuthController(AuthService authService) {
     19        this.authService = authService;
     20    }
     21
    2022    @PostMapping("/register")
    2123    public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
     
    2325        return ResponseEntity.status(HttpStatus.CREATED).body(response);
    2426    }
    25    
     27
    2628    @PostMapping("/login")
    2729    public ResponseEntity<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
  • backend/src/main/java/com/trekr/backend/dto/auth/AuthResponse.java

    r140d098 r3ebe47c  
    11package com.trekr.backend.dto.auth;
    22
    3 import lombok.AllArgsConstructor;
    4 import lombok.Data;
    5 import lombok.NoArgsConstructor;
    6 
    7 @Data
    8 @NoArgsConstructor
    9 @AllArgsConstructor
    103public class AuthResponse {
    114    private String token;
     
    147    private String username;
    158    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    }
    1660}
  • backend/src/main/java/com/trekr/backend/dto/auth/LoginRequest.java

    r140d098 r3ebe47c  
    22
    33import jakarta.validation.constraints.NotBlank;
    4 import lombok.Data;
    54
    6 @Data
    75public class LoginRequest {
    8    
     6
    97    @NotBlank(message = "Username or email is required")
    108    private String usernameOrEmail;
    11    
     9
    1210    @NotBlank(message = "Password is required")
    1311    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    }
    1431}
  • backend/src/main/java/com/trekr/backend/dto/auth/RegisterRequest.java

    r140d098 r3ebe47c  
    44import jakarta.validation.constraints.NotBlank;
    55import jakarta.validation.constraints.Size;
    6 import lombok.Data;
    76
    8 @Data
    97public class RegisterRequest {
    10    
     8
    119    @NotBlank(message = "Email is required")
    1210    @Email(message = "Email must be valid")
    1311    private String email;
    14    
     12
    1513    @NotBlank(message = "Username is required")
    1614    @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
    1715    private String username;
    18    
     16
    1917    @NotBlank(message = "Password is required")
    2018    @Size(min = 6, message = "Password must be at least 6 characters")
    2119    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    }
    2247}
  • backend/src/main/java/com/trekr/backend/entity/User.java

    r140d098 r3ebe47c  
    22
    33import jakarta.persistence.*;
    4 import lombok.AllArgsConstructor;
    5 import lombok.Getter;
    6 import lombok.NoArgsConstructor;
    7 import lombok.Setter;
    84
    95@Entity
    106@Table(name = "USERS", schema = "trekr")
    11 @Getter
    12 @Setter
    13 @NoArgsConstructor
    14 @AllArgsConstructor
    157public class User {
    16    
     8
    179    @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)
    2011    @Column(name = "user_id")
    2112    private Long userId;
    22    
     13
    2314    @Column(name = "email", nullable = false, unique = true)
    2415    private String email;
    25    
     16
    2617    @Column(name = "username", nullable = false, unique = true)
    2718    private String username;
    28    
     19
    2920    @Column(name = "password", nullable = false)
    3021    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    }
    3164}
  • 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
    24
    3 # General app config
    4 spring.application.name=trekr
    5 
    6 #spring.datasource.hikari.data-source-properties.useServerPrepStmts=false
    7 #spring.datasource.hikari.data-source-properties.prepareThreshold=0
    8 
    9 logging.level.org.springframework.security=DEBUG
    10 
    11 # JPA & Hibernate
    12 spring.jpa.hibernate.ddl-auto=update
    13 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
    14 spring.jpa.show-sql=false
    15 spring.jpa.properties.hibernate.format_sql=true
    16 spring.jpa.properties.hibernate.default_schema=trekr
    17 
    18 #logging.level.org.hibernate.SQL=DEBUG
    19 #logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
    20 
    21 
    22 # HikariCP settings
    23 spring.datasource.hikari.pool-name=trekrHikariPool
    24 
    25 # Maximum connections in the pool
    26 # Rule of thumb: (CPU cores × 2) + effective_spindle_count
    27 # For most apps: 10-20 is good
    28 spring.datasource.hikari.maximum-pool-size=20
    29 
    30 # Minimum idle connections always ready
    31 # Keep at least 5 ready for quick response
    32 spring.datasource.hikari.minimum-idle=5
    33 
    34 # How long to wait for a connection (before blocking the request) - 30sec
    35 spring.datasource.hikari.connection-timeout=30000
    36 
    37 # How long a connection can sit idle (before being closed) - 10min
    38 spring.datasource.hikari.idle-timeout=600000
    39 
    40 # Maximum lifetime of a connection (before being replaced with a new one) - 30min
    41 # Prevents stale connections
    42 spring.datasource.hikari.max-lifetime=1800000
    43 
    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 path
    49 api.base.path=/api
    50 api.admin.path=/api/admin
    51 
    52 #spring.profiles.active=dev
    53 
    54 # Max file size
    55 server.tomcat.max-part-count=50
    56 
    57 logging.level.org.springframework.web=DEBUG
    58 logging.level.org.apache.coyote.http11=DEBUG
    59 
    60 
    61 # PostgreSQL connection
    62 spring.datasource.driver-class-name=org.postgresql.Driver
    63 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  
    22
    33import lombok.Getter;
    4 import lombok.RequiredArgsConstructor;
    54
    65import java.io.Serializable;
     
    1110 */
    1211@Getter
    13 @RequiredArgsConstructor
    1412public class UserPrincipal implements Serializable {
     13    private static final long serialVersionUID = 1L;
     14
    1515    private final Long userId;
    1616    private final String username;
    1717    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    }
    1824}
  • backend/src/main/java/com/trekr/backend/service/AuthService.java

    r140d098 r3ebe47c  
    66import com.trekr.backend.entity.User;
    77import com.trekr.backend.repository.UserRepository;
    8 import lombok.RequiredArgsConstructor;
    98import org.springframework.security.crypto.password.PasswordEncoder;
    109import org.springframework.stereotype.Service;
     
    1211
    1312@Service
    14 @RequiredArgsConstructor
    1513public class AuthService {
    1614
     
    1816    private final PasswordEncoder passwordEncoder;
    1917    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    }
    2024
    2125    @Transactional
  • backend/src/main/resources/application.properties

    r140d098 r3ebe47c  
    1 spring.application.name=backend
     1spring.application.name=trekr
     2
     3# ============================
     4# JPA & Hibernate
     5# ============================
     6spring.jpa.hibernate.ddl-auto=update
     7spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
     8spring.jpa.show-sql=false
     9spring.jpa.properties.hibernate.format_sql=true
     10spring.jpa.properties.hibernate.default_schema=trekr
     11spring.jpa.properties.hibernate.hbm2ddl.create_namespaces=true
     12
     13# ============================
     14# PostgreSQL connection (loaded from backend/.env or backend/env)
     15# ============================
     16spring.datasource.driver-class-name=org.postgresql.Driver
     17spring.datasource.url=${SPRING_DATASOURCE_URL}
     18spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
     19spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
     20
     21# ============================
     22# JWT Configuration (supports JWT_SECRET or JWT_CONFIG_SECRET)
     23# ============================
     24jwt.secret=${JWT_SECRET:${JWT_CONFIG_SECRET:dev-fallback-secret-min-32-chars-required}}
     25
     26# ============================
     27# Logging
     28# ============================
     29logging.level.org.springframework.security=DEBUG
     30
     31# ============================
     32# HikariCP
     33# ============================
     34spring.datasource.hikari.pool-name=trekrHikariPool
     35spring.datasource.hikari.maximum-pool-size=20
     36spring.datasource.hikari.minimum-idle=5
     37spring.datasource.hikari.connection-timeout=30000
     38spring.datasource.hikari.idle-timeout=600000
     39spring.datasource.hikari.max-lifetime=1800000
     40
     41# Max multipart parts
     42server.tomcat.max-part-count=50
Note: See TracChangeset for help on using the changeset viewer.