Skip to content

Commit 50e401c

Browse files
committed
test
1 parent 67a23bd commit 50e401c

File tree

5 files changed

+175
-47
lines changed

5 files changed

+175
-47
lines changed

.github/workflows/ci.yml

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ on:
77
pull_request:
88
branches:
99
- main
10-
workflow_dispatch: # Enable manual trigger
10+
workflow_dispatch:
1111

1212
jobs:
13-
build:
13+
build-and-deploy:
1414
runs-on: ubuntu-latest
1515

1616
services:
@@ -22,7 +22,7 @@ jobs:
2222
MYSQL_PASSWORD: ${{ secrets.PASSWORD }}
2323
MYSQL_ROOT_PASSWORD: ${{ secrets.PASSWORD }}
2424
ports:
25-
- 33306:3306 # Expose the internal MySQL port 3306 as 33306
25+
- 33306:3306
2626
options: >-
2727
--health-cmd "mysqladmin ping --silent"
2828
--health-interval 20s
@@ -33,14 +33,30 @@ jobs:
3333
- name: Checkout code
3434
uses: actions/checkout@v2
3535
with:
36-
fetch-depth: 0 # Fetch all history for all branches and tags
36+
fetch-depth: 0
3737

38-
# Set up Node.js environment
3938
- name: Set up Node.js
4039
uses: actions/setup-node@v2
4140
with:
4241
node-version: "16"
4342

43+
# Add caching for dependencies
44+
- name: Cache dependencies
45+
uses: actions/cache@v2
46+
with:
47+
path: |
48+
**/node_modules
49+
key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }}
50+
51+
# Add caching for build outputs
52+
- name: Cache build outputs
53+
uses: actions/cache@v2
54+
with:
55+
path: |
56+
client/dist
57+
server/public
58+
key: ${{ runner.os }}-build-${{ github.sha }}
59+
4460
# Install server dependencies
4561
- name: Install server dependencies
4662
working-directory: ./server
@@ -49,13 +65,13 @@ jobs:
4965
# Start the Express backend
5066
- name: Start Express backend
5167
working-directory: ./server
52-
run: npm start & # Use '&' to run it in the background
68+
run: npm start &
5369

54-
# Wait for Express server to be ready
55-
- name: Wait for Express server to be ready
70+
# Wait for Express server
71+
- name: Wait for Express server
5672
run: |
5773
echo "Waiting for Express server to be ready..."
58-
for i in {1..10}; do # Try for up to 10 attempts
74+
for i in {1..10}; do
5975
if curl -s http://localhost:${{ secrets.PORT }}; then
6076
echo "Express server is ready!"
6177
break
@@ -64,20 +80,20 @@ jobs:
6480
sleep 5
6581
done
6682
67-
# Install and test backend (server)
68-
- name: Install and test server
83+
# Run server tests
84+
- name: Test server
6985
working-directory: ./server
7086
env:
7187
PORT: ${{ secrets.PORT }}
7288
USER: ${{ secrets.USER }}
7389
HOST: 127.0.0.1
7490
DATABASE: ${{ secrets.DATABASE }}
7591
PASSWORD: ${{ secrets.PASSWORD }}
76-
DBPORT: 33306 # Match the changed port
92+
DBPORT: 33306
7793
JWT_SECRET: ${{ secrets.JWT_SECRET }}
7894
run: npm test
7995

