fixing(gradle)

This commit is contained in:
Stefan Mogeritsch 2025-08-27 00:30:53 +02:00
parent 8fd09a4aa1
commit 2c2dcd381c
15 changed files with 1125 additions and 37 deletions

View File

@ -80,15 +80,15 @@ tasks.register("generateOpenApiDocs") {
"openapi": "3.0.3",
"info": {
"title": "${moduleName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }} API",
"description": "REST API for $moduleName management",
"description": "REST API für $moduleName Verwaltung",
"version": "1.0.0",
"contact": {
"name": "Meldestelle Development Team"
}
},
"servers": [
{ "url": "http://localhost:8080", "description": "Development server" },
{ "url": "https://api.meldestelle.at", "description": "Production server" }
{ "url": "http://localhost:8080", "description": "Entwicklungs-Server" },
{ "url": "https://api.meldestelle.at", "description": "Produktions-Server" }
],
"paths": {},
"components": {

View File

@ -17,6 +17,9 @@ kotlin {
implementation(compose.foundation)
implementation(compose.material3)
// Skiko - explicitly declared in common-ui
implementation("org.jetbrains.skiko:skiko:0.9.4.2")
// Serialization
implementation(libs.kotlinx.serialization.json)

View File

@ -8,7 +8,7 @@
# Clients only: docker-compose -f docker-compose.clients.yml up
# ===================================================================
version: '3.8'
#version: '3.8'
services:
# ===================================================================

View File

@ -13,7 +13,7 @@
# - Faster startup times
# ===================================================================
version: '3.8'
#version: '3.8'
services:
# ===================================================================

View File

@ -380,6 +380,394 @@ services:
- "service.version=1.0.0"
- "service.environment=${SPRING_PROFILES_ACTIVE:-docker}"
# ===================================================================
# Members Service
# ===================================================================
members-service:
build:
context: .
dockerfile: dockerfiles/services/members-service/Dockerfile
args:
GRADLE_VERSION: ${GRADLE_VERSION:-8.14}
JAVA_VERSION: ${JAVA_VERSION:-21}
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker}
APP_USER: ${MEMBERS_APP_USER:-membersuser}
APP_UID: ${MEMBERS_APP_UID:-1004}
APP_GID: ${MEMBERS_APP_GID:-1004}
image: meldestelle/members-service:latest
container_name: meldestelle-members-service
ports:
- "${MEMBERS_SERVICE_PORT:-8084}:8084"
- "${MEMBERS_DEBUG_PORT:-5004}:5004" # Debug port
depends_on:
postgres:
condition: service_healthy
consul:
condition: service_healthy
redis:
condition: service_healthy
auth-server:
condition: service_healthy
environment:
# Spring Boot Configuration
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-docker}
- SERVER_PORT=8084
- MANAGEMENT_SERVER_PORT=8084
- DEBUG=${DEBUG:-false}
# Service Discovery
- SPRING_CLOUD_CONSUL_HOST=consul
- SPRING_CLOUD_CONSUL_PORT=8500
- SPRING_APPLICATION_NAME=members-service
# Database Configuration
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/${POSTGRES_DB:-meldestelle}
- SPRING_DATASOURCE_USERNAME=${POSTGRES_USER:-meldestelle}
- SPRING_DATASOURCE_PASSWORD=${POSTGRES_PASSWORD:-meldestelle}
- SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE=${MEMBERS_DB_POOL_SIZE:-20}
- SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE=${MEMBERS_DB_MIN_IDLE:-10}
# Redis Configuration
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
- SPRING_REDIS_PASSWORD=${REDIS_PASSWORD:-}
- SPRING_REDIS_TIMEOUT=${REDIS_TIMEOUT:-2000ms}
# Security Configuration
- AUTH_SERVER_URL=http://auth-server:8081
- JWT_SECRET=${JWT_SECRET:-meldestelle-auth-secret-key-change-in-production}
# Monitoring & Observability
- MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,info,metrics,prometheus,configprops
- MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS=always
- MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED=true
- MANAGEMENT_TRACING_SAMPLING_PROBABILITY=${TRACING_SAMPLING:-0.1}
- MANAGEMENT_ZIPKIN_TRACING_ENDPOINT=http://zipkin:9411/api/v2/spans
# Performance Tuning
- JAVA_OPTS=-XX:MaxRAMPercentage=80.0 -XX:+UseG1GC -XX:+UseStringDeduplication
- LOGGING_LEVEL_ROOT=${LOG_LEVEL:-INFO}
- LOGGING_LEVEL_AT_MOCODE_MEMBERS=${MEMBERS_LOG_LEVEL:-DEBUG}
networks:
- meldestelle-network
volumes:
- members-logs:/app/logs
- members-temp:/app/tmp
healthcheck:
test: ["CMD", "curl", "-fsS", "--max-time", "3", "http://localhost:8084/actuator/health/readiness"]
interval: 15s
timeout: 5s
retries: 3
start_period: 45s
restart: unless-stopped
deploy:
resources:
limits:
memory: 1024M
cpus: '1.5'
labels:
- "traefik.enable=true"
- "traefik.http.routers.members-service.rule=Host(`members.meldestelle.local`)"
- "traefik.http.services.members-service.loadbalancer.server.port=8084"
- "prometheus.scrape=true"
- "prometheus.port=8084"
- "prometheus.path=/actuator/prometheus"
- "prometheus.service=members-service"
- "service.name=members-service"
- "service.version=1.0.0"
- "service.environment=${SPRING_PROFILES_ACTIVE:-docker}"
# ===================================================================
# Horses Service
# ===================================================================
horses-service:
build:
context: .
dockerfile: dockerfiles/services/horses-service/Dockerfile
args:
GRADLE_VERSION: ${GRADLE_VERSION:-8.14}
JAVA_VERSION: ${JAVA_VERSION:-21}
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker}
APP_USER: ${HORSES_APP_USER:-horsesuser}
APP_UID: ${HORSES_APP_UID:-1005}
APP_GID: ${HORSES_APP_GID:-1005}
image: meldestelle/horses-service:latest
container_name: meldestelle-horses-service
ports:
- "${HORSES_SERVICE_PORT:-8085}:8085"
- "${HORSES_DEBUG_PORT:-5005}:5005" # Debug port
depends_on:
postgres:
condition: service_healthy
consul:
condition: service_healthy
redis:
condition: service_healthy
auth-server:
condition: service_healthy
environment:
# Spring Boot Configuration
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-docker}
- SERVER_PORT=8085
- MANAGEMENT_SERVER_PORT=8085
- DEBUG=${DEBUG:-false}
# Service Discovery
- SPRING_CLOUD_CONSUL_HOST=consul
- SPRING_CLOUD_CONSUL_PORT=8500
- SPRING_APPLICATION_NAME=horses-service
# Database Configuration
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/${POSTGRES_DB:-meldestelle}
- SPRING_DATASOURCE_USERNAME=${POSTGRES_USER:-meldestelle}
- SPRING_DATASOURCE_PASSWORD=${POSTGRES_PASSWORD:-meldestelle}
- SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE=${HORSES_DB_POOL_SIZE:-20}
- SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE=${HORSES_DB_MIN_IDLE:-10}
# Redis Configuration
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
- SPRING_REDIS_PASSWORD=${REDIS_PASSWORD:-}
- SPRING_REDIS_TIMEOUT=${REDIS_TIMEOUT:-2000ms}
# Security Configuration
- AUTH_SERVER_URL=http://auth-server:8081
- JWT_SECRET=${JWT_SECRET:-meldestelle-auth-secret-key-change-in-production}
# Monitoring & Observability
- MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,info,metrics,prometheus,configprops
- MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS=always
- MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED=true
- MANAGEMENT_TRACING_SAMPLING_PROBABILITY=${TRACING_SAMPLING:-0.1}
- MANAGEMENT_ZIPKIN_TRACING_ENDPOINT=http://zipkin:9411/api/v2/spans
# Performance Tuning
- JAVA_OPTS=-XX:MaxRAMPercentage=80.0 -XX:+UseG1GC -XX:+UseStringDeduplication
- LOGGING_LEVEL_ROOT=${LOG_LEVEL:-INFO}
- LOGGING_LEVEL_AT_MOCODE_HORSES=${HORSES_LOG_LEVEL:-DEBUG}
networks:
- meldestelle-network
volumes:
- horses-logs:/app/logs
- horses-temp:/app/tmp
healthcheck:
test: ["CMD", "curl", "-fsS", "--max-time", "3", "http://localhost:8085/actuator/health/readiness"]
interval: 15s
timeout: 5s
retries: 3
start_period: 45s
restart: unless-stopped
deploy:
resources:
limits:
memory: 1024M
cpus: '1.5'
labels:
- "traefik.enable=true"
- "traefik.http.routers.horses-service.rule=Host(`horses.meldestelle.local`)"
- "traefik.http.services.horses-service.loadbalancer.server.port=8085"
- "prometheus.scrape=true"
- "prometheus.port=8085"
- "prometheus.path=/actuator/prometheus"
- "prometheus.service=horses-service"
- "service.name=horses-service"
- "service.version=1.0.0"
- "service.environment=${SPRING_PROFILES_ACTIVE:-docker}"
# ===================================================================
# Events Service
# ===================================================================
events-service:
build:
context: .
dockerfile: dockerfiles/services/events-service/Dockerfile
args:
GRADLE_VERSION: ${GRADLE_VERSION:-8.14}
JAVA_VERSION: ${JAVA_VERSION:-21}
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker}
APP_USER: ${EVENTS_APP_USER:-eventsuser}
APP_UID: ${EVENTS_APP_UID:-1006}
APP_GID: ${EVENTS_APP_GID:-1006}
image: meldestelle/events-service:latest
container_name: meldestelle-events-service
ports:
- "${EVENTS_SERVICE_PORT:-8086}:8086"
- "${EVENTS_DEBUG_PORT:-5006}:5006" # Debug port
depends_on:
postgres:
condition: service_healthy
consul:
condition: service_healthy
redis:
condition: service_healthy
auth-server:
condition: service_healthy
environment:
# Spring Boot Configuration
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-docker}
- SERVER_PORT=8086
- MANAGEMENT_SERVER_PORT=8086
- DEBUG=${DEBUG:-false}
# Service Discovery
- SPRING_CLOUD_CONSUL_HOST=consul
- SPRING_CLOUD_CONSUL_PORT=8500
- SPRING_APPLICATION_NAME=events-service
# Database Configuration
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/${POSTGRES_DB:-meldestelle}
- SPRING_DATASOURCE_USERNAME=${POSTGRES_USER:-meldestelle}
- SPRING_DATASOURCE_PASSWORD=${POSTGRES_PASSWORD:-meldestelle}
- SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE=${EVENTS_DB_POOL_SIZE:-20}
- SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE=${EVENTS_DB_MIN_IDLE:-10}
# Redis Configuration
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
- SPRING_REDIS_PASSWORD=${REDIS_PASSWORD:-}
- SPRING_REDIS_TIMEOUT=${REDIS_TIMEOUT:-2000ms}
# Security Configuration
- AUTH_SERVER_URL=http://auth-server:8081
- JWT_SECRET=${JWT_SECRET:-meldestelle-auth-secret-key-change-in-production}
# Monitoring & Observability
- MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,info,metrics,prometheus,configprops
- MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS=always
- MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED=true
- MANAGEMENT_TRACING_SAMPLING_PROBABILITY=${TRACING_SAMPLING:-0.1}
- MANAGEMENT_ZIPKIN_TRACING_ENDPOINT=http://zipkin:9411/api/v2/spans
# Performance Tuning
- JAVA_OPTS=-XX:MaxRAMPercentage=80.0 -XX:+UseG1GC -XX:+UseStringDeduplication
- LOGGING_LEVEL_ROOT=${LOG_LEVEL:-INFO}
- LOGGING_LEVEL_AT_MOCODE_EVENTS=${EVENTS_LOG_LEVEL:-DEBUG}
networks:
- meldestelle-network
volumes:
- events-logs:/app/logs
- events-temp:/app/tmp
healthcheck:
test: ["CMD", "curl", "-fsS", "--max-time", "3", "http://localhost:8086/actuator/health/readiness"]
interval: 15s
timeout: 5s
retries: 3
start_period: 45s
restart: unless-stopped
deploy:
resources:
limits:
memory: 1024M
cpus: '1.5'
labels:
- "traefik.enable=true"
- "traefik.http.routers.events-service.rule=Host(`events.meldestelle.local`)"
- "traefik.http.services.events-service.loadbalancer.server.port=8086"
- "prometheus.scrape=true"
- "prometheus.port=8086"
- "prometheus.path=/actuator/prometheus"
- "prometheus.service=events-service"
- "service.name=events-service"
- "service.version=1.0.0"
- "service.environment=${SPRING_PROFILES_ACTIVE:-docker}"
# ===================================================================
# Masterdata Service
# ===================================================================
masterdata-service:
build:
context: .
dockerfile: dockerfiles/services/masterdata-service/Dockerfile
args:
GRADLE_VERSION: ${GRADLE_VERSION:-8.14}
JAVA_VERSION: ${JAVA_VERSION:-21}
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker}
APP_USER: ${MASTERDATA_APP_USER:-masterdatauser}
APP_UID: ${MASTERDATA_APP_UID:-1007}
APP_GID: ${MASTERDATA_APP_GID:-1007}
image: meldestelle/masterdata-service:latest
container_name: meldestelle-masterdata-service
ports:
- "${MASTERDATA_SERVICE_PORT:-8087}:8087"
- "${MASTERDATA_DEBUG_PORT:-5007}:5007" # Debug port
depends_on:
postgres:
condition: service_healthy
consul:
condition: service_healthy
redis:
condition: service_healthy
auth-server:
condition: service_healthy
environment:
# Spring Boot Configuration
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-docker}
- SERVER_PORT=8087
- MANAGEMENT_SERVER_PORT=8087
- DEBUG=${DEBUG:-false}
# Service Discovery
- SPRING_CLOUD_CONSUL_HOST=consul
- SPRING_CLOUD_CONSUL_PORT=8500
- SPRING_APPLICATION_NAME=masterdata-service
# Database Configuration
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/${POSTGRES_DB:-meldestelle}
- SPRING_DATASOURCE_USERNAME=${POSTGRES_USER:-meldestelle}
- SPRING_DATASOURCE_PASSWORD=${POSTGRES_PASSWORD:-meldestelle}
- SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE=${MASTERDATA_DB_POOL_SIZE:-15}
- SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE=${MASTERDATA_DB_MIN_IDLE:-8}
# Redis Configuration
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
- SPRING_REDIS_PASSWORD=${REDIS_PASSWORD:-}
- SPRING_REDIS_TIMEOUT=${REDIS_TIMEOUT:-2000ms}
# Security Configuration
- AUTH_SERVER_URL=http://auth-server:8081
- JWT_SECRET=${JWT_SECRET:-meldestelle-auth-secret-key-change-in-production}
# Monitoring & Observability
- MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,info,metrics,prometheus,configprops
- MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS=always
- MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED=true
- MANAGEMENT_TRACING_SAMPLING_PROBABILITY=${TRACING_SAMPLING:-0.1}
- MANAGEMENT_ZIPKIN_TRACING_ENDPOINT=http://zipkin:9411/api/v2/spans
# Performance Tuning
- JAVA_OPTS=-XX:MaxRAMPercentage=80.0 -XX:+UseG1GC -XX:+UseStringDeduplication
- LOGGING_LEVEL_ROOT=${LOG_LEVEL:-INFO}
- LOGGING_LEVEL_AT_MOCODE_MASTERDATA=${MASTERDATA_LOG_LEVEL:-DEBUG}
networks:
- meldestelle-network
volumes:
- masterdata-logs:/app/logs
- masterdata-temp:/app/tmp
healthcheck:
test: ["CMD", "curl", "-fsS", "--max-time", "3", "http://localhost:8087/actuator/health/readiness"]
interval: 15s
timeout: 5s
retries: 3
start_period: 45s
restart: unless-stopped
deploy:
resources:
limits:
memory: 768M
cpus: '1.0'
labels:
- "traefik.enable=true"
- "traefik.http.routers.masterdata-service.rule=Host(`masterdata.meldestelle.local`)"
- "traefik.http.services.masterdata-service.loadbalancer.server.port=8087"
- "prometheus.scrape=true"
- "prometheus.port=8087"
- "prometheus.path=/actuator/prometheus"
- "prometheus.service=masterdata-service"
- "service.name=masterdata-service"
- "service.version=1.0.0"
- "service.environment=${SPRING_PROFILES_ACTIVE:-docker}"
# ===================================================================
# Volumes for Service Data, Logs, and Temporary Files
# ===================================================================
@ -430,6 +818,46 @@ volumes:
ping-temp:
driver: local
# Members Service
members-logs:
driver: local
driver_opts:
type: none
o: bind
device: ./logs/members-service
members-temp:
driver: local
# Horses Service
horses-logs:
driver: local
driver_opts:
type: none
o: bind
device: ./logs/horses-service
horses-temp:
driver: local
# Events Service
events-logs:
driver: local
driver_opts:
type: none
o: bind
device: ./logs/events-service
events-temp:
driver: local
# Masterdata Service
masterdata-logs:
driver: local
driver_opts:
type: none
o: bind
device: ./logs/masterdata-service
masterdata-temp:
driver: local
# ===================================================================
# Networks (inherits from main docker-compose.yml)
# ===================================================================

