Skip to content

Commit 2399455

Browse files
committed
dockerize application with full stack setup, including PostgreSQL, Spring Boot server, and React client; add startup script and nginx configuration
1 parent d4348a0 commit 2399455

File tree

7 files changed

+268
-5
lines changed

7 files changed

+268
-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: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,31 @@ 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+
### Full stack (database + server + client)
51+
52+
1. Build and launch everything:
5153

5254
```bash
53-
docker compose -f docker/docker-compose.yml up -d
55+
docker compose -f docker/docker-compose.yml up --build
5456
```
5557

58+
3. Access the services:
59+
* Client (served by nginx): http://localhost:4173
60+
* Spring Boot server: http://localhost:8080
61+
* PostgreSQL: localhost:5432 (user `postgres`, password `harmonia`)
62+
63+
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.
64+
65+
### Database only
66+
67+
If you only need the database for local development you can start just the PostgreSQL service:
68+
69+
```bash
70+
docker compose -f docker/docker-compose.yml up -d postgres
71+
```
72+
5673
## 🚀 How to Run the Server
5774

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

130147
src/main/webapp/src/app/generated/
131148

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.
149+
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: 40 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,44 @@ 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+
SPRING_AI_AZURE_OPENAI_API_KEY: ${SPRING_AI_AZURE_OPENAI_API_KEY:-change-me}
36+
SPRING_AI_AZURE_OPENAI_ENDPOINT: ${SPRING_AI_AZURE_OPENAI_ENDPOINT:-https://ase-se01.openai.azure.com/}
37+
SPRING_AI_AZURE_OPENAI_CHAT_OPTIONS_DEPLOYMENT_NAME: ${SPRING_AI_AZURE_OPENAI_CHAT_OPTIONS_DEPLOYMENT_NAME:-gpt-5-mini}
38+
ports:
39+
- "8080:8080"
40+
41+
client:
42+
build:
43+
context: ..
44+
dockerfile: docker/client.Dockerfile
45+
args:
46+
VITE_API_BASE_URL: ${VITE_API_BASE_URL:-http://localhost:8080}
47+
container_name: harmonia-client
48+
restart: unless-stopped
49+
depends_on:
50+
server:
51+
condition: service_started
52+
ports:
53+
- "4173:80"
1554

1655
volumes:
1756
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.sh

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/bin/bash
2+
3+
# Harmonia Startup Script
4+
# This script starts all services using Docker containers
5+
6+
set -e # Exit on error
7+
8+
# Colors for output
9+
GREEN='\033[0;32m'
10+
BLUE='\033[0;34m'
11+
YELLOW='\033[1;33m'
12+
RED='\033[0;31m'
13+
NC='\033[0m' # No Color
14+
15+
echo -e "${BLUE}========================================${NC}"
16+
echo -e "${BLUE} Starting Harmonia Application${NC}"
17+
echo -e "${BLUE} (Containerized Version)${NC}"
18+
echo -e "${BLUE}========================================${NC}\n"
19+
20+
# Step 1: Check Docker is running
21+
echo -e "${GREEN}[1/4] Checking Docker...${NC}"
22+
if ! docker info >/dev/null 2>&1; then
23+
echo -e "${RED}✗ Docker is not running. Please start Docker Desktop.${NC}"
24+
exit 1
25+
fi
26+
echo -e "${GREEN}✓ Docker is running${NC}\n"
27+
28+
# Step 2: Clean up any existing containers
29+
echo -e "${GREEN}[2/4] Cleaning up existing containers...${NC}"
30+
docker compose -f docker/docker-compose.yml down --remove-orphans 2>/dev/null || true
31+
echo -e "${GREEN}✓ Cleanup completed${NC}\n"
32+
33+
# Step 3: Build and start all services
34+
echo -e "${GREEN}[3/4] Building and starting all services...${NC}"
35+
echo -e "${YELLOW}This may take several minutes for the first run...${NC}"
36+
echo -e "${YELLOW}Building Docker images and starting containers...${NC}\n"
37+
38+
# Start services in background and capture output
39+
docker compose -f docker/docker-compose.yml up --build -d
40+
41+
if [ $? -eq 0 ]; then
42+
echo -e "${GREEN}✓ All services started successfully${NC}\n"
43+
else
44+
echo -e "${RED}✗ Failed to start services${NC}"
45+
echo -e "${YELLOW}Check logs with: docker compose -f docker/docker-compose.yml logs${NC}"
46+
exit 1
47+
fi
48+
49+
# Step 4: Wait for services to be ready
50+
echo -e "${GREEN}[4/4] Waiting for services to be ready...${NC}"
51+
echo -e "${YELLOW}Checking service health...${NC}"
52+
53+
# Wait for postgres to be healthy
54+
echo -e "${YELLOW}• Waiting for PostgreSQL...${NC}"
55+
timeout=60
56+
while [ $timeout -gt 0 ]; do
57+
if docker compose -f docker/docker-compose.yml ps postgres | grep -q "healthy"; then
58+
echo -e "${GREEN} ✓ PostgreSQL is ready${NC}"
59+
break
60+
fi
61+
sleep 2
62+
((timeout-=2))
63+
done
64+
65+
if [ $timeout -le 0 ]; then
66+
echo -e "${RED} ✗ PostgreSQL failed to start within 60 seconds${NC}"
67+
exit 1
68+
fi
69+
70+
# Wait for server to be responding
71+
echo -e "${YELLOW}• Waiting for Spring Boot server...${NC}"
72+
timeout=120
73+
while [ $timeout -gt 0 ]; do
74+
if curl -f -s http://localhost:8080/actuator/health >/dev/null 2>&1; then
75+
echo -e "${GREEN} ✓ Server is ready${NC}"
76+
break
77+
fi
78+
sleep 3
79+
((timeout-=3))
80+
done
81+
82+
if [ $timeout -le 0 ]; then
83+
echo -e "${YELLOW} ⚠ Server may still be starting (timeout reached)${NC}"
84+
fi
85+
86+
# Check if client is responding
87+
echo -e "${YELLOW}• Waiting for React client...${NC}"
88+
timeout=30
89+
while [ $timeout -gt 0 ]; do
90+
if curl -f -s http://localhost:4173 >/dev/null 2>&1; then
91+
echo -e "${GREEN} ✓ Client is ready${NC}"
92+
break
93+
fi
94+
sleep 2
95+
((timeout-=2))
96+
done
97+
98+
if [ $timeout -le 0 ]; then
99+
echo -e "${YELLOW} ⚠ Client may still be starting (timeout reached)${NC}"
100+
fi
101+
102+
# Summary
103+
echo -e "\n${BLUE}========================================${NC}"
104+
echo -e "${GREEN}✓ Harmonia is now running!${NC}"
105+
echo -e "${BLUE}========================================${NC}"
106+
echo -e "${YELLOW}Server:${NC} http://localhost:8080"
107+
echo -e "${YELLOW}Client:${NC} http://localhost:4173"
108+
echo -e "${YELLOW}Database:${NC} PostgreSQL on localhost:5432"
109+
echo -e "\n${YELLOW}Useful commands:${NC}"
110+
echo -e "${YELLOW}• View logs:${NC} docker compose -f docker/docker-compose.yml logs -f"
111+
echo -e "${YELLOW}• Stop services:${NC} docker compose -f docker/docker-compose.yml down"
112+
echo -e "${YELLOW}• Restart services:${NC} docker compose -f docker/docker-compose.yml restart"
113+
echo -e "\n${YELLOW}Press Ctrl+C to stop all services${NC}\n"
114+
115+
# Keep script running and handle Ctrl+C
116+
trap "echo -e '\n${RED}Shutting down all services...${NC}'; docker compose -f docker/docker-compose.yml down; exit" INT
117+
118+
# Follow logs to keep script running
119+
docker compose -f docker/docker-compose.yml logs -f

0 commit comments

Comments
 (0)