80-
# Install and build frontend (client)
96+
# Install and build frontend
8197
- name: Install and build client
8298
working-directory: ./client
8399
env:
@@ -88,24 +104,74 @@ jobs:
88104
run: |
89105
npm install
90106
npm run build
91-
mv ./dist ../server/public # Move built React files to the server's public directory
92107
93-
# Debug step: MySQL logs
94-
- name: MySQL Logs
95-
run: docker logs $(docker ps -q --filter "ancestor=mysql:8.0")
108+
# Prepare deployment
109+
- name: Prepare for deployment
110+
run: |
111+
mkdir -p server/public
112+
cp -r client/dist/* server/public/
113+
114+
# Debug: List contents of directories
115+
- name: Debug - List directories
116+
run: |
117+
echo "Contents of server/public:"
118+
ls -la server/public
119+
echo "Contents of client/dist:"
120+
ls -la client/dist
96121
97-
# Deploy to Heroku (only if on the main branch and build was successful)
122+
# Deploy to Heroku
98123
- name: Deploy to Heroku
99124
if: github.ref == 'refs/heads/main' && success()
100125
env:
101-
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }} # Ensure you have this secret set in GitHub
126+
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
127+
run: |
128+
# Enable error logging
129+
set -x
130+
131+
# Ensure the public directory exists and copy files
132+
mkdir -p server/public
133+
cp -r client/dist/* server/public/
134+
135+
# Configure Git
136+
git config --global user.email "[email protected]"
137+
git config --global user.name "CI/CD Pipeline"
138+
139+
# Stage and commit the built files
140+
git add server/public -f
141+
git commit -m "Update frontend build [skip ci]" || echo "No changes to commit"
142+
143+
# Install Heroku CLI if not already installed
144+
if ! command -v heroku &> /dev/null; then
145+
curl https://cli-assets.heroku.com/install.sh | sh
146+
fi
147+
148+
# Add Heroku remote
149+
heroku git:remote -a gymero
150+
151+
# Push to Heroku
152+
git push https://heroku:${{ secrets.HEROKU_API_KEY }}@git.heroku.com/gymero.git HEAD:main --force
153+
154+
# If deployment fails, show Heroku logs
155+
if [ $? -ne 0 ]; then
156+
heroku logs --tail --app gymero
157+
exit 1
158+
159+
# Post-deployment verification
160+
- name: Verify deployment
161+
if: github.ref == 'refs/heads/main' && success()
162+
run: |
163+
echo "Waiting for deployment to stabilize..."
164+
sleep 30
165+
curl --fail https://gymero.live || exit 1
166+
167+
# Debug MySQL logs if needed
168+
- name: MySQL Logs
169+
if: failure()
170+
run: docker logs $(docker ps -q --filter "ancestor=mysql:8.0")
171+
172+
# Cleanup
173+
- name: Cleanup
174+
if: always()
102175
run: |
103-
echo "Adding Heroku remote..."
104-
git remote add heroku https://apikey:${{ secrets.HEROKU_API_KEY }}@git.heroku.com/gymero.git
105-
echo "Committing built frontend..."
106-
git config user.name "CI/CD Pipeline"
107-
git config user.email "[email protected]"
108-
git add server/public
109-
git commit -m "Include frontend build artifacts [skip ci]" || echo "No changes to commit"
110-
echo "Pushing to Heroku..."
111-
git push heroku main --force # Ensure you are pushing the right branch
176+
rm -rf server/public/*
177+
git reset --hard HEAD

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# .gitignore
2+
**/node_modules
3+
**/.env
4+
**/dist
5+
!server/public/

server/Procfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: node server.js

server/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"scripts": {
88
"start": "node server.js",
99
"dev": "nodemon server.js",
10-
"test": "echo \"No tests specified\""
10+
"test": "echo \"No tests specified\"",
11+
"build": "cd ../client && npm install && npm run build && cp -r dist/* ../server/public/",
12+
"heroku-postbuild": "cd ../client && npm install && npm run build && cp -r dist/* ../server/public/"
1113
},
1214
"keywords": [],
1315
"author": "",

server/server.js

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { config } from "dotenv";
66
import { Server } from "socket.io";
77
import path from "path";
88
import { fileURLToPath } from "url";
9+
import fs from "fs";
910

1011
import mealsRoutes from "./routes/mealsRoutes.js";
1112
import workoutRoutes from "./routes/workoutRoutes.js";
@@ -30,23 +31,13 @@ const PORT = process.env.PORT || 8081;
3031

3132
const isProduction = process.env.NODE_ENV === "production";
3233
const clientOrigin = isProduction
33-
? "https://gymero.live" // Production URL
34-
: "http://localhost:5173"; // Local development URL
34+
? "https://gymero.live"
35+
: "http://localhost:5173";
3536

3637
if (isProduction) {
3738
app.use(expressSslify.HTTPS({ trustProtoHeader: true }));
3839
}
3940

