// server/server.js // Assignment 6 — Express backend for PlateScout. // This server stores users in memory for now. // MongoDB, password hashing, JWTs, and protected routes come later. require("dotenv").config(); const express = require("express"); const cors = require("cors"); const app = express(); const PORT = process.env.PORT || 3000; // Middleware — mounted BEFORE any route. // cors() — lets the browser call this server during dev // express.json() — populates req.body on POST requests app.use(cors()); app.use(express.json()); // In-memory "database". Cleared every time nodemon restarts. // MongoDB replaces this in Lesson 20 / Assignment 7. const users = []; function validateInputs({ username, email, password }) { // TODO: Validate username. // - Required. // - Must be at least 3 characters AFTER trimming whitespace // (use .trim() before checking .length). // - Return a string like "Username must be at least 3 characters." // so the front end can display it. // // TODO: Validate email. // - Required. // - Must match a basic email shape — text @ text . text. // A regex such as /^[^\s@]+@[^\s@]+\.[^\s@]+$/ is enough. // // TODO: Validate password. // - Required. // - Must be at least 8 characters. // // Return after the FIRST failure so the user only sees one error // message at a time. The order username → email → password keeps // the messages predictable. // // TODO: Return an empty string ("") when all three fields are valid. // The /api/register handler treats a falsy return value as success. } app.post("/api/register", (req, res) => { const { username, email, password } = req.body; const validationError = validateInputs({ username, email, password }); if (validationError) { return res.status(400).json({ error: validationError }); } // TODO: Reject duplicate usernames. // - Look in the users array for an existing record with the same // username. Array.prototype.find or Array.prototype.some both work. // - If a duplicate is found, return status 409 with // { error: "Username already taken." } // and stop the function so the new user is not added. const newUser = { username, email, password, }; users.push(newUser); return res.status(201).json({ message: "User registered successfully.", user: { username: newUser.username, email: newUser.email, }, }); }); app.post("/api/login", (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: "Username and password are required.", }); } // TODO: Find the user with the matching username. // - Search the users array. Array.prototype.find works well here. // // TODO: If the user does NOT exist, return status 401. // - Use the GENERIC message "Invalid username or password." // (do NOT say "user not found" — never reveal which field is wrong). // // TODO: If the password does NOT match, return status 401. // - Compare strictly (===). We'll add bcrypt hashing in Lesson 19; // for this assignment a plain-text comparison is fine. // - Use the SAME generic message: "Invalid username or password." return res.status(200).json({ message: "Login successful.", user: { // TODO: Return only the matched user's username and email. // Do NOT return the password — the client never needs it back. }, }); }); // 404 fallback — must come AFTER all routes so they match first. app.use((req, res) => { return res.status(404).json({ error: "Route not found.", }); }); app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });