Skip to content

Commit 6489d84

Browse files
committed
feat: refactor deployment workflow, enhance service configurations, and add system status monitoring
1 parent f5c7183 commit 6489d84

File tree

16 files changed

+497
-83
lines changed

16 files changed

+497
-83
lines changed

build_and_run.sh

Lines changed: 0 additions & 16 deletions
This file was deleted.

.github/workflows/deploy.yml

Lines changed: 226 additions & 55 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ target/
44
*.war
55
*.ear
66
*.class
7+
!.mvn/wrapper/maven-wrapper.jar
8+
!**/src/main/**/target/
9+
!**/src/test/**/target/
710

811
# Eclipse files
912
.classpath
@@ -22,6 +25,8 @@ target/
2225
# Environment files with sensitive information
2326
.env
2427
.env.*
28+
.env.local
29+
.env.*.local
2530
!.env.template
2631
set_oracle_env.bat
2732
*.pem
@@ -43,6 +48,10 @@ set_oracle_env.bat
4348
vm-deploy/
4449
# Only include specifically vetted files
4550
!vm-deploy/eureka-server/Dockerfile
51+
# Ignore vm-deploy directory except config
52+
vm-deploy/*
53+
!vm-deploy/config/
54+
!vm-deploy/docker-compose.yml
4655

4756
# Logs
4857
logs/

api-gateway/src/main/resources/application.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ spring:
3131
enabled: true
3232
lower-case-service-id: true
3333
routes:
34+
- id: eureka-server-apps
35+
uri: ${EUREKA_SERVER_URL:lb://eureka-server}
36+
predicates:
37+
- Path=/eureka/services
38+
filters:
39+
- RewritePath=/eureka/services, /eureka/apps
40+
- AddResponseHeader=Access-Control-Allow-Origin, https://musicanalytics.netlify.app
41+
- AddResponseHeader=Access-Control-Allow-Methods, GET,OPTIONS
42+
- AddResponseHeader=Access-Control-Allow-Headers, Content-Type
43+
- AddResponseHeader=Access-Control-Allow-Credentials, true
44+
45+
- id: actuator-routes
46+
uri: lb://api-gateway
47+
predicates:
48+
- Path=/actuator/**
49+
filters:
50+
- AddResponseHeader=Access-Control-Allow-Origin, https://musicanalytics.netlify.app
51+
- AddResponseHeader=Access-Control-Allow-Methods, GET,OPTIONS
52+
- AddResponseHeader=Access-Control-Allow-Headers, Content-Type
53+
- AddResponseHeader=Access-Control-Allow-Credentials, true
54+
3455
- id: recommendation-service
3556
uri: ${RECOMMENDATION_SERVICE_URL:lb://recommendation-service}
3657
predicates:
@@ -82,9 +103,14 @@ management:
82103
exposure:
83104
include: "*"
84105
endpoint:
106+
health:
107+
show-details: always
85108
gateway:
86109
enabled: true
87110

111+
security:
112+
enabled: false
113+
88114
# Docker profile
89115
---
90116
spring:
@@ -124,6 +150,27 @@ spring:
124150
allowCredentials: true
125151
maxAge: 3600
126152
routes:
153+
- id: eureka-server-apps
154+
uri: http://eureka-server:8761
155+
predicates:
156+
- Path=/eureka/services
157+
filters:
158+
- RewritePath=/eureka/services, /eureka/apps
159+
- AddResponseHeader=Access-Control-Allow-Origin, https://musicanalytics.netlify.app
160+
- AddResponseHeader=Access-Control-Allow-Methods, GET,OPTIONS
161+
- AddResponseHeader=Access-Control-Allow-Headers, Content-Type
162+
- AddResponseHeader=Access-Control-Allow-Credentials, true
163+
164+
- id: actuator-routes
165+
uri: lb://api-gateway
166+
predicates:
167+
- Path=/actuator/**
168+
filters:
169+
- AddResponseHeader=Access-Control-Allow-Origin, https://musicanalytics.netlify.app
170+
- AddResponseHeader=Access-Control-Allow-Methods, GET,OPTIONS
171+
- AddResponseHeader=Access-Control-Allow-Headers, Content-Type
172+
- AddResponseHeader=Access-Control-Allow-Credentials, true
173+
127174
- id: recommendation-service
128175
uri: ${RECOMMENDATION_SERVICE_URL}
129176
predicates:
@@ -171,3 +218,14 @@ eureka:
171218
non-secure-port-enabled: true
172219
secure-port-enabled: false
173220

221+
management:
222+
endpoints:
223+
web:
224+
exposure:
225+
include: "*"
226+
endpoint:
227+
health:
228+
show-details: always
229+
gateway:
230+
enabled: true
231+

eureka-server/src/main/resources/application.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,22 @@ eureka:
5454
instance:
5555
prefer-ip-address: true
5656
hostname: eureka-server
57+
client:
58+
registerWithEureka: false
59+
fetchRegistry: false
60+
serviceUrl:
61+
defaultZone: http://localhost:8761/eureka/
62+
server:
63+
wait-time-in-ms-when-sync-empty: 0
64+
enable-self-preservation: false
65+
66+
management:
67+
endpoints:
68+
web:
69+
exposure:
70+
include: health,info
71+
base-path: /actuator
72+
endpoint:
73+
health:
74+
show-details: always
5775

frontend/Dockerfile

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
# Use an official Node.js image to build the application
2-
FROM node:14
3-
4-
# Set the working directory inside the container
1+
# Build stage
2+
FROM node:16-alpine as build
53
WORKDIR /app
6-
7-
# Copy the package.json and package-lock.json files and install dependencies
84
COPY package*.json ./
95
RUN npm install
10-
11-
# Copy the source code and build the application
12-
COPY . .
6+
COPY . ./
137
RUN npm run build
148

15-
# Expose port 3000 and set the entry point to run the application
16-
EXPOSE 3000
17-
CMD ["npm", "start"]
9+
# Production stage
10+
FROM nginx:alpine
11+
RUN apk add --no-cache curl
12+
COPY --from=build /app/build /usr/share/nginx/html
13+
COPY nginx.conf /etc/nginx/conf.d/default.conf
14+
EXPOSE 80
15+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
16+
CMD curl -f http://localhost/ || exit 1
17+
CMD ["nginx", "-g", "daemon off;"]

frontend/src/App.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Recommendation from './components/Recommendation';
44
import Statistics from './components/Statistics';
55
import UserTracking from './components/UserTracking';
66
import TrackingHistory from './components/TrackingHistory';
7+
import SystemStatus from './components/SystemStatus';
78
import api from './api'; // Import the api module
89

910
// Get the API_BASE_URL from process.env or default to your VM IP
@@ -38,6 +39,11 @@ function NavLink({ to, children }) {
3839
<path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm1-7.41V8c0-.55-.45-1-1-1s-1 .45-1 1v4c0 .28.11.53.29.71l3 3c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13 11.59z" />
3940
</svg>
4041
)}
42+
{to === '/status' && (
43+
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
44+
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-4h2v2h-2zm0-2h2V7h-2z" />
45+
</svg>
46+
)}
4147
<span>{children}</span>
4248
</Link>
4349
</li>
@@ -60,6 +66,7 @@ function App() {
6066
<NavLink to="/statistics">Top Tracks</NavLink>
6167
<NavLink to="/user-tracking">Add Track</NavLink>
6268
<NavLink to="/history">History</NavLink>
69+
<NavLink to="/status">System Status</NavLink>
6370
</ul>
6471
</nav>
6572
<main>
@@ -68,6 +75,7 @@ function App() {
6875
<Route path="/statistics" element={<Statistics />} />
6976
<Route path="/user-tracking" element={<UserTracking />} />
7077
<Route path="/history" element={<TrackingHistory />} />
78+
<Route path="/status" element={<SystemStatus />} />
7179
<Route path="/" element={<Recommendation />} />
7280
</Routes>
7381
</main>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React, { useState, useEffect } from 'react';
2+
import api from '../api';
3+
4+
function SystemStatus() {
5+
const [status, setStatus] = useState({
6+
loading: true,
7+
apiGateway: 'Unknown',
8+
eureka: 'Unknown',
9+
services: {
10+
recommendation: 'Unknown',
11+
statistics: 'Unknown',
12+
userTracking: 'Unknown'
13+
}
14+
});
15+
16+
useEffect(() => {
17+
const checkStatus = async () => {
18+
try {
19+
// Check API Gateway status
20+
const apiGatewayResponse = await api.get('/actuator/health');
21+
22+
// Try to get services from Eureka
23+
const servicesResponse = await api.get('/eureka/services');
24+
25+
setStatus({
26+
loading: false,
27+
apiGateway: apiGatewayResponse.data.status || 'UP',
28+
eureka: 'UP',
29+
services: {
30+
recommendation: servicesResponse.data.includes('RECOMMENDATION-SERVICE') ? 'UP' : 'DOWN',
31+
statistics: servicesResponse.data.includes('STATISTICS-SERVICE') ? 'UP' : 'DOWN',
32+
userTracking: servicesResponse.data.includes('USER-TRACKING-SERVICE') ? 'UP' : 'DOWN'
33+
}
34+
});
35+
} catch (error) {
36+
console.error('Failed to fetch system status:', error);
37+
setStatus(prev => ({
38+
...prev,
39+
loading: false,
40+
apiGateway: error.message.includes('Network Error') ? 'DOWN' : prev.apiGateway,
41+
eureka: 'DOWN'
42+
}));
43+
}
44+
};
45+
46+
checkStatus();
47+
// Check status every 30 seconds
48+
const interval = setInterval(checkStatus, 30000);
49+
50+
return () => clearInterval(interval);
51+
}, []);
52+
53+
return (
54+
<div className="system-status">
55+
<h2>System Status</h2>
56+
{status.loading ? (
57+
<p>Loading system status...</p>
58+
) : (
59+
<div className="status-grid">
60+
<div className={`status-item ${status.apiGateway === 'UP' ? 'status-up' : 'status-down'}`}>
61+
<h3>API Gateway</h3>
62+
<p>{status.apiGateway}</p>
63+
</div>
64+
<div className={`status-item ${status.eureka === 'UP' ? 'status-up' : 'status-down'}`}>
65+
<h3>Eureka Server</h3>
66+
<p>{status.eureka}</p>
67+
</div>
68+
<div className={`status-item ${status.services.recommendation === 'UP' ? 'status-up' : 'status-down'}`}>
69+
<h3>Recommendation Service</h3>
70+
<p>{status.services.recommendation}</p>
71+
</div>
72+
<div className={`status-item ${status.services.statistics === 'UP' ? 'status-up' : 'status-down'}`}>
73+
<h3>Statistics Service</h3>
74+
<p>{status.services.statistics}</p>
75+
</div>
76+
<div className={`status-item ${status.services.userTracking === 'UP' ? 'status-up' : 'status-down'}`}>
77+
<h3>User Tracking Service</h3>
78+
<p>{status.services.userTracking}</p>
79+
</div>
80+
</div>
81+
)}
82+
<div className="last-updated">
83+
<small>Last updated: {new Date().toLocaleTimeString()}</small>
84+
</div>
85+
</div>
86+
);
87+
}
88+
89+
export default SystemStatus;

frontend/src/styles.css

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,67 @@ button:hover {
455455
opacity: 1;
456456
}
457457
}
458+
459+
/* System Status Component Styles */
460+
.system-status {
461+
background: #1a1a1a;
462+
border-radius: 12px;
463+
padding: 20px;
464+
margin: 20px;
465+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
466+
}
467+
468+
.system-status h2 {
469+
color: #fff;
470+
margin-bottom: 20px;
471+
text-align: center;
472+
}
473+
474+
.status-grid {
475+
display: grid;
476+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
477+
gap: 20px;
478+
margin-bottom: 20px;
479+
}
480+
481+
.status-item {
482+
background: #2a2a2a;
483+
padding: 15px;
484+
border-radius: 8px;
485+
text-align: center;
486+
transition: all 0.3s ease;
487+
}
488+
489+
.status-item h3 {
490+
color: #fff;
491+
margin-bottom: 10px;
492+
font-size: 1rem;
493+
}
494+
495+
.status-item p {
496+
font-weight: bold;
497+
margin: 0;
498+
}
499+
500+
.status-up {
501+
border-left: 4px solid #4caf50;
502+
}
503+
504+
.status-up p {
505+
color: #4caf50;
506+
}
507+
508+
.status-down {
509+
border-left: 4px solid #f44336;
510+
}
511+
512+
.status-down p {
513+
color: #f44336;
514+
}
515+
516+
.last-updated {
517+
text-align: right;
518+
color: #666;
519+
font-size: 0.8rem;
520+
margin-top: 10px;
521+
}

recommendation-service/src/main/resources/application.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ management:
3434
health:
3535
show-details: always
3636

37+
security:
38+
enabled: false
39+
3740
# Docker profile
3841
---
3942
spring:

0 commit comments

Comments
 (0)