40-
// Create a rate limiter
41-
// const apiLimiter = rateLimit({
42-
// windowMs: 15 * 60 * 1000, // 15 minutes
43-
// max: 50, // Limit each IP to 100 requests per windowMs
44-
// message: "Too many requests from this IP, please try again later.",
45-
// });
46-
47-
// // Apply the rate limiter to all API routes
48-
// app.use("/api/", apiLimiter);
49-
5041
const server = http.createServer(app);
5142
const io = new Server(server, {
5243
cors: {
@@ -56,6 +47,7 @@ const io = new Server(server, {
5647
},
5748
});
5849

50+
// Middleware
5951
app.use(
6052
cors({
6153
origin: [clientOrigin],
@@ -66,6 +58,16 @@ app.use(
6658
app.use(express.json());
6759
app.use(cookieParser());
6860

61+
// Ensure public directory exists
62+
const publicDir = path.join(__dirname, "public");
63+
if (!fs.existsSync(publicDir)) {
64+
fs.mkdirSync(publicDir, { recursive: true });
65+
}
66+
67+
// Serve static files from the public directory
68+
app.use(express.static(publicDir));
69+
70+
// API Routes
6971
app.use("/auth", authRoutes);
7072
app.use("/api/users", userRoutes);
7173
app.use("/api/workouts", workoutRoutes);
@@ -77,26 +79,42 @@ app.use("/api/streak", streakRoutes);
7779
app.use("/api/routines", gymRoutineRoutes);
7880
app.use("/api/daily-settings", dailySettingsRoutes);
7981

80-
// Serve static files only in production
82+
// Serve static files in production
8183
if (isProduction) {
82-
const clientPath = path.resolve(__dirname, "../client/dist");
84+
// Serve from both public and client/dist directories
85+
const clientPath = path.resolve(__dirname, "public");
8386
app.use(express.static(clientPath));
8487

85-
// Define the root route to serve the index.html file
88+
// Health check endpoint
89+
app.get("/health", (req, res) => {
90+
res.status(200).send("OK");
91+
});
92+
93+
// Define routes for serving the SPA
8694
app.get("/", (req, res) => {
8795
res.sendFile(path.join(clientPath, "index.html"));
8896
});
8997

90-
// Catch-all route for SPA
98+
// Catch-all route for client-side routing
9199
app.get("*", (req, res) => {
92-
res.sendFile(path.join(clientPath, "index.html"));
100+
// First try to serve from public directory
101+
const publicPath = path.join(__dirname, "public", req.path);
102+
if (fs.existsSync(publicPath)) {
103+
res.sendFile(publicPath);
104+
} else {
105+
// If not found in public, serve the SPA's index.html
106+
res.sendFile(path.join(__dirname, "public", "index.html"));
107+
}
93108
});
94109
}
95110

96111
// Socket.IO event listeners
97112
io.on("connection", (socket) => {
113+
console.log("New client connected");
114+
98115
socket.on("joinRoom", (roomId) => {
99116
socket.join(roomId);
117+
console.log(`Client joined room: ${roomId}`);
100118
});
101119

102120
socket.on(
@@ -122,11 +140,47 @@ io.on("connection", (socket) => {
122140
);
123141

124142
socket.on("disconnect", () => {
125-
console.log("User disconnected");
143+
console.log("Client disconnected");
144+
});
145+
146+
// Error handling for socket
147+
socket.on("error", (error) => {
148+
console.error("Socket error:", error);
126149
});
127150
});
128151

152+
// Global error handler
153+
app.use((err, req, res, next) => {
154+
console.error(err.stack);
155+
res.status(500).send("Something broke!");
156+
});
157+
129158
// Start the server
130159
server.listen(PORT, () => {
131160
console.log(`Server running on port ${PORT}`);
161+
console.log(`Environment: ${process.env.NODE_ENV}`);
162+
console.log(
163+
`Static files being served from: ${path.join(__dirname, "public")}`
164+
);
165+
});
166+
167+
// Handle server shutdown gracefully
168+
process.on("SIGTERM", () => {
169+
console.log("SIGTERM signal received: closing HTTP server");
170+
server.close(() => {
171+
console.log("HTTP server closed");
172+
process.exit(0);
173+
});
132174
});
175+
176+
process.on("uncaughtException", (err) => {
177+
console.error("Uncaught Exception:", err);
178+
process.exit(1);
179+
});
180+
181+
process.on("unhandledRejection", (reason, promise) => {
182+
console.error("Unhandled Rejection at:", promise, "reason:", reason);
183+
// Handle the error appropriately
184+
});
185+
186+
export default server;

0 commit comments

Comments
 (0)