feat(sprint-5): Phase 1 — Docker Compose full stack, CORS, Next.js upgrade

- Dockerfile.backend: multi-stage Java 21 build (eclipse-temurin)
- docker-compose.yml: PostgreSQL 16 + backend + frontend with health checks
- SecurityConfig: CORS for localhost:3000 frontend origin
- application-docker.properties: Docker profile with env vars
- Spring Boot Actuator health endpoint enabled
- Next.js upgraded 15.2.8 → 15.5.18 (security fixes)
This commit is contained in:
Patrick Plate
2026-06-12 19:51:24 +02:00
parent dce27a4291
commit 279f2f6de0
8 changed files with 2903 additions and 4614 deletions
+5
View File
@@ -118,6 +118,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- Actuator (health endpoint for Docker) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
@@ -1,6 +1,7 @@
package de.cannamanage.api.security;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@@ -13,8 +14,12 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
* Security configuration — Sprint 3: API + Staff portal with JWT + Member portal with sessions.
@@ -38,6 +43,7 @@ public class SecurityConfig {
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
@@ -116,4 +122,21 @@ public class SecurityConfig {
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of(
"http://localhost:3000",
"http://frontend:3000"
));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
}
@@ -0,0 +1,19 @@
# Docker profile — used when running in Docker Compose
spring.datasource.url=${SPRING_DATASOURCE_URL}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
# Enable Flyway for container startup (fresh DB)
spring.flyway.enabled=true
spring.jpa.hibernate.ddl-auto=validate
# JWT secret from environment
cannamanage.security.jwt.secret=${CANNAMANAGE_SECURITY_JWT_SECRET}
# Actuator
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=never
# Disable mail in Docker (no SMTP container)
spring.mail.host=localhost
spring.mail.port=1025
@@ -31,6 +31,10 @@ spring.mail.from=${MAIL_FROM:noreply@cannamanage.de}
# App base URL (for invite links)
app.base-url=${APP_BASE_URL:http://localhost:8080}
# Actuator
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=never
# Session configuration (member portal)
server.servlet.session.timeout=30m
server.servlet.session.cookie.same-site=strict