feat: add Docker Compose dev environment with nginx reverse proxy
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:7
|
||||
ports:
|
||||
- "27017:27017"
|
||||
volumes:
|
||||
- mongo_data:/data/db
|
||||
healthcheck:
|
||||
test: echo 'db.runCommand("ping").ok' | mongosh --quiet
|
||||
interval: 10s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
backend:
|
||||
container_name: paragonplayground-backend
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "5000:8080"
|
||||
environment:
|
||||
- MongoDb__ConnectionString=mongodb://mongodb:27017
|
||||
- MongoDb__DatabaseName=paragon_playground
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://+:8080
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
frontend:
|
||||
container_name: paragonplayground-frontend
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- ./frontend/src:/app/src
|
||||
depends_on:
|
||||
- backend
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
- ./certs:/etc/nginx/certs
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
volumes:
|
||||
mongo_data:
|
||||
@@ -0,0 +1,42 @@
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name ~^(?<org_slug>[a-z0-9-]+)\.paragonplayground\.localhost$;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/_wildcard.paragonplayground.localhost.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/_wildcard.paragonplayground.localhost-key.pem;
|
||||
|
||||
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;
|
||||
proxy_set_header X-Organization-Slug $org_slug;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://frontend:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name ~^(?<org_slug>[a-z0-9-]+)\.paragonplayground\.localhost$;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name paragonplayground.localhost;
|
||||
|
||||
location / {
|
||||
return 400 "Missing organization subdomain. Use https://<org-slug>.paragonplayground.localhost";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the .NET solution.
|
||||
.PARAMETER Configuration
|
||||
Build configuration (Debug | Release). Defaults to Debug.
|
||||
#>
|
||||
param(
|
||||
[ValidateSet('Debug', 'Release')]
|
||||
[string]$Configuration = 'Debug'
|
||||
)
|
||||
|
||||
$repoRoot = Join-Path $PSScriptRoot '..'
|
||||
$sln = Join-Path $repoRoot 'backend\ParagonPlayground.slnx'
|
||||
|
||||
if (-not (Test-Path $sln)) {
|
||||
Write-Host "Solution not found at $sln" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Building $Configuration..." -ForegroundColor Cyan
|
||||
dotnet build $sln --configuration $Configuration
|
||||
exit $LASTEXITCODE
|
||||
@@ -0,0 +1,22 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Cleans .NET build artifacts (bin/obj) for all projects in the solution.
|
||||
#>
|
||||
|
||||
$repoRoot = Join-Path $PSScriptRoot '..'
|
||||
$sln = Join-Path $repoRoot 'backend\ParagonPlayground.slnx'
|
||||
|
||||
if (-not (Test-Path $sln)) {
|
||||
Write-Host "Solution not found at $sln" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Running dotnet clean..." -ForegroundColor Cyan
|
||||
dotnet clean $sln --verbosity quiet
|
||||
|
||||
Write-Host "Removing bin/obj directories..." -ForegroundColor Cyan
|
||||
$dirs = Get-ChildItem -Path (Join-Path $repoRoot 'backend') -Include bin,obj -Recurse -Directory
|
||||
$dirs | Remove-Item -Recurse -Force
|
||||
|
||||
Write-Host "Cleaned $($dirs.Count) directories." -ForegroundColor Green
|
||||
exit $LASTEXITCODE
|
||||
@@ -0,0 +1,15 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Stops the development environment and removes containers/networks.
|
||||
#>
|
||||
|
||||
$composeFile = Join-Path $PSScriptRoot '..\docker-compose.dev.yml'
|
||||
|
||||
if (-not (Test-Path $composeFile)) {
|
||||
Write-Host "Compose file not found at $composeFile" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Stopping development environment..." -ForegroundColor Cyan
|
||||
docker compose -f $composeFile down
|
||||
exit $LASTEXITCODE
|
||||
@@ -0,0 +1,27 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Starts the development environment via Docker Compose.
|
||||
.PARAMETER Detached
|
||||
Run containers in the background. Defaults to true.
|
||||
#>
|
||||
param(
|
||||
[switch]$Detached = $true
|
||||
)
|
||||
|
||||
$composeFile = Join-Path $PSScriptRoot '..\docker-compose.dev.yml'
|
||||
|
||||
if (-not (Test-Path $composeFile)) {
|
||||
Write-Host "Compose file not found at $composeFile" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Starting development environment..." -ForegroundColor Cyan
|
||||
$detachArg = if ($Detached) { '-d' } else { '' }
|
||||
docker compose -f $composeFile up --build $detachArg
|
||||
|
||||
if ($LASTEXITCODE -eq 0 -and $Detached) {
|
||||
Write-Host ""
|
||||
Write-Host "All services started. Run 'scripts/dev-down.ps1' to stop." -ForegroundColor Green
|
||||
Write-Host "Open https://acme.paragonplayground.localhost in your browser." -ForegroundColor Green
|
||||
}
|
||||
exit $LASTEXITCODE
|
||||
@@ -0,0 +1,26 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Restores .NET packages and installs frontend npm dependencies.
|
||||
#>
|
||||
|
||||
$projectRoot = Join-Path $PSScriptRoot '..'
|
||||
$backendDir = Join-Path $projectRoot 'backend'
|
||||
$frontendDir = Join-Path $projectRoot 'frontend'
|
||||
$sln = Join-Path $backendDir 'ParagonPlayground.slnx'
|
||||
|
||||
Write-Host "Restoring .NET packages..." -ForegroundColor Cyan
|
||||
if (Test-Path $sln) {
|
||||
dotnet restore $sln
|
||||
} else {
|
||||
dotnet restore $backendDir
|
||||
}
|
||||
|
||||
Write-Host "`nInstalling frontend npm dependencies..." -ForegroundColor Cyan
|
||||
if (Test-Path $frontendDir) {
|
||||
Push-Location $frontendDir
|
||||
npm install
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
Write-Host "`nAll dependencies installed." -ForegroundColor Green
|
||||
exit $LASTEXITCODE
|
||||
@@ -0,0 +1,65 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generates local TLS certificates for *.paragonplayground.localhost using mkcert.
|
||||
|
||||
.DESCRIPTION
|
||||
Uses mkcert (https://github.com/FiloSottile/mkcert) to create a local
|
||||
Certificate Authority and sign a wildcard certificate for local development.
|
||||
The CA is automatically trusted by the OS and browsers (no security warnings).
|
||||
|
||||
If mkcert is not installed, the script will offer to install it via winget.
|
||||
|
||||
.NOTES
|
||||
Run this script from an elevated (Admin) PowerShell prompt so mkcert can
|
||||
install the CA into the JDK system trust store (cacerts). Without elevation
|
||||
the CA is still trusted by the OS and browsers, but Java tools may reject
|
||||
the certificate.
|
||||
#>
|
||||
|
||||
$certsDir = Join-Path $PSScriptRoot "..\certs"
|
||||
|
||||
if (-not (Test-Path $certsDir)) {
|
||||
New-Item -ItemType Directory -Path $certsDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Check if mkcert is available
|
||||
$mkcert = Get-Command mkcert -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $mkcert) {
|
||||
Write-Host "mkcert not found." -ForegroundColor Yellow
|
||||
$install = Read-Host "Install mkcert via winget? (y/n)"
|
||||
|
||||
if ($install -eq "y") {
|
||||
winget install FiloSottile.mkcert
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Installation failed. Install manually: https://github.com/FiloSottile/mkcert" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$env:Path = [Environment]::GetEnvironmentVariable("Path", "User") + ";$env:Path"
|
||||
} else {
|
||||
Write-Host "Install mkcert manually from https://github.com/FiloSottile/mkcert and re-run." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Installing local CA (may prompt for admin)..." -ForegroundColor Cyan & mkcert -install
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Failed to install CA." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Generating certificate for *.paragonplayground.localhost..." -ForegroundColor Cyan & mkcert -cert-file "$certsDir/_wildcard.paragonplayground.localhost.pem" `
|
||||
-key-file "$certsDir/_wildcard.paragonplayground.localhost-key.pem" `
|
||||
"*.paragonplayground.localhost"
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "Certificates generated in $certsDir" -ForegroundColor Green
|
||||
Write-Host " cert: $certsDir\_wildcard.paragonplayground.localhost.pem" -ForegroundColor Green
|
||||
Write-Host " key: $certsDir\_wildcard.paragonplayground.localhost-key.pem" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "Certificate generation failed." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Runs .NET tests for the solution.
|
||||
.PARAMETER Configuration
|
||||
Build configuration (Debug | Release). Defaults to Debug.
|
||||
.PARAMETER NoBuild
|
||||
Skip build step. Defaults to false.
|
||||
#>
|
||||
param(
|
||||
[ValidateSet('Debug', 'Release')]
|
||||
[string]$Configuration = 'Debug',
|
||||
[switch]$NoBuild
|
||||
)
|
||||
|
||||
$repoRoot = Join-Path $PSScriptRoot '..'
|
||||
$sln = Join-Path $repoRoot 'backend\ParagonPlayground.slnx'
|
||||
|
||||
if (-not (Test-Path $sln)) {
|
||||
Write-Host "Solution not found at $sln" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$noBuildArg = if ($NoBuild) { '--no-build' } else { '' }
|
||||
|
||||
Write-Host "Running tests ($Configuration)..." -ForegroundColor Cyan
|
||||
dotnet test $sln --configuration $Configuration $noBuildArg --verbosity normal
|
||||
exit $LASTEXITCODE
|
||||
Reference in New Issue
Block a user