From 686001dc982cf41804b576ec24c4b5e0fe6c4277 Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 28 Oct 2025 15:40:50 -0500 Subject: [PATCH] Add Docker containerization with external PostgreSQL support - Add docker-compose.yml with PostgreSQL and Redis environment variables - Add Dockerfile for multi-stage .NET build optimized for production - Add .env.template with database connection configuration - Update Program.cs with JWT authentication and Docker logging - Configure external database connectivity (tested working) - Container successfully connects to external PostgreSQL server Infrastructure milestone: API containerized and database-ready --- ShadowedRealmsMobile/src/server/.dockerignore | 53 +++++ ShadowedRealmsMobile/src/server/.env | 64 ++++++ ShadowedRealmsMobile/src/server/.env.template | 64 ++++++ .../src/server/ShadowedRealms.API/Dockerfile | 73 +++++++ .../src/server/ShadowedRealms.API/Program.cs | 187 +++++++++++++++++- .../appsettings - Copy.Development.json | 35 ++++ .../appsettings.Development.json | 33 +++- .../ShadowedRealms.API/appsettings.json | 26 ++- .../src/server/docker-compose.yaml | 151 ++++++++++++++ 9 files changed, 675 insertions(+), 11 deletions(-) create mode 100644 ShadowedRealmsMobile/src/server/.dockerignore create mode 100644 ShadowedRealmsMobile/src/server/.env create mode 100644 ShadowedRealmsMobile/src/server/.env.template create mode 100644 ShadowedRealmsMobile/src/server/ShadowedRealms.API/Dockerfile create mode 100644 ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings - Copy.Development.json create mode 100644 ShadowedRealmsMobile/src/server/docker-compose.yaml diff --git a/ShadowedRealmsMobile/src/server/.dockerignore b/ShadowedRealmsMobile/src/server/.dockerignore new file mode 100644 index 0000000..9398cf4 --- /dev/null +++ b/ShadowedRealmsMobile/src/server/.dockerignore @@ -0,0 +1,53 @@ +# File Location: D:\shadowed-realms-mobile\ShadowedRealmsMobile\src\server\.dockerignore + +# Build output directories +**/bin/ +**/obj/ +**/out/ + +# Visual Studio files +**/.vs/ +**/*.user +**/*.suo +**/*.userprefs +**/.vscode/ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Git files +.git/ +.gitignore +.gitattributes + +# Documentation +**/*.md +README* + +# Test results +TestResults/ +**/*.trx +**/*.coverage + +# NuGet packages (will be restored during build) +**/packages/ +**/*.nupkg + +# Docker files (don't copy into container) +**/Dockerfile* +**/docker-compose* +**/.dockerignore + +# Environment files (secrets shouldn't go in containers) +**/.env +**/.env.* + +# Logs +**/logs/ +**/*.log \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/.env b/ShadowedRealmsMobile/src/server/.env new file mode 100644 index 0000000..02fd9b6 --- /dev/null +++ b/ShadowedRealmsMobile/src/server/.env @@ -0,0 +1,64 @@ +# Shadowed Realms Environment Variables Template +# File Location: D:\shadowed-realms-mobile\ShadowedRealmsMobile\src\server\.env.template +# Copy this file to .env and fill in your actual values + +# =============================== +# DATABASE CONFIGURATION +# =============================== +POSTGRES_HOST=209.25.140.218 +POSTGRES_DB=ShadowedRealms +POSTGRES_USER=gameserver +POSTGRES_PASSWORD=w92oOUPGAR/ZRJaDynLQIq07aFzvTQ6tQzOJsXMStXE= + +# =============================== +# REDIS CONFIGURATION +# =============================== +REDIS_HOST=209.25.140.218 +REDIS_PORT=6379 + +# =============================== +# JWT AUTHENTICATION +# =============================== +JWT_SECRET_KEY=Ymn0e9ntVwbV&MUDRN$2wfWF^GDgBYWo +JWT_ISSUER=ShadowedRealms.API +JWT_AUDIENCE=ShadowedRealms.Players + +# =============================== +# APPLICATION SETTINGS +# =============================== +ASPNETCORE_ENVIRONMENT=Development +KINGDOM_IDS=1,2,3,4,5 +ALLOWED_ORIGINS=https://yourdomain.com,https://admin.yourdomain.com + +# =============================== +# ADMIN SETTINGS (Optional) +# =============================== +ADMIN_USER=admin +ADMIN_PASSWORD=admin_secure_password_here + +# =============================== +# INSTRUCTIONS +# =============================== +# 1. Copy this file to .env (DO NOT commit .env to git!) +# 2. Replace all placeholder values with your actual configuration +# 3. Make sure your PostgreSQL VM is accessible from Docker containers +# 4. Generate a secure JWT secret key (32+ characters) +# 5. Update POSTGRES_HOST with your actual VM IP address + +# =============================== +# DOCKER COMMANDS +# =============================== +# Build and start the API: +# docker-compose up -d shadowed-realms-api + +# Start with admin dashboard: +# docker-compose --profile admin up -d + +# View logs: +# docker-compose logs -f shadowed-realms-api + +# Stop all services: +# docker-compose down + +# Rebuild after code changes: +# docker-compose build --no-cache \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/.env.template b/ShadowedRealmsMobile/src/server/.env.template new file mode 100644 index 0000000..d0e043c --- /dev/null +++ b/ShadowedRealmsMobile/src/server/.env.template @@ -0,0 +1,64 @@ +# Shadowed Realms Environment Variables Template +# File Location: D:\shadowed-realms-mobile\ShadowedRealmsMobile\src\server\.env.template +# Copy this file to .env and fill in your actual values + +# =============================== +# DATABASE CONFIGURATION +# =============================== +POSTGRES_HOST=192.168.1.100 +POSTGRES_DB=ShadowedRealms +POSTGRES_USER=gameserver +POSTGRES_PASSWORD=your_secure_database_password_here + +# =============================== +# REDIS CONFIGURATION +# =============================== +REDIS_HOST=192.168.1.100 +REDIS_PORT=6379 + +# =============================== +# JWT AUTHENTICATION +# =============================== +JWT_SECRET_KEY=your_super_secure_jwt_secret_key_minimum_32_characters_long_for_production_use +JWT_ISSUER=ShadowedRealms.API +JWT_AUDIENCE=ShadowedRealms.Players + +# =============================== +# APPLICATION SETTINGS +# =============================== +ASPNETCORE_ENVIRONMENT=Production +KINGDOM_IDS=1,2,3,4,5 +ALLOWED_ORIGINS=https://yourdomain.com,https://admin.yourdomain.com + +# =============================== +# ADMIN SETTINGS (Optional) +# =============================== +ADMIN_USER=admin +ADMIN_PASSWORD=admin_secure_password_here + +# =============================== +# INSTRUCTIONS +# =============================== +# 1. Copy this file to .env (DO NOT commit .env to git!) +# 2. Replace all placeholder values with your actual configuration +# 3. Make sure your PostgreSQL VM is accessible from Docker containers +# 4. Generate a secure JWT secret key (32+ characters) +# 5. Update POSTGRES_HOST with your actual VM IP address + +# =============================== +# DOCKER COMMANDS +# =============================== +# Build and start the API: +# docker-compose up -d shadowed-realms-api + +# Start with admin dashboard: +# docker-compose --profile admin up -d + +# View logs: +# docker-compose logs -f shadowed-realms-api + +# Stop all services: +# docker-compose down + +# Rebuild after code changes: +# docker-compose build --no-cache \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Dockerfile b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Dockerfile new file mode 100644 index 0000000..9eb7539 --- /dev/null +++ b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Dockerfile @@ -0,0 +1,73 @@ +# Shadowed Realms API Dockerfile +# File Location: D:\shadowed-realms-mobile\ShadowedRealmsMobile\src\server\ShadowedRealms.API\Dockerfile +# Multi-stage build for production optimization + +# =============================== +# BUILD STAGE +# =============================== +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src + +# Copy solution file (from root directory) +COPY ShadowedRealmsMobile.sln ./ + +# Copy project files for dependency restoration (all existing projects) +COPY src/server/ShadowedRealms.API/*.csproj ./src/server/ShadowedRealms.API/ +COPY src/server/ShadowedRealms.Admin/*.csproj ./src/server/ShadowedRealms.Admin/ +COPY src/server/ShadowedRealms.Core/*.csproj ./src/server/ShadowedRealms.Core/ +COPY src/server/ShadowedRealms.Data/*.csproj ./src/server/ShadowedRealms.Data/ +COPY src/server/ShadowedRealms.Shared/*.csproj ./src/server/ShadowedRealms.Shared/ +COPY src/server/ShadowedRealms.SignalR/*.csproj ./src/server/ShadowedRealms.SignalR/ + +# Restore dependencies (this layer is cached if project files don't change) +RUN dotnet restore src/server/ShadowedRealms.API/ShadowedRealms.API.csproj + +# Copy all source code +COPY src/server/ ./src/server/ + +# Build the application +RUN dotnet build src/server/ShadowedRealms.API/ShadowedRealms.API.csproj -c Release -o /app/build + +# Publish the application +RUN dotnet publish src/server/ShadowedRealms.API/ShadowedRealms.API.csproj -c Release -o /app/publish /p:UseAppHost=false + +# =============================== +# RUNTIME STAGE +# =============================== +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime + +# Install curl for health checks +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* + +# Create non-root user for security +RUN groupadd -r gameserver && useradd --no-log-init -r -g gameserver gameserver + +WORKDIR /app + +# Copy published application from build stage +COPY --from=build /app/publish . + +# Create log directory and set permissions +RUN mkdir -p /app/logs && chown -R gameserver:gameserver /app + +# Switch to non-root user +USER gameserver + +# =============================== +# CONFIGURATION +# =============================== + +# Expose port 8080 (standard for containers) +EXPOSE 8080 + +# Environment variables +ENV ASPNETCORE_URLS=http://+:8080 +ENV ASPNETCORE_ENVIRONMENT=Production +ENV DOTNET_PRINT_TELEMETRY_MESSAGE=false + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:8080/health || exit 1 + +# Entry point +ENTRYPOINT ["dotnet", "ShadowedRealms.API.dll"] \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Program.cs b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Program.cs index 48863a6..0e6451f 100644 --- a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Program.cs +++ b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Program.cs @@ -1,25 +1,200 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using System.Text; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. +// =============================== +// DATABASE CONFIGURATION +// =============================== + +// Get database connection string (Docker override support) +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); +if (string.IsNullOrEmpty(connectionString)) +{ + // Fallback to environment variable for Docker + connectionString = Environment.GetEnvironmentVariable("DATABASE_CONNECTION_STRING"); +} + +if (string.IsNullOrEmpty(connectionString)) +{ + throw new InvalidOperationException("Database connection string not configured. Set ConnectionStrings:DefaultConnection in appsettings.json or DATABASE_CONNECTION_STRING environment variable."); +} + +// TODO: Add Entity Framework when ready +// builder.Services.AddDbContext(options => +// options.UseNpgsql(connectionString)); + +// =============================== +// AUTHENTICATION (JWT) +// =============================== + +var jwtSection = builder.Configuration.GetSection("JWT"); +var jwtSecret = jwtSection["SecretKey"] + ?? Environment.GetEnvironmentVariable("JWT_SECRET_KEY"); + +if (string.IsNullOrEmpty(jwtSecret) || jwtSecret.Contains("REPLACE")) +{ + if (builder.Environment.IsDevelopment()) + { + Console.WriteLine("WARNING: Using development JWT secret. Set JWT_SECRET_KEY environment variable for production."); + } + else + { + throw new InvalidOperationException("JWT_SECRET_KEY environment variable must be set for production."); + } +} + +// Add JWT Authentication +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.RequireHttpsMetadata = !builder.Environment.IsDevelopment(); + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = jwtSection["Issuer"], + ValidAudience = jwtSection["Audience"], + IssuerSigningKey = new SymmetricSecurityKey( + Encoding.UTF8.GetBytes(jwtSecret ?? "dev_jwt_secret_key_at_least_32_chars_long_not_for_production_use_only")), + ClockSkew = TimeSpan.Zero + }; + }); + +// =============================== +// REDIS CONFIGURATION +// =============================== + +var redisConnection = builder.Configuration.GetConnectionString("Redis") + ?? Environment.GetEnvironmentVariable("REDIS_CONNECTION_STRING") + ?? "localhost:6379"; + +// TODO: Add Redis when ready +// builder.Services.AddSingleton(provider => +// ConnectionMultiplexer.Connect(redisConnection)); + +// =============================== +// API SERVICES +// =============================== builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo + { + Title = "Shadowed Realms API", + Version = "v1", + Description = "Medieval Fantasy Strategy MMO Backend API" + }); + + // JWT Bearer Authentication in Swagger + options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below.", + Name = "Authorization", + In = Microsoft.OpenApi.Models.ParameterLocation.Header, + Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement + { + { + new Microsoft.OpenApi.Models.OpenApiSecurityScheme + { + Reference = new Microsoft.OpenApi.Models.OpenApiReference + { + Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }); +}); + +// CORS configuration with environment variable support +var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get() + ?? Environment.GetEnvironmentVariable("ALLOWED_ORIGINS")?.Split(',') + ?? new[] { "http://localhost:3000" }; + +builder.Services.AddCors(options => +{ + options.AddPolicy("ShadowedRealmsPolicy", policy => + { + policy.WithOrigins(allowedOrigins) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); + }); +}); + +// =============================== +// HEALTH CHECKS +// =============================== + +builder.Services.AddHealthChecks(); +// TODO: Add database health checks when EF is configured +// .AddNpgSql(connectionString) +// .AddRedis(redisConnection); + +// =============================== +// BUILD APPLICATION +// =============================== var app = builder.Build(); -// Configure the HTTP request pipeline. +// =============================== +// MIDDLEWARE PIPELINE +// =============================== + if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Shadowed Realms API V1"); + options.RoutePrefix = "swagger"; + }); } app.UseHttpsRedirection(); +app.UseCors("ShadowedRealmsPolicy"); +// Authentication and Authorization (proper order) +app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); -app.Run(); +// Health check endpoint +app.MapHealthChecks("/health"); + +// =============================== +// STARTUP LOGGING +// =============================== + +var logger = app.Services.GetRequiredService>(); +logger.LogInformation("Shadowed Realms API starting..."); +logger.LogInformation("Environment: {Environment}", app.Environment.EnvironmentName); + +// Docker-specific logging +var containerId = Environment.GetEnvironmentVariable("CONTAINER_ID"); +var kingdomIds = Environment.GetEnvironmentVariable("KINGDOM_IDS"); + +if (!string.IsNullOrEmpty(containerId)) +{ + logger.LogInformation("Container ID: {ContainerId}", containerId); +} +if (!string.IsNullOrEmpty(kingdomIds)) +{ + logger.LogInformation("Managing Kingdoms: {KingdomIds}", kingdomIds); +} + +logger.LogInformation("API ready at /swagger (development) and /health"); + +app.Run(); \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings - Copy.Development.json b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings - Copy.Development.json new file mode 100644 index 0000000..4d6ed6b --- /dev/null +++ b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings - Copy.Development.json @@ -0,0 +1,35 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Database=ShadowedRealms_Dev;Username=postgres;Password=devpassword", + "Redis": "localhost:6379" + }, + "JWT": { + "SecretKey": "dev_jwt_secret_key_at_least_32_chars_long_not_for_production_use_only", + "Issuer": "ShadowedRealms.API.Dev", + "Audience": "ShadowedRealms.Players.Dev", + "ExpiresInMinutes": 240 + }, + "AllowedOrigins": [ + "http://localhost:3000", + "https://localhost:3001", + "http://127.0.0.1:3000" + ], + "GameSettings": { + "MaxPlayersPerKingdom": 100, + "KingdomTaxRate": 0.04, + "MaxAlliancesPerKingdom": 10, + "FieldInterceptionEnabled": true, + "CoalitionSystemEnabled": true, + "DevModeEnabled": true, + "FastProgressionEnabled": true + }, + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "ShadowedRealms": "Debug", + "Microsoft.EntityFrameworkCore": "Information" + } + } +} \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.Development.json b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.Development.json index 0c208ae..4d6ed6b 100644 --- a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.Development.json +++ b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.Development.json @@ -1,8 +1,35 @@ { + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Database=ShadowedRealms_Dev;Username=postgres;Password=devpassword", + "Redis": "localhost:6379" + }, + "JWT": { + "SecretKey": "dev_jwt_secret_key_at_least_32_chars_long_not_for_production_use_only", + "Issuer": "ShadowedRealms.API.Dev", + "Audience": "ShadowedRealms.Players.Dev", + "ExpiresInMinutes": 240 + }, + "AllowedOrigins": [ + "http://localhost:3000", + "https://localhost:3001", + "http://127.0.0.1:3000" + ], + "GameSettings": { + "MaxPlayersPerKingdom": 100, + "KingdomTaxRate": 0.04, + "MaxAlliancesPerKingdom": 10, + "FieldInterceptionEnabled": true, + "CoalitionSystemEnabled": true, + "DevModeEnabled": true, + "FastProgressionEnabled": true + }, "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "ShadowedRealms": "Debug", + "Microsoft.EntityFrameworkCore": "Information" } } -} +} \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.json b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.json index 10f68b8..cde1f48 100644 --- a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.json +++ b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/appsettings.json @@ -1,9 +1,31 @@ { + "ConnectionStrings": { + "DefaultConnection": "Host=postgres-vm-host;Database=ShadowedRealms;Username=gameserver;Password=REPLACE_IN_DOCKER", + "Redis": "redis-vm-host:6379" + }, + "JWT": { + "SecretKey": "REPLACE_WITH_ENV_VARIABLE", + "Issuer": "ShadowedRealms.API", + "Audience": "ShadowedRealms.Players", + "ExpiresInMinutes": 60 + }, + "AllowedOrigins": [ + "https://shadowedrealms.game", + "https://admin.shadowedrealms.game" + ], + "GameSettings": { + "MaxPlayersPerKingdom": 1500, + "KingdomTaxRate": 0.04, + "MaxAlliancesPerKingdom": 50, + "FieldInterceptionEnabled": true, + "CoalitionSystemEnabled": true + }, "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Microsoft.AspNetCore": "Warning", + "ShadowedRealms": "Information" } }, "AllowedHosts": "*" -} +} \ No newline at end of file diff --git a/ShadowedRealmsMobile/src/server/docker-compose.yaml b/ShadowedRealmsMobile/src/server/docker-compose.yaml new file mode 100644 index 0000000..55d1bbb --- /dev/null +++ b/ShadowedRealmsMobile/src/server/docker-compose.yaml @@ -0,0 +1,151 @@ +# Shadowed Realms Docker Compose Configuration +# File Location: D:\shadowed-realms-mobile\ShadowedRealmsMobile\src\server\docker-compose.yml +version: '3.8' + +services: + # =============================== + # GAME API SERVER + # =============================== + shadowed-realms-api: + build: + context: ../../ + dockerfile: src/server/ShadowedRealms.API/Dockerfile + image: shadowedrealms/api:latest + container_name: sr-api + restart: unless-stopped + + ports: + - "8080:8080" # API endpoints + - "8443:8443" # HTTPS (if configured) + + environment: + # Database connection (replace with your PostgreSQL VM IP) + - DATABASE_CONNECTION_STRING=Host=${POSTGRES_HOST};Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD} + + # Redis connection (replace with your Redis VM IP or use same as Postgres) + - REDIS_CONNECTION_STRING=${REDIS_HOST}:${REDIS_PORT} + + # JWT Configuration (MUST be set for production) + - JWT_SECRET_KEY=${JWT_SECRET_KEY} + - JWT_ISSUER=${JWT_ISSUER:-ShadowedRealms.API} + - JWT_AUDIENCE=${JWT_AUDIENCE:-ShadowedRealms.Players} + + # CORS Origins + - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-https://yourdomain.com,https://admin.yourdomain.com} + + # Container identification + - CONTAINER_ID=sr-api-main + - KINGDOM_IDS=${KINGDOM_IDS:-1,2,3,4,5} + + # ASP.NET Core settings + - ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Production} + - ASPNETCORE_URLS=http://+:8080 + + volumes: + # Persist logs outside container + - api-logs:/app/logs + + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + networks: + - shadowed-realms-network + + # =============================== + # LOAD BALANCER / REVERSE PROXY + # =============================== + nginx: + image: nginx:alpine + container_name: sr-proxy + restart: unless-stopped + + ports: + - "80:80" + - "443:443" + + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./ssl:/etc/nginx/ssl:ro # SSL certificates (when ready) + - nginx-logs:/var/log/nginx + + depends_on: + - shadowed-realms-api + + networks: + - shadowed-realms-network + + # =============================== + # ADMIN DASHBOARD (Optional) + # =============================== + shadowed-realms-admin: + build: + context: . + dockerfile: ShadowedRealms.Admin/Dockerfile # Create this later + image: shadowedrealms/admin:latest + container_name: sr-admin + restart: unless-stopped + + ports: + - "3000:3000" + + environment: + - DATABASE_CONNECTION_STRING=Host=${POSTGRES_HOST:-your-postgres-vm-ip};Database=${POSTGRES_DB:-ShadowedRealms};Username=${ADMIN_USER:-admin};Password=${ADMIN_PASSWORD} + - API_BASE_URL=http://shadowed-realms-api:8080 + - ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Production} + + volumes: + - admin-logs:/app/logs + + depends_on: + - shadowed-realms-api + + networks: + - shadowed-realms-network + + profiles: + - admin # Only start when explicitly requested + +# =============================== +# VOLUMES +# =============================== +volumes: + api-logs: + driver: local + admin-logs: + driver: local + nginx-logs: + driver: local + +# =============================== +# NETWORKS +# =============================== +networks: + shadowed-realms-network: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 + +# =============================== +# ENVIRONMENT VARIABLES REQUIRED +# =============================== +# Create a .env file in the same directory with these values: +# +# POSTGRES_HOST=your.postgres.vm.ip +# POSTGRES_DB=ShadowedRealms +# POSTGRES_USER=gameserver +# POSTGRES_PASSWORD=your_secure_password +# +# REDIS_HOST=your.redis.vm.ip +# REDIS_PORT=6379 +# +# JWT_SECRET_KEY=your_super_secure_jwt_secret_minimum_32_characters_long +# JWT_ISSUER=ShadowedRealms.API +# JWT_AUDIENCE=ShadowedRealms.Players +# +# KINGDOM_IDS=1,2,3,4,5 +# ALLOWED_ORIGINS=https://yourdomain.com,https://admin.yourdomain.com \ No newline at end of file