diff --git a/package-lock.json b/package-lock.json
index c2ec7fb..840485e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -278,6 +278,7 @@
"integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -609,6 +610,7 @@
"integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"workspaces": [
"packages/*"
],
diff --git a/public/js/exercises.js b/public/js/exercises.js
index 13cb28f..8f03fbf 100644
--- a/public/js/exercises.js
+++ b/public/js/exercises.js
@@ -4,9 +4,15 @@ const API = "/api/exercises";
// Fetch exercises from the API with optional filters
async function fetchExercises(filters = {}) {
const params = new URLSearchParams(filters);
- const res = await fetch(`${API}?${params}`);
- const data = await res.json();
- renderExercises(data);
+ try {
+ const res = await fetch(`${API}?${params}`);
+ // Throw if the server returned a non-2xx status so the catch block handles it
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ const data = await res.json();
+ renderExercises(data);
+ } catch (err) {
+ console.error("Failed to fetch exercises:", err);
+ }
}
// Build and render exercise cards from API data
@@ -91,11 +97,17 @@ document
Desc: document.getElementById("new-desc").value,
};
- await fetch(API, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(exercise),
- });
+ try {
+ const res = await fetch(API, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(exercise),
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ } catch (err) {
+ console.error("Failed to add exercise:", err);
+ return;
+ }
document.getElementById("exercise-form").style.display = "none";
document.getElementById("add-exercise-form").reset();
@@ -106,7 +118,13 @@ document
async function handleDelete(e) {
const id = e.target.dataset.id;
if (!confirm("Delete this exercise?")) return;
- await fetch(`${API}/${id}`, { method: "DELETE" });
+ try {
+ const res = await fetch(`${API}/${id}`, { method: "DELETE" });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ } catch (err) {
+ console.error("Failed to delete exercise:", err);
+ return;
+ }
fetchExercises();
}
@@ -142,18 +160,24 @@ function handleEdit(e) {
`;
card.querySelector(".save-btn").addEventListener("click", async () => {
- await fetch(`${API}/${id}`, {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- Title: card.querySelector(".edit-title").value,
- BodyPart: card.querySelector(".edit-bodypart").value,
- Equipment: card.querySelector(".edit-equipment").value,
- Type: card.querySelector(".edit-type").value,
- Level: card.querySelector(".edit-level").value,
- Desc: card.querySelector(".edit-desc").value,
- }),
- });
+ try {
+ const res = await fetch(`${API}/${id}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ Title: card.querySelector(".edit-title").value,
+ BodyPart: card.querySelector(".edit-bodypart").value,
+ Equipment: card.querySelector(".edit-equipment").value,
+ Type: card.querySelector(".edit-type").value,
+ Level: card.querySelector(".edit-level").value,
+ Desc: card.querySelector(".edit-desc").value,
+ }),
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ } catch (err) {
+ console.error("Failed to update exercise:", err);
+ return;
+ }
fetchExercises();
});
@@ -177,8 +201,15 @@ document.getElementById("random-btn").addEventListener("click", async () => {
checkedBodyParts.forEach((bp) => params.append("bodyPart", bp));
checkedEquipment.forEach((eq) => params.append("equipment", eq));
- const res = await fetch(`/api/exercises/random?${params}`);
- const exercises = await res.json();
+ let exercises;
+ try {
+ const res = await fetch(`/api/exercises/random?${params}`);
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ exercises = await res.json();
+ } catch (err) {
+ console.error("Failed to generate random workout:", err);
+ return;
+ }
const results = document.getElementById("random-results");
results.innerHTML = `
Your Random Workout
`;
diff --git a/public/js/workouts.js b/public/js/workouts.js
index f0ecb33..7477792 100644
--- a/public/js/workouts.js
+++ b/public/js/workouts.js
@@ -63,8 +63,16 @@ async function runSearch() {
equipment: formEquipment.value,
level: formLevel.value,
});
- const res = await fetch(`/api/exercises?${params}`);
- const exercises = await res.json();
+ let exercises;
+ try {
+ const res = await fetch(`/api/exercises?${params}`);
+ // Throw if the server returned a non-2xx status so the catch block handles it
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ exercises = await res.json();
+ } catch (err) {
+ console.error("Failed to search exercises:", err);
+ return;
+ }
formSearchResults.innerHTML = "";
exercises.forEach((ex) => {
const div = document.createElement("div");
@@ -95,8 +103,15 @@ formEquipment.addEventListener("change", triggerSearch);
formLevel.addEventListener("change", triggerSearch);
async function loadWorkouts() {
- const res = await fetch("/api/workouts");
- const workouts = await res.json();
+ let workouts;
+ try {
+ const res = await fetch("/api/workouts");
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ workouts = await res.json();
+ } catch (err) {
+ console.error("Failed to load workouts:", err);
+ return;
+ }
workoutList.innerHTML = "";
@@ -131,7 +146,13 @@ async function loadWorkouts() {
});
card.querySelector(".delete-btn").addEventListener("click", async () => {
- await fetch(`/api/workouts/${plan._id}`, { method: "DELETE" });
+ try {
+ const res = await fetch(`/api/workouts/${plan._id}`, { method: "DELETE" });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ } catch (err) {
+ console.error("Failed to delete workout:", err);
+ return;
+ }
loadWorkouts();
});
@@ -150,18 +171,25 @@ addPlanForm.addEventListener("submit", async (event) => {
if (!workout.name) return;
- if (editingId) {
- await fetch(`/api/workouts/${editingId}`, {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(workout),
- });
- } else {
- await fetch("/api/workouts", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(workout),
- });
+ try {
+ if (editingId) {
+ const res = await fetch(`/api/workouts/${editingId}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(workout),
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ } else {
+ const res = await fetch("/api/workouts", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(workout),
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ }
+ } catch (err) {
+ console.error("Failed to save workout:", err);
+ return;
}
closeForm();
diff --git a/server/routes/exercises.js b/server/routes/exercises.js
index 4bc9de1..ae70e8e 100644
--- a/server/routes/exercises.js
+++ b/server/routes/exercises.js
@@ -18,7 +18,9 @@ export function registerExerciseRoutes(app) {
.limit(20)
.toArray();
res.json(exercises);
- } catch (_error) {
+ } catch (err) {
+ // Log the real error so it appears in server logs for debugging
+ console.error(err);
res.status(500).json({ error: "Failed to fetch exercises" });
}
});
@@ -84,8 +86,12 @@ export function registerExerciseRoutes(app) {
results = results.concat(extra);
}
- // Shuffle results
- results.sort(() => Math.random() - 0.5);
+ // Fisher-Yates shuffle: unbiased O(n) algorithm that guarantees each
+ // permutation is equally likely, unlike sort(() => Math.random() - 0.5)
+ for (let i = results.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [results[i], results[j]] = [results[j], results[i]];
+ }
return res.json(results);
}
@@ -101,7 +107,8 @@ export function registerExerciseRoutes(app) {
.toArray();
res.json(exercises);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to generate workout" });
}
});
@@ -112,7 +119,8 @@ export function registerExerciseRoutes(app) {
const exercise = req.body;
const result = await db.collection("exercises").insertOne(exercise);
res.json(result);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to add exercise" });
}
});
@@ -127,7 +135,8 @@ export function registerExerciseRoutes(app) {
.collection("exercises")
.updateOne({ _id: id }, { $set: update });
res.json(result);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to update exercise" });
}
});
@@ -139,7 +148,8 @@ export function registerExerciseRoutes(app) {
const id = new ObjectId(req.params.id);
const result = await db.collection("exercises").deleteOne({ _id: id });
res.json(result);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to delete exercise" });
}
});
diff --git a/server/routes/workouts.js b/server/routes/workouts.js
index 6a0a2af..99a2443 100644
--- a/server/routes/workouts.js
+++ b/server/routes/workouts.js
@@ -6,7 +6,9 @@ export function registerWorkoutRoutes(app) {
try {
const workouts = await db.collection("workouts").find({}).toArray();
res.json(workouts);
- } catch (_error) {
+ } catch (err) {
+ // Log the real error so it appears in server logs for debugging
+ console.error(err);
res.status(500).json({ error: "Failed to fetch workout plans" });
}
});
@@ -17,7 +19,8 @@ export function registerWorkoutRoutes(app) {
const workout = req.body;
const result = await db.collection("workouts").insertOne(workout);
res.json(result);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to create workout plan" });
}
});
@@ -29,7 +32,8 @@ export function registerWorkoutRoutes(app) {
const id = new ObjectId(req.params.id);
const result = await db.collection("workouts").deleteOne({ _id: id });
res.json(result);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to delete workout plan" });
}
});
@@ -44,7 +48,8 @@ export function registerWorkoutRoutes(app) {
.collection("workouts")
.updateOne({ _id: id }, { $set: update });
res.json(result);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to update workouts" });
}
});
@@ -56,7 +61,8 @@ export function registerWorkoutRoutes(app) {
const id = new ObjectId(req.params.id);
const workout = await db.collection("workouts").findOne({ _id: id });
res.json(workout);
- } catch (_error) {
+ } catch (err) {
+ console.error(err);
res.status(500).json({ error: "Failed to fetch this workout plan" });
}
});