From b6d44f14b133fda12ed4b17cb6f5c33af426c272 Mon Sep 17 00:00:00 2001 From: zhitguo Date: Thu, 25 Jun 2026 23:07:13 -0400 Subject: [PATCH] update error handling --- package-lock.json | 2 + public/js/exercises.js | 77 ++++++++++++++++++++++++++------------ public/js/workouts.js | 62 +++++++++++++++++++++--------- server/routes/exercises.js | 24 ++++++++---- server/routes/workouts.js | 16 +++++--- 5 files changed, 129 insertions(+), 52 deletions(-) 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" }); } });