diff --git a/package-lock.json b/package-lock.json index 2134f81..d35ec1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4565,9 +4565,9 @@ } }, "node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" diff --git a/packages/backend/routes/users.js b/packages/backend/routes/users.js index 649caa8..b78e214 100644 --- a/packages/backend/routes/users.js +++ b/packages/backend/routes/users.js @@ -38,6 +38,20 @@ function getAllowedUpdates(body, allowedFields) { ); } +export function normalizeUserName(name) { + if (typeof name !== "string") { + return { error: "Username is required" }; + } + + const value = name.trim(); + + if (!value) { + return { error: "Username is required" }; + } + + return { value }; +} + //check if email exists function isDuplicateKeyError(error) { return error?.code === 11000; @@ -246,6 +260,16 @@ router.put("/:userParam", requireAuth, async (req, res) => { const updates = getAllowedUpdates(req.body, allowedUserUpdateFields); + if (Object.hasOwn(updates, "name")) { + const normalizedName = normalizeUserName(updates.name); + + if (normalizedName.error) { + return res.status(400).json({ error: normalizedName.error }); + } + + updates.name = normalizedName.value; + } + if (Object.keys(updates).length === 0) { return res.status(400).json({ error: `At least one valid field is required: ${allowedUserUpdateFields.join(", ")}`, diff --git a/packages/frontend/src/components/Profile.jsx b/packages/frontend/src/components/Profile.jsx index fb0fd90..2565d84 100644 --- a/packages/frontend/src/components/Profile.jsx +++ b/packages/frontend/src/components/Profile.jsx @@ -67,6 +67,7 @@ function getInterestError(value, currentInterests) { function Profile() { const [user, setUser] = useState(null); const [form, setForm] = useState({ + name: "", age: "", interests: [], profileVisibility: "private", @@ -90,6 +91,7 @@ function Profile() { .then((data) => { setUser(data.user); setForm({ + name: data.user.name || "", age: data.user.age || "", interests: normalizeInterests(data.user.interests), profileVisibility: data.user.profileVisibility || "private", @@ -141,11 +143,19 @@ function Profile() { setError(""); setMessage(""); + const name = form.name.trim(); + + if (!name) { + setError("Username is required"); + return; + } + fetch(`${AZURE_URL}/api/users/${user._id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ + name, age: form.age === "" ? null : Number(form.age), interests: form.interests, profileVisibility: form.profileVisibility, @@ -160,6 +170,7 @@ function Profile() { setUser(data); setForm((current) => ({ ...current, + name: data.name || "", age: data.age || "", interests: normalizeInterests(data.interests), profileVisibility: data.profileVisibility || "private", @@ -244,8 +255,18 @@ function Profile() {