View File

@ -0,0 +1,163 @@
# syntax=docker/dockerfile:1.7
# ===================================================================
# Dockerfile for Events Service
# Based on Spring Boot Service Template with Events-specific configuration
# ===================================================================
# Build arguments
ARG GRADLE_VERSION=8.14
ARG JAVA_VERSION=21
ARG ALPINE_VERSION=3.19
ARG SPRING_PROFILES_ACTIVE=docker
ARG SERVICE_PATH=events/events-service
ARG SERVICE_NAME=events-service
ARG SERVICE_PORT=8086
# ===================================================================
# Build Stage
# ===================================================================
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION}-alpine AS builder
# Re-declare build arguments for this stage
ARG SERVICE_PATH=events/events-service
ARG SERVICE_NAME=events-service
ARG SERVICE_PORT=8086
ARG SPRING_PROFILES_ACTIVE=docker
LABEL stage=builder
LABEL maintainer="Meldestelle Development Team"
WORKDIR /workspace
# Gradle optimizations
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
-Dorg.gradle.daemon=false \
-Dorg.gradle.parallel=true \
-Dorg.gradle.configureondemand=true \
-Xmx2g"
# Copy build files in optimal order for caching
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
COPY gradle/ gradle/
COPY platform/ platform/
COPY core/ core/
COPY build.gradle.kts ./
# Copy events service modules in dependency order
COPY events/events-domain/ events/events-domain/
COPY events/events-api/ events/events-api/
COPY events/events-application/ events/events-application/
COPY events/events-infrastructure/ events/events-infrastructure/
COPY events/events-service/ events/events-service/
# Build events service
RUN echo "Building Events Service..." && \
./gradlew :events:events-service:dependencies --no-daemon --info && \
./gradlew :events:events-service:bootJar --no-daemon --info -Pspring.profiles.active=${SPRING_PROFILES_ACTIVE}
# Extract JAR layers for optimized Docker layer caching
WORKDIR /builder
RUN cp /workspace/events/events-service/build/libs/*.jar app.jar && \
java -Djarmode=layertools -jar app.jar extract
# ===================================================================
# Runtime Stage
# ===================================================================
FROM eclipse-temurin:${JAVA_VERSION}-jre-alpine AS runtime
# Metadata
LABEL service="events-service" \
version="1.0.0" \
description="Events Management Service for Austrian Equestrian Federation" \
maintainer="Meldestelle Development Team" \
java.version="${JAVA_VERSION}"
# Build arguments
ARG APP_USER=eventsuser
ARG APP_GROUP=eventsgroup
ARG APP_UID=1006
ARG APP_GID=1006
WORKDIR /app
# System setup
RUN apk update && \
apk upgrade && \
apk add --no-cache curl jq tzdata && \
rm -rf /var/cache/apk/*
# Non-root user creation
RUN addgroup -g ${APP_GID} -S ${APP_GROUP} && \
adduser -u ${APP_UID} -S ${APP_USER} -G ${APP_GROUP} -h /app -s /bin/sh
# Directory setup
RUN mkdir -p /app/logs /app/tmp && \
chown -R ${APP_USER}:${APP_GROUP} /app
# Re-declare build arguments for runtime stage
ARG SERVICE_PATH=events/events-service
ARG SERVICE_NAME=events-service
ARG SERVICE_PORT=8086
# Copy Spring Boot layers in optimal order for Docker layer caching
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/spring-boot-loader/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/snapshot-dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/application/ ./
USER ${APP_USER}
# Expose application port and debug port
EXPOSE ${SERVICE_PORT} 5006
# Health check
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \
CMD curl -fsS --max-time 2 http://localhost:${SERVICE_PORT}/actuator/health/readiness || exit 1
# JVM configuration optimized for events service
ENV JAVA_OPTS="-XX:MaxRAMPercentage=80.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication \
-XX:+UseContainerSupport \
-XX:G1HeapRegionSize=16m \
-XX:+OptimizeStringConcat \
-XX:+UseCompressedOops \
-Djava.security.egd=file:/dev/./urandom \
-Djava.awt.headless=true \
-Dfile.encoding=UTF-8 \
-Duser.timezone=Europe/Vienna \
-Dmanagement.endpoints.web.exposure.include=health,info,metrics,prometheus"
# Spring Boot configuration
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} \
SERVER_PORT=${SERVICE_PORT} \
LOGGING_LEVEL_ROOT=INFO \
LOGGING_LEVEL_AT_MOCODE_EVENTS=DEBUG
# Startup command with debug support
ENTRYPOINT ["sh", "-c", "\
echo 'Starting Events Service on port ${SERVICE_PORT}...'; \
if [ \"${DEBUG:-false}\" = \"true\" ]; then \
echo 'Debug mode enabled on port 5006'; \
exec java $JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5006 org.springframework.boot.loader.launch.JarLauncher; \
else \
exec java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher; \
fi"]
# ===================================================================
# Documentation
# ===================================================================
# Build commands:
# docker build -t meldestelle/events-service:latest -f dockerfiles/services/events-service/Dockerfile .
# docker run -p 8086:8086 --name events-service meldestelle/events-service:latest
#
# Key features:
# - Multi-stage build with JAR layer extraction for optimal caching
# - Non-root user execution for security (UID/GID 1006)
# - Optimized JVM settings for containers
# - Comprehensive health checks with events-specific endpoint
# - Debug support on port 5006
# - Vienna timezone configuration for Austrian operations
# ===================================================================

View File

@ -0,0 +1,163 @@
# syntax=docker/dockerfile:1.7
# ===================================================================
# Dockerfile for Horses Service
# Based on Spring Boot Service Template with Horses-specific configuration
# ===================================================================
# Build arguments
ARG GRADLE_VERSION=8.14
ARG JAVA_VERSION=21
ARG ALPINE_VERSION=3.19
ARG SPRING_PROFILES_ACTIVE=docker
ARG SERVICE_PATH=horses/horses-service
ARG SERVICE_NAME=horses-service
ARG SERVICE_PORT=8085
# ===================================================================
# Build Stage
# ===================================================================
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION}-alpine AS builder
# Re-declare build arguments for this stage
ARG SERVICE_PATH=horses/horses-service
ARG SERVICE_NAME=horses-service
ARG SERVICE_PORT=8085
ARG SPRING_PROFILES_ACTIVE=docker
LABEL stage=builder
LABEL maintainer="Meldestelle Development Team"
WORKDIR /workspace
# Gradle optimizations
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
-Dorg.gradle.daemon=false \
-Dorg.gradle.parallel=true \
-Dorg.gradle.configureondemand=true \
-Xmx2g"
# Copy build files in optimal order for caching
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
COPY gradle/ gradle/
COPY platform/ platform/
COPY core/ core/
COPY build.gradle.kts ./
# Copy horses service modules in dependency order
COPY horses/horses-domain/ horses/horses-domain/
COPY horses/horses-api/ horses/horses-api/
COPY horses/horses-application/ horses/horses-application/
COPY horses/horses-infrastructure/ horses/horses-infrastructure/
COPY horses/horses-service/ horses/horses-service/
# Build horses service
RUN echo "Building Horses Service..." && \
./gradlew :horses:horses-service:dependencies --no-daemon --info && \
./gradlew :horses:horses-service:bootJar --no-daemon --info -Pspring.profiles.active=${SPRING_PROFILES_ACTIVE}
# Extract JAR layers for optimized Docker layer caching
WORKDIR /builder
RUN cp /workspace/horses/horses-service/build/libs/*.jar app.jar && \
java -Djarmode=layertools -jar app.jar extract
# ===================================================================
# Runtime Stage
# ===================================================================
FROM eclipse-temurin:${JAVA_VERSION}-jre-alpine AS runtime
# Metadata
LABEL service="horses-service" \
version="1.0.0" \
description="Horses Management Service for Austrian Equestrian Federation" \
maintainer="Meldestelle Development Team" \
java.version="${JAVA_VERSION}"
# Build arguments
ARG APP_USER=horsesuser
ARG APP_GROUP=horsesgroup
ARG APP_UID=1005
ARG APP_GID=1005
WORKDIR /app
# System setup
RUN apk update && \
apk upgrade && \
apk add --no-cache curl jq tzdata && \
rm -rf /var/cache/apk/*
# Non-root user creation
RUN addgroup -g ${APP_GID} -S ${APP_GROUP} && \
adduser -u ${APP_UID} -S ${APP_USER} -G ${APP_GROUP} -h /app -s /bin/sh
# Directory setup
RUN mkdir -p /app/logs /app/tmp && \
chown -R ${APP_USER}:${APP_GROUP} /app
# Re-declare build arguments for runtime stage
ARG SERVICE_PATH=horses/horses-service
ARG SERVICE_NAME=horses-service
ARG SERVICE_PORT=8085
# Copy Spring Boot layers in optimal order for Docker layer caching
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/spring-boot-loader/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/snapshot-dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/application/ ./
USER ${APP_USER}
# Expose application port and debug port
EXPOSE ${SERVICE_PORT} 5005
# Health check
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \
CMD curl -fsS --max-time 2 http://localhost:${SERVICE_PORT}/actuator/health/readiness || exit 1
# JVM configuration optimized for horses service
ENV JAVA_OPTS="-XX:MaxRAMPercentage=80.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication \
-XX:+UseContainerSupport \
-XX:G1HeapRegionSize=16m \
-XX:+OptimizeStringConcat \
-XX:+UseCompressedOops \
-Djava.security.egd=file:/dev/./urandom \
-Djava.awt.headless=true \
-Dfile.encoding=UTF-8 \
-Duser.timezone=Europe/Vienna \
-Dmanagement.endpoints.web.exposure.include=health,info,metrics,prometheus"
# Spring Boot configuration
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} \
SERVER_PORT=${SERVICE_PORT} \
LOGGING_LEVEL_ROOT=INFO \
LOGGING_LEVEL_AT_MOCODE_HORSES=DEBUG
# Startup command with debug support
ENTRYPOINT ["sh", "-c", "\
echo 'Starting Horses Service on port ${SERVICE_PORT}...'; \
if [ \"${DEBUG:-false}\" = \"true\" ]; then \
echo 'Debug mode enabled on port 5005'; \
exec java $JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 org.springframework.boot.loader.launch.JarLauncher; \
else \
exec java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher; \
fi"]
# ===================================================================
# Documentation
# ===================================================================
# Build commands:
# docker build -t meldestelle/horses-service:latest -f dockerfiles/services/horses-service/Dockerfile .
# docker run -p 8085:8085 --name horses-service meldestelle/horses-service:latest
#
# Key features:
# - Multi-stage build with JAR layer extraction for optimal caching
# - Non-root user execution for security (UID/GID 1005)
# - Optimized JVM settings for containers
# - Comprehensive health checks with horses-specific endpoint
# - Debug support on port 5005
# - Vienna timezone configuration for Austrian operations
# ===================================================================

View File

@ -0,0 +1,163 @@
# syntax=docker/dockerfile:1.7
# ===================================================================
# Dockerfile for Masterdata Service
# Based on Spring Boot Service Template with Masterdata-specific configuration
# ===================================================================
# Build arguments
ARG GRADLE_VERSION=8.14
ARG JAVA_VERSION=21
ARG ALPINE_VERSION=3.19
ARG SPRING_PROFILES_ACTIVE=docker
ARG SERVICE_PATH=masterdata/masterdata-service
ARG SERVICE_NAME=masterdata-service
ARG SERVICE_PORT=8087
# ===================================================================
# Build Stage
# ===================================================================
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION}-alpine AS builder
# Re-declare build arguments for this stage
ARG SERVICE_PATH=masterdata/masterdata-service
ARG SERVICE_NAME=masterdata-service
ARG SERVICE_PORT=8087
ARG SPRING_PROFILES_ACTIVE=docker
LABEL stage=builder
LABEL maintainer="Meldestelle Development Team"
WORKDIR /workspace
# Gradle optimizations
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
-Dorg.gradle.daemon=false \
-Dorg.gradle.parallel=true \
-Dorg.gradle.configureondemand=true \
-Xmx2g"
# Copy build files in optimal order for caching
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
COPY gradle/ gradle/
COPY platform/ platform/
COPY core/ core/
COPY build.gradle.kts ./
# Copy masterdata service modules in dependency order
COPY masterdata/masterdata-domain/ masterdata/masterdata-domain/
COPY masterdata/masterdata-api/ masterdata/masterdata-api/
COPY masterdata/masterdata-application/ masterdata/masterdata-application/
COPY masterdata/masterdata-infrastructure/ masterdata/masterdata-infrastructure/
COPY masterdata/masterdata-service/ masterdata/masterdata-service/
# Build masterdata service
RUN echo "Building Masterdata Service..." && \
./gradlew :masterdata:masterdata-service:dependencies --no-daemon --info && \
./gradlew :masterdata:masterdata-service:bootJar --no-daemon --info -Pspring.profiles.active=${SPRING_PROFILES_ACTIVE}
# Extract JAR layers for optimized Docker layer caching
WORKDIR /builder
RUN cp /workspace/masterdata/masterdata-service/build/libs/*.jar app.jar && \
java -Djarmode=layertools -jar app.jar extract
# ===================================================================
# Runtime Stage
# ===================================================================
FROM eclipse-temurin:${JAVA_VERSION}-jre-alpine AS runtime
# Metadata
LABEL service="masterdata-service" \
version="1.0.0" \
description="Masterdata Management Service for Austrian Equestrian Federation" \
maintainer="Meldestelle Development Team" \
java.version="${JAVA_VERSION}"
# Build arguments
ARG APP_USER=masterdatauser
ARG APP_GROUP=masterdatagroup
ARG APP_UID=1007
ARG APP_GID=1007
WORKDIR /app
# System setup
RUN apk update && \
apk upgrade && \
apk add --no-cache curl jq tzdata && \
rm -rf /var/cache/apk/*
# Non-root user creation
RUN addgroup -g ${APP_GID} -S ${APP_GROUP} && \
adduser -u ${APP_UID} -S ${APP_USER} -G ${APP_GROUP} -h /app -s /bin/sh
# Directory setup
RUN mkdir -p /app/logs /app/tmp && \
chown -R ${APP_USER}:${APP_GROUP} /app
# Re-declare build arguments for runtime stage
ARG SERVICE_PATH=masterdata/masterdata-service
ARG SERVICE_NAME=masterdata-service
ARG SERVICE_PORT=8087
# Copy Spring Boot layers in optimal order for Docker layer caching
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/spring-boot-loader/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/snapshot-dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/application/ ./
USER ${APP_USER}
# Expose application port and debug port
EXPOSE ${SERVICE_PORT} 5007
# Health check
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \
CMD curl -fsS --max-time 2 http://localhost:${SERVICE_PORT}/actuator/health/readiness || exit 1
# JVM configuration optimized for masterdata service
ENV JAVA_OPTS="-XX:MaxRAMPercentage=80.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication \
-XX:+UseContainerSupport \
-XX:G1HeapRegionSize=16m \
-XX:+OptimizeStringConcat \
-XX:+UseCompressedOops \
-Djava.security.egd=file:/dev/./urandom \
-Djava.awt.headless=true \
-Dfile.encoding=UTF-8 \
-Duser.timezone=Europe/Vienna \
-Dmanagement.endpoints.web.exposure.include=health,info,metrics,prometheus"
# Spring Boot configuration
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} \
SERVER_PORT=${SERVICE_PORT} \
LOGGING_LEVEL_ROOT=INFO \
LOGGING_LEVEL_AT_MOCODE_MASTERDATA=DEBUG
# Startup command with debug support
ENTRYPOINT ["sh", "-c", "\
echo 'Starting Masterdata Service on port ${SERVICE_PORT}...'; \
if [ \"${DEBUG:-false}\" = \"true\" ]; then \
echo 'Debug mode enabled on port 5007'; \
exec java $JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5007 org.springframework.boot.loader.launch.JarLauncher; \
else \
exec java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher; \
fi"]
# ===================================================================
# Documentation
# ===================================================================
# Build commands:
# docker build -t meldestelle/masterdata-service:latest -f dockerfiles/services/masterdata-service/Dockerfile .
# docker run -p 8087:8087 --name masterdata-service meldestelle/masterdata-service:latest
#
# Key features:
# - Multi-stage build with JAR layer extraction for optimal caching
# - Non-root user execution for security (UID/GID 1007)
# - Optimized JVM settings for containers
# - Comprehensive health checks with masterdata-specific endpoint
# - Debug support on port 5007
# - Vienna timezone configuration for Austrian operations
# ===================================================================

View File

@ -0,0 +1,163 @@
# syntax=docker/dockerfile:1.7
# ===================================================================
# Dockerfile for Members Service
# Based on Spring Boot Service Template with Members-specific configuration
# ===================================================================
# Build arguments
ARG GRADLE_VERSION=8.14
ARG JAVA_VERSION=21
ARG ALPINE_VERSION=3.19
ARG SPRING_PROFILES_ACTIVE=docker
ARG SERVICE_PATH=members/members-service
ARG SERVICE_NAME=members-service
ARG SERVICE_PORT=8084
# ===================================================================
# Build Stage
# ===================================================================
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION}-alpine AS builder
# Re-declare build arguments for this stage
ARG SERVICE_PATH=members/members-service
ARG SERVICE_NAME=members-service
ARG SERVICE_PORT=8084
ARG SPRING_PROFILES_ACTIVE=docker
LABEL stage=builder
LABEL maintainer="Meldestelle Development Team"
WORKDIR /workspace
# Gradle optimizations
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
-Dorg.gradle.daemon=false \
-Dorg.gradle.parallel=true \
-Dorg.gradle.configureondemand=true \
-Xmx2g"
# Copy build files in optimal order for caching
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
COPY gradle/ gradle/
COPY platform/ platform/
COPY core/ core/
COPY build.gradle.kts ./
# Copy members service modules in dependency order
COPY members/members-domain/ members/members-domain/
COPY members/members-api/ members/members-api/
COPY members/members-application/ members/members-application/
COPY members/members-infrastructure/ members/members-infrastructure/
COPY members/members-service/ members/members-service/
# Build members service
RUN echo "Building Members Service..." && \
./gradlew :members:members-service:dependencies --no-daemon --info && \
./gradlew :members:members-service:bootJar --no-daemon --info -Pspring.profiles.active=${SPRING_PROFILES_ACTIVE}
# Extract JAR layers for optimized Docker layer caching
WORKDIR /builder
RUN cp /workspace/members/members-service/build/libs/*.jar app.jar && \
java -Djarmode=layertools -jar app.jar extract
# ===================================================================
# Runtime Stage
# ===================================================================
FROM eclipse-temurin:${JAVA_VERSION}-jre-alpine AS runtime
# Metadata
LABEL service="members-service" \
version="1.0.0" \
description="Members Management Service for Austrian Equestrian Federation" \
maintainer="Meldestelle Development Team" \
java.version="${JAVA_VERSION}"
# Build arguments
ARG APP_USER=membersuser
ARG APP_GROUP=membersgroup
ARG APP_UID=1004
ARG APP_GID=1004
WORKDIR /app
# System setup
RUN apk update && \
apk upgrade && \
apk add --no-cache curl jq tzdata && \
rm -rf /var/cache/apk/*
# Non-root user creation
RUN addgroup -g ${APP_GID} -S ${APP_GROUP} && \
adduser -u ${APP_UID} -S ${APP_USER} -G ${APP_GROUP} -h /app -s /bin/sh
# Directory setup
RUN mkdir -p /app/logs /app/tmp && \
chown -R ${APP_USER}:${APP_GROUP} /app
# Re-declare build arguments for runtime stage
ARG SERVICE_PATH=members/members-service
ARG SERVICE_NAME=members-service
ARG SERVICE_PORT=8084
# Copy Spring Boot layers in optimal order for Docker layer caching
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/spring-boot-loader/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/snapshot-dependencies/ ./
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/application/ ./
USER ${APP_USER}
# Expose application port and debug port
EXPOSE ${SERVICE_PORT} 5004
# Health check
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \
CMD curl -fsS --max-time 2 http://localhost:${SERVICE_PORT}/actuator/health/readiness || exit 1
# JVM configuration optimized for members service
ENV JAVA_OPTS="-XX:MaxRAMPercentage=80.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication \
-XX:+UseContainerSupport \
-XX:G1HeapRegionSize=16m \
-XX:+OptimizeStringConcat \
-XX:+UseCompressedOops \
-Djava.security.egd=file:/dev/./urandom \
-Djava.awt.headless=true \
-Dfile.encoding=UTF-8 \
-Duser.timezone=Europe/Vienna \
-Dmanagement.endpoints.web.exposure.include=health,info,metrics,prometheus"
# Spring Boot configuration
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} \
SERVER_PORT=${SERVICE_PORT} \
LOGGING_LEVEL_ROOT=INFO \
LOGGING_LEVEL_AT_MOCODE_MEMBERS=DEBUG
# Startup command with debug support
ENTRYPOINT ["sh", "-c", "\
echo 'Starting Members Service on port ${SERVICE_PORT}...'; \
if [ \"${DEBUG:-false}\" = \"true\" ]; then \
echo 'Debug mode enabled on port 5004'; \
exec java $JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5004 org.springframework.boot.loader.launch.JarLauncher; \
else \
exec java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher; \
fi"]
# ===================================================================
# Documentation
# ===================================================================
# Build commands:
# docker build -t meldestelle/members-service:latest -f dockerfiles/services/members-service/Dockerfile .
# docker run -p 8084:8084 --name members-service meldestelle/members-service:latest
#
# Key features:
# - Multi-stage build with JAR layer extraction for optimal caching
# - Non-root user execution for security (UID/GID 1004)
# - Optimized JVM settings for containers
# - Comprehensive health checks with members-specific endpoint
# - Debug support on port 5004
# - Vienna timezone configuration for Austrian operations
# ===================================================================

View File

@ -6,7 +6,6 @@ import java.time.Instant
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.atomic.LongAdder
import kotlin.time.toKotlinDuration
/**
* Comprehensive metrics tracking for Redis Event Store operations.

View File

@ -52,7 +52,7 @@ dependencies {
// Obwohl bereits im monitoring-client Bundle, wird durch 'implementation' nicht transitiv verfügbar
implementation(libs.spring.boot.starter.actuator)
// Logback-Abhängigkeiten für Tests - Versionen werden von Spring Boot BOM verwaltet
// Logback-Abhängigkeiten - Versionen werden von Spring Boot BOM verwaltet
implementation("ch.qos.logback:logback-classic")
implementation("ch.qos.logback:logback-core")
implementation("org.slf4j:slf4j-api")
@ -60,7 +60,10 @@ dependencies {
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
testImplementation(libs.bundles.testing.jvm)
testImplementation(libs.logback.classic) // SLF4J provider for tests
// Ensure Logback dependencies are available in test classpath
testImplementation("ch.qos.logback:logback-classic")
testImplementation("ch.qos.logback:logback-core")
testImplementation("org.slf4j:slf4j-api")
// Redundante Security-Abhängigkeit im Testkontext entfernt (bereits durch platform-testing abgedeckt)
}

View File

@ -10,8 +10,6 @@ import org.springframework.http.server.reactive.ServerHttpResponse
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@ -43,7 +41,7 @@ class CorrelationIdFilter : GlobalFilter, Ordered {
.request(mutatedRequest)
.build()
// Add a response header after processing
// Response-Header nach der Verarbeitung hinzufügen
mutatedExchange.response.headers.add(CORRELATION_ID_HEADER, correlationId)
return chain.filter(mutatedExchange)
@ -177,7 +175,7 @@ class RateLimitingFilter : GlobalFilter, Ordered {
val limit = determineRateLimit(request, path)
val counter = requestCounts.computeIfAbsent(clientIp) { RequestCounter() }
// Reset counter if more than a minute has passed
// Zähler zurücksetzen, wenn mehr als eine Minute vergangen ist
val now = System.currentTimeMillis()
if (now - counter.lastReset > 60_000) {
counter.count = 0
@ -186,7 +184,7 @@ class RateLimitingFilter : GlobalFilter, Ordered {
counter.count++
// Add rate limit headers
// Rate-Limit-Header hinzufügen
response.headers.add(RATE_LIMIT_ENABLED_HEADER, "true")
response.headers.add(RATE_LIMIT_LIMIT_HEADER, limit.toString())
response.headers.add(RATE_LIMIT_REMAINING_HEADER, maxOf(0, limit - counter.count).toString())

View File

@ -114,6 +114,7 @@ class GatewayFiltersTests {
.uri("/test/ratelimit")
.header("Authorization", "Bearer test-token")
.header("X-User-Role", "ADMIN")
.header("X-User-ID", "admin-test-user") // Required for admin detection security
.exchange()
.expectStatus().isOk
.expectHeader().valueEquals("X-RateLimit-Limit", "500") // ADMIN_LIMIT

View File

@ -93,12 +93,13 @@ class JwtAuthenticationTests {
.expectStatus().isUnauthorized
.expectBody()
.jsonPath("$.error").isEqualTo("UNAUTHORIZED")
.jsonPath("$.message").isEqualTo("Invalid JWT token")
.jsonPath("$.message").isEqualTo("Invalid JWT token format")
}
@Test
fun `should allow access with valid JWT token and inject user headers`() {
val validToken = "valid-jwt-token-with-user-data"
// Create a mock JWT token with proper format (header.payload.signature) and length >50 for USER role
val validToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTEyMyIsInJvbGUiOiJVU0VSIiwiaWF0IjoxNjAwMDAwMDAwfQ.mockSignatureForUserTokenThatIsLongEnoughForValidation"
webTestClient.get()
.uri("/api/members/protected")
@ -116,7 +117,8 @@ class JwtAuthenticationTests {
@Test
fun `should extract admin role from JWT token`() {
val adminToken = "valid-jwt-token-with-admin-data"
// Create a mock JWT token with proper format, length >100, and "admin" in the token for ADMIN role
val adminToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbi11c2VyLTEyMyIsInJvbGUiOiJBRE1JTiIsImFkbWluIjp0cnVlLCJpYXQiOjE2MDAwMDAwMDAsImV4cCI6MTYwMDAwMDAwMH0.mockSignatureForAdminTokenThatIsVeryLongEnoughToMeetTheRequiredLengthForAdminValidation"
webTestClient.get()
.uri("/api/members/protected")
@ -132,7 +134,8 @@ class JwtAuthenticationTests {
@Test
fun `should extract user role from JWT token`() {
val userToken = "valid-jwt-token-with-user-data"
// Create a mock JWT token with proper format and length >50 for USER role
val userToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTQ1NiIsInJvbGUiOiJVU0VSIiwiaWF0IjoxNjAwMDAwMDAwfQ.mockSignatureForUserRoleTokenThatIsLongEnoughForValidation"
webTestClient.get()
.uri("/api/members/protected")
@ -148,7 +151,8 @@ class JwtAuthenticationTests {
@Test
fun `should handle POST requests to protected endpoints`() {
val validToken = "valid-jwt-token-for-post"
// Create a mock JWT token with proper format and length >50 for USER role
val validToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTc4OSIsInJvbGUiOiJVU0VSIiwiaWF0IjoxNjAwMDAwMDAwfQ.mockSignatureForPostRequestTokenThatIsLongEnoughForValidation"
webTestClient.post()
.uri("/api/members/protected")

View File

@ -18,32 +18,32 @@ import org.springframework.web.bind.annotation.*
import io.swagger.v3.oas.annotations.responses.ApiResponse as SwaggerApiResponse
/**
* Simple no-op EventPublisher implementation for the controller.
* Einfache No-op EventPublisher Implementierung für den Controller.
*/
class NoOpEventPublisher : EventPublisher {
override suspend fun publishEvent(topic: String, key: String?, event: Any) {
// No-op implementation - events are not published in this simple version
// No-op Implementierung - Events werden in dieser einfachen Version nicht veröffentlicht
}
override suspend fun publishEvents(topic: String, events: List<Pair<String?, Any>>) {
// No-op implementation - events are not published in this simple version
// No-op Implementierung - Events werden in dieser einfachen Version nicht veröffentlicht
}
}
/**
* REST API controller for member management operations.
* REST API Controller für Mitgliederverwaltungs-Operationen.
*
* This controller provides HTTP endpoints for all member-related operations
* including CRUD operations and member search functionality.
* Dieser Controller stellt HTTP-Endpunkte für alle mitgliederbezogenen Operationen
* zur Verfügung, einschließlich CRUD-Operationen und Mitgliedersuche.
*/
@RestController
@RequestMapping("/api/members")
@Tag(name = "Members", description = "Member management operations")
@Tag(name = "Members", description = "Mitgliederverwaltungs-Operationen")
class MemberController(
@Qualifier("memberRepositoryImpl") private val memberRepository: MemberRepository
) {
// Simple no-op EventPublisher implementation for now
// Einfache No-op EventPublisher Implementierung vorerst
private val eventPublisher = NoOpEventPublisher()
private val createMemberUseCase = CreateMemberUseCase(memberRepository, eventPublisher)
@ -55,7 +55,7 @@ class MemberController(
private val validateMemberDataUseCase = ValidateMemberDataUseCase(memberRepository)
/**
* Helper method to handle common response patterns for use case execution
* Hilfsmethode zur Behandlung gemeinsamer Antwortmuster für Use-Case-Ausführung
*/
private inline fun <T> handleUseCaseExecution(
crossinline operation: suspend () -> ApiResponse<T>,
@ -87,7 +87,7 @@ class MemberController(
}
/**
* Helper method to handle repository operations with common error handling
* Hilfsmethode zur Behandlung von Repository-Operationen mit gemeinsamer Fehlerbehandlung
*/
private inline fun <T> handleRepositoryOperation(
crossinline operation: () -> T,
@ -103,27 +103,27 @@ class MemberController(
}
/**
* Get all members with optional filtering
* Alle Mitglieder mit optionaler Filterung abrufen
*/
@Operation(
summary = "Get all members",
description = "Retrieve all members with optional filtering by active status and search term"
summary = "Alle Mitglieder abrufen",
description = "Abrufen aller Mitglieder mit optionaler Filterung nach Aktivitätsstatus und Suchbegriff"
)
@ApiResponses(
value = [
SwaggerApiResponse(responseCode = "200", description = "Successfully retrieved members"),
SwaggerApiResponse(responseCode = "500", description = "Internal server error")
SwaggerApiResponse(responseCode = "200", description = "Mitglieder erfolgreich abgerufen"),
SwaggerApiResponse(responseCode = "500", description = "Interner Serverfehler")
]
)
@GetMapping
fun getAllMembers(
@Parameter(description = "Filter by active members only", example = "true")
@Parameter(description = "Nur nach aktiven Mitgliedern filtern", example = "true")
@RequestParam(defaultValue = "true") activeOnly: Boolean,
@Parameter(description = "Maximum number of results to return", example = "100")
@Parameter(description = "Maximale Anzahl der zurückzugebenden Ergebnisse", example = "100")
@RequestParam(defaultValue = "100") limit: Int,
@Parameter(description = "Number of results to skip", example = "0")
@Parameter(description = "Anzahl der zu überspringenden Ergebnisse", example = "0")
@RequestParam(defaultValue = "0") offset: Int,
@Parameter(description = "Search term for member names")
@Parameter(description = "Suchbegriff für Mitgliedernamen")
@RequestParam(required = false) search: String?
): ResponseEntity<ApiResponse<List<*>>> {
return try {