Skip to content

Commit a85279e

Browse files
authored
General: Dockerize application with full stack setup (#12)
* dockerize application with full stack setup, including PostgreSQL, Spring Boot server, and React client; add startup script and nginx configuration * update client port from 4173 to 5173 in README, docker-compose, and startup script * add startup scripts for automated Docker setup on macOS/Linux and Windows * remove Azure OpenAI configuration from server environment variables in docker-compose
1 parent d4348a0 commit a85279e

File tree

8 files changed

+441
-5
lines changed

8 files changed

+441
-5
lines changed

.dockerignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.git
2+
.github
3+
.gradle
4+
build/
5+
out/
6+
node_modules/
7+
src/main/webapp/node_modules/
8+
src/main/webapp/dist/
9+
*.iml
10+
.idea/
11+
Dockerfile
12+
docker-compose.yml
13+
docker/postgres_data/
14+
.DS_Store

README.md

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,56 @@ Harmonia is built as a Java/Spring-based monolith, prioritizing stability and jo
4545

4646
---
4747

48-
## 🐳 Docker Command for PostgreSQL
48+
## 🐳 Run Harmonia with Docker
4949

50-
Use this command to quickly set up a local instance of the PostgreSQL database required by the server.
50+
### Quick Start with Startup Scripts
51+
52+
Use the provided startup scripts for an automated setup:
53+
54+
**macOS/Linux:**
55+
```bash
56+
./start.sh
57+
```
58+
59+
**Windows (PowerShell):**
60+
```powershell
61+
.\start.ps1
62+
```
63+
64+
The scripts will:
65+
- Check if Docker is running
66+
- Clean up any existing containers
67+
- Build and start all services (database, server, client)
68+
- Wait for services to be ready
69+
- Display access URLs and helpful commands
70+
71+
### Full stack (database + server + client)
72+
73+
Alternatively, you can manually build and launch everything:
74+
75+
Alternatively, you can manually build and launch everything:
76+
77+
1. Build and launch:
5178

5279
```bash
53-
docker compose -f docker/docker-compose.yml up -d
80+
docker compose -f docker/docker-compose.yml up --build
5481
```
5582

83+
2. Access the services:
84+
* Client (served by nginx): http://localhost:5173
85+
* Spring Boot server: http://localhost:8080
86+
* PostgreSQL: localhost:5432 (user `postgres`, password `harmonia`)
87+
88+
The Compose setup builds the Gradle boot jar inside `docker/server.Dockerfile`, bundles the React client with Vite via `docker/client.Dockerfile`, and proxies `/api` + `/actuator` calls from nginx to the server container. All images restart automatically unless stopped.
89+
90+
### Database only
91+
92+
If you only need the database for local development you can start just the PostgreSQL service:
93+
94+
```bash
95+
docker compose -f docker/docker-compose.yml up -d postgres
96+
```
97+
5698
## 🚀 How to Run the Server
5799

58100
The application uses Gradle for dependency management and building. Assuming you have **Java 25** installed, use the
@@ -129,4 +171,4 @@ This will generate fully typed API clients and models in:
129171

130172
src/main/webapp/src/app/generated/
131173

132-
These files are overwritten each time you run the generator and should be committed to version control to keep client and server in sync.
174+
These files are overwritten each time you run the generator and should be committed to version control to keep client and server in sync.

docker/client.Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Build the React/Vite client
2+
FROM node:22-bookworm-slim AS builder
3+
WORKDIR /app
4+
5+
COPY src/main/webapp/package*.json ./
6+
RUN npm ci
7+
8+
COPY src/main/webapp .
9+
10+
ARG VITE_API_BASE_URL=http://localhost:8080
11+
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
12+
ENV NODE_ENV=production
13+
14+
RUN npm run build
15+
16+
# Serve the production build with nginx
17+
FROM nginx:1.27-alpine AS runner
18+
19+
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
20+
COPY --from=builder /app/dist /usr/share/nginx/html
21+
22+
EXPOSE 80
23+
24+
CMD ["nginx", "-g", "daemon off;"]

docker/docker-compose.yml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
version: '3.9'
1+
name: harmonia
2+
23
services:
34
postgres:
45
image: postgres:18.0
@@ -12,6 +13,41 @@ services:
1213
- "5432:5432"
1314
volumes:
1415
- postgres_data:/var/lib/postgresql
16+
healthcheck:
17+
test: ["CMD-SHELL", "pg_isready -U postgres"]
18+
interval: 10s
19+
timeout: 5s
20+
retries: 5
21+
22+
server:
23+
build:
24+
context: ..
25+
dockerfile: docker/server.Dockerfile
26+
container_name: harmonia-server
27+
restart: unless-stopped
28+
depends_on:
29+
postgres:
30+
condition: service_healthy
31+
environment:
32+
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/harmonia
33+
SPRING_DATASOURCE_USERNAME: postgres
34+
SPRING_DATASOURCE_PASSWORD: harmonia
35+
ports:
36+
- "8080:8080"
37+
38+
client:
39+
build:
40+
context: ..
41+
dockerfile: docker/client.Dockerfile
42+
args:
43+
VITE_API_BASE_URL: ${VITE_API_BASE_URL:-http://localhost:8080}
44+
container_name: harmonia-client
45+
restart: unless-stopped
46+
depends_on:
47+
server:
48+
condition: service_started
49+
ports:
50+
- "5173:80"
1551

1652
volumes:
1753
postgres_data:

docker/nginx.conf

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
server {
2+
listen 80;
3+
server_name _;
4+
5+
root /usr/share/nginx/html;
6+
index index.html;
7+
8+
gzip on;
9+
gzip_types text/plain application/xml text/css application/javascript application/json image/svg+xml;
10+
11+
location / {
12+
try_files $uri /index.html;
13+
}
14+
15+
location /api/ {
16+
proxy_pass http://harmonia-server:8080/api/;
17+
proxy_set_header Host $host;
18+
proxy_set_header X-Real-IP $remote_addr;
19+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
20+
proxy_set_header X-Forwarded-Proto $scheme;
21+
}
22+
23+
location /actuator/ {
24+
proxy_pass http://harmonia-server:8080/actuator/;
25+
proxy_set_header Host $host;
26+
proxy_set_header X-Real-IP $remote_addr;
27+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
28+
proxy_set_header X-Forwarded-Proto $scheme;
29+
}
30+
}

docker/server.Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Build the Spring Boot application
2+
FROM eclipse-temurin:25-jdk AS builder
3+
WORKDIR /app
4+
5+
COPY . .
6+
7+
RUN chmod +x gradlew
8+
RUN ./gradlew --no-daemon bootJar -x test \
9+
&& JAR_FILE=$(find build/libs -maxdepth 1 -type f -name "*.jar" ! -name "*-plain.jar" | head -n 1) \
10+
&& cp "${JAR_FILE}" /app/application.jar
11+
12+
# Run the packaged jar with a lightweight JRE
13+
FROM eclipse-temurin:25-jre
14+
WORKDIR /app
15+
16+
COPY --from=builder /app/application.jar app.jar
17+
18+
EXPOSE 8080
19+
20+
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

start.ps1

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Harmonia Startup Script (Windows PowerShell)
2+
# This script starts all services using Docker containers
3+
4+
$ErrorActionPreference = "Stop"
5+
6+
# Colors for output
7+
function Write-Green { Write-Host $args -ForegroundColor Green }
8+
function Write-Blue { Write-Host $args -ForegroundColor Blue }
9+
function Write-Yellow { Write-Host $args -ForegroundColor Yellow }
10+
function Write-Red { Write-Host $args -ForegroundColor Red }
11+
12+
Write-Blue "========================================"
13+
Write-Blue " Starting Harmonia Application"
14+
Write-Blue " (Containerized Version)"
15+
Write-Blue "========================================"
16+
Write-Host ""
17+
18+
# Step 1: Check Docker is running
19+
Write-Green "[1/4] Checking Docker..."
20+
try {
21+
docker info | Out-Null
22+
Write-Green "✓ Docker is running"
23+
Write-Host ""
24+
} catch {
25+
Write-Red "✗ Docker is not running. Please start Docker Desktop."
26+
exit 1
27+
}
28+
29+
# Step 2: Clean up any existing containers
30+
Write-Green "[2/4] Cleaning up existing containers..."
31+
docker compose -f docker/docker-compose.yml down --remove-orphans 2>$null
32+
Write-Green "✓ Cleanup completed"
33+
Write-Host ""
34+
35+
# Step 3: Build and start all services
36+
Write-Green "[3/4] Building and starting all services..."
37+
Write-Yellow "This may take several minutes for the first run..."
38+
Write-Yellow "Building Docker images and starting containers..."
39+
Write-Host ""
40+
41+
docker compose -f docker/docker-compose.yml up --build -d
42+
43+
if ($LASTEXITCODE -eq 0) {
44+
Write-Green "✓ All services started successfully"
45+
Write-Host ""
46+
} else {
47+
Write-Red "✗ Failed to start services"
48+
Write-Yellow "Check logs with: docker compose -f docker/docker-compose.yml logs"
49+
exit 1
50+
}
51+
52+
# Step 4: Wait for services to be ready
53+
Write-Green "[4/4] Waiting for services to be ready..."
54+
Write-Yellow "Checking service health..."
55+
56+
# Wait for postgres to be healthy
57+
Write-Yellow "• Waiting for PostgreSQL..."
58+
$timeout = 60
59+
$postgresReady = $false
60+
while ($timeout -gt 0) {
61+
$status = docker compose -f docker/docker-compose.yml ps postgres 2>$null | Select-String "healthy"
62+
if ($status) {
63+
Write-Green " ✓ PostgreSQL is ready"
64+
$postgresReady = $true
65+
break
66+
}
67+
Start-Sleep -Seconds 2
68+
$timeout -= 2
69+
}
70+
71+
if (-not $postgresReady) {
72+
Write-Red " ✗ PostgreSQL failed to start within 60 seconds"
73+
exit 1
74+
}
75+
76+
# Wait for server to be responding
77+
Write-Yellow "• Waiting for Spring Boot server..."
78+
$timeout = 120
79+
$serverReady = $false
80+
while ($timeout -gt 0) {
81+
try {
82+
$response = Invoke-WebRequest -Uri "http://localhost:8080/actuator/health" -UseBasicParsing -TimeoutSec 2 -ErrorAction SilentlyContinue
83+
if ($response.StatusCode -eq 200 -or $response.StatusCode -eq 401) {
84+
Write-Green " ✓ Server is ready"
85+
$serverReady = $true
86+
break
87+
}
88+
} catch {
89+
# Server not ready yet
90+
}
91+
Start-Sleep -Seconds 3
92+
$timeout -= 3
93+
}
94+
95+
if (-not $serverReady) {
96+
Write-Yellow " ⚠ Server may still be starting (timeout reached)"
97+
}
98+
99+
# Check if client is responding
100+
Write-Yellow "• Waiting for React client..."
101+
$timeout = 30
102+
$clientReady = $false
103+
while ($timeout -gt 0) {
104+
try {
105+
$response = Invoke-WebRequest -Uri "http://localhost:5173" -UseBasicParsing -TimeoutSec 2 -ErrorAction SilentlyContinue
106+
if ($response.StatusCode -eq 200) {
107+
Write-Green " ✓ Client is ready"
108+
$clientReady = $true
109+
break
110+
}
111+
} catch {
112+
# Client not ready yet
113+
}
114+
Start-Sleep -Seconds 2
115+
$timeout -= 2
116+
}
117+
118+
if (-not $clientReady) {
119+
Write-Yellow " ⚠ Client may still be starting (timeout reached)"
120+
}
121+
122+
# Summary
123+
Write-Host ""
124+
Write-Blue "========================================"
125+
Write-Green "✓ Harmonia is now running!"
126+
Write-Blue "========================================"
127+
Write-Yellow "Server:" -NoNewline
128+
Write-Host " http://localhost:8080"
129+
Write-Yellow "Client:" -NoNewline
130+
Write-Host " http://localhost:5173"
131+
Write-Yellow "Database:" -NoNewline
132+
Write-Host " PostgreSQL on localhost:5432"
133+
Write-Host ""
134+
Write-Yellow "Useful commands:"
135+
Write-Yellow "• View logs:" -NoNewline
136+
Write-Host " docker compose -f docker/docker-compose.yml logs -f"
137+
Write-Yellow "• Stop services:" -NoNewline
138+
Write-Host " docker compose -f docker/docker-compose.yml down"
139+
Write-Yellow "• Restart services:" -NoNewline
140+
Write-Host " docker compose -f docker/docker-compose.yml restart"
141+
Write-Host ""
142+
Write-Yellow "Press Ctrl+C to stop all services"
143+
Write-Host ""
144+
145+
# Keep script running and handle Ctrl+C
146+
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
147+
148+
# Cleanup on exit
149+
Write-Host ""
150+
Write-Red "Shutting down all services..."
151+
docker compose -f docker/docker-compose.yml down

0 commit comments

Comments
 (0)