feature/user-authentication #2

Merged
lasserh merged 31 commits from feature/user-authentication into main 2026-06-07 13:17:55 +02:00
7 changed files with 318 additions and 0 deletions
Showing only changes of commit 79a59bccc4 - Show all commits

40
.dockerignore Normal file
View file

@ -0,0 +1,40 @@
# Root .dockerignore for the entire solution
# Git
.git/
.gitignore
# Docker files
Dockerfile
docker-compose*
.dockerignore
# IDE
.idea/
.vs/
.vscode/
*.suo
*.user
# OS
.DS_Store
Thumbs.db
# Build output
**/bin/
**/obj/
**/dist/
# Node modules
**/node_modules/
# Logs
*.log
# Test results
**/TestResults/
# Secrets
**/appsettings.Development.json
**/secrets.json
**/.env*

31
GermanApp/.dockerignore Normal file
View file

@ -0,0 +1,31 @@
# .NET Core
**/bin/
**/obj/
# User secrets
**/appsettings.Development.json
**/secrets.json
# NuGet packages
**/packages/
# IDE
.idea/
.vs/
*.user
*.suo
# Git
.git/
.gitignore
# Docker
Dockerfile
.dockerignore
# OS
.DS_Store
Thumbs.db
# Test results
**/TestResults/

54
GermanApp/Dockerfile Normal file
View file

@ -0,0 +1,54 @@
# GermanApp Backend Dockerfile
# .NET 9.0 Web API Application
# Multi-stage build for production optimization
# ============================================
# Build Stage
# ============================================
FROM mcr.microsoft.com/dotnet/sdk:9.0.204-alpine3.19 AS build
WORKDIR /src
# Copy project file and restore dependencies
COPY ["GermanApp/GermanApp.csproj", "GermanApp/"]
RUN dotnet restore "GermanApp/GermanApp.csproj"
# Copy everything else and build
COPY . .
WORKDIR "/src/GermanApp"
RUN dotnet build "GermanApp.csproj" -c Release -o /app/build
# Publish the application
RUN dotnet publish "GermanApp.csproj" -c Release -o /app/publish \
--no-restore \
-p:PublishReadyToRun=true \
-p:PublishSingleFile=false \
-p:PublishTrimmed=true
# ============================================
# Publish Stage
# ============================================
FROM build AS publish
# ============================================
# Runtime Stage
# ============================================
FROM mcr.microsoft.com/dotnet/aspnet:9.0.4-alpine3.19 AS runtime
WORKDIR /app
# Copy published app from publish stage
COPY --from=publish /app/publish .
# Set environment variables
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Entry point
ENTRYPOINT ["dotnet", "GermanApp.dll"]

68
docker-compose.yml Normal file
View file

@ -0,0 +1,68 @@
version: '3.8'
services:
# PostgreSQL Database
db:
image: postgres:15-alpine
container_name: deutschlernen-db
environment:
POSTGRES_DB: DeutschLernen
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d DeutschLernen"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Backend API
backend:
build:
context: ./GermanApp
dockerfile: Dockerfile
container_name: deutschlernen-backend
environment:
ASPNETCORE_ENVIRONMENT: Development
ASPNETCORE_URLS: http://+:8080
ConnectionStrings__DefaultConnection: Host=db;Port=5432;Database=DeutschLernen;Username=postgres;Password=postgres
depends_on:
db:
condition: service_healthy
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
restart: unless-stopped
# Frontend
frontend:
build:
context: ./german-app-frontend
dockerfile: Dockerfile
container_name: deutschlernen-frontend
environment:
NODE_ENV: production
depends_on:
backend:
condition: service_healthy
ports:
- "3000:3000"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
restart: unless-stopped
volumes:
postgres_data:

View file

@ -0,0 +1,41 @@
# Node modules
node_modules/
# npm cache
npm-cache/
# Dist directory (will be built in container)
dist/
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Git
.git/
.gitignore
# Docker
Dockerfile
.dockerignore
# Logs
*.log
npm-debug.log*
# Environment files
.env
.env.local
.env.*.local
# Build output
build/
# Test coverage
coverage/

View file

@ -0,0 +1,43 @@
# GermanApp Frontend Dockerfile
# React 19 + TypeScript + Vite Application
# Multi-stage build for production optimization
# ============================================
# Build Stage
# ============================================
FROM node:20-alpine AS build
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source files
COPY . .
# Build the application
RUN npm run build
# ============================================
# Runtime Stage
# ============================================
FROM nginx:alpine AS runtime
WORKDIR /usr/share/nginx/html
# Copy built files from build stage
COPY --from=build /app/dist .
# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
# Entry point (nginx runs by default)
CMD ["nginx", "-g", "daemon off;"]

View file

@ -0,0 +1,41 @@
server {
listen 3000;
server_name localhost;
# Root directory
root /usr/share/nginx/html;
index index.html;
# Handle React Router - return index.html for all requests
location / {
try_files $uri $uri/ /index.html;
}
# API proxy to backend (when running in Docker Compose)
location /api/ {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}