From 80f0d2784b3ff25d7669d3d8d5503c145a1eaf62 Mon Sep 17 00:00:00 2001 From: Inko-z Date: Thu, 6 Nov 2025 19:39:53 -0700 Subject: [PATCH 1/5] Added some basic changes nend to implement route fixes --- shatter-backend/src/models/user_model.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shatter-backend/src/models/user_model.ts b/shatter-backend/src/models/user_model.ts index baf8986..3fb1880 100644 --- a/shatter-backend/src/models/user_model.ts +++ b/shatter-backend/src/models/user_model.ts @@ -9,6 +9,7 @@ import { Schema, model } from 'mongoose'; export interface IUser { name: string; email: string; + password: string; } // Create the Mongoose Schema (the database blueprint) @@ -28,6 +29,11 @@ const UserSchema = new Schema( trim: true, lowercase: true, // converts all emails to lowercase before saving for consistency unique: true // enforce uniqueness, error 11000 if duplicate is detected + }, + password: { + type: String, + required: true, + select: false // exclude password field by default when querying users for security } }, { From 17d11da27ddaef99c01c24bb8817aa767aa8b1cd Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Sat, 15 Nov 2025 18:09:35 -0700 Subject: [PATCH 2/5] Updated the user data model to include passwords. --- shatter-backend/package-lock.json | 6 ++++++ shatter-backend/src/controllers/user_controller.ts | 8 ++++---- shatter-backend/src/models/user.ts | 0 shatter-backend/src/server.ts | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) delete mode 100644 shatter-backend/src/models/user.ts diff --git a/shatter-backend/package-lock.json b/shatter-backend/package-lock.json index a80d3cc..90ce0dd 100644 --- a/shatter-backend/package-lock.json +++ b/shatter-backend/package-lock.json @@ -470,6 +470,7 @@ "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -596,6 +597,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -827,6 +829,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1325,6 +1328,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2035,6 +2039,7 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -3216,6 +3221,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/shatter-backend/src/controllers/user_controller.ts b/shatter-backend/src/controllers/user_controller.ts index 17189c4..ea0732e 100644 --- a/shatter-backend/src/controllers/user_controller.ts +++ b/shatter-backend/src/controllers/user_controller.ts @@ -24,16 +24,16 @@ export const createUser = async (req: Request, res: Response) => { try { // Destructure the req body sent by the client // The ?? {} ensures we don't get error if req.body is undefined - const { name, email } = req.body ?? {}; + const { name, email, password} = req.body ?? {}; // Basic validation to ensure both name and email are provided // if not respond with bad request and stop further processes - if (!name || !email) { - return res.status(400).json({ error: 'name and email required' }); + if (!name || !email || !password) { + return res.status(400).json({ error: 'name, email required, and password' }); } // create a new user doc in DB using Mongoose's .create() - const user = await User.create({ name, email }); + const user = await User.create({ name, email, password}); // respond with "created" and send back created user as JSON res.status(201).json(user); } catch(err: any) { diff --git a/shatter-backend/src/models/user.ts b/shatter-backend/src/models/user.ts deleted file mode 100644 index e69de29..0000000 diff --git a/shatter-backend/src/server.ts b/shatter-backend/src/server.ts index c23c38c..44a0e8b 100644 --- a/shatter-backend/src/server.ts +++ b/shatter-backend/src/server.ts @@ -21,7 +21,7 @@ async function start() { // start listening for incoming HTTP requests on chosen port app.listen(PORT, () => { - console.log('Server running on http://localhost:${PORT}'); + console.log(`Server running on http://localhost:${PORT}`); }); } catch (err) { // if connection goes wrong, log the error console.error('Failed to start server:', err); From f09713a9ad0d9737b1b9625fa66bc63826407b07 Mon Sep 17 00:00:00 2001 From: Inko-z Date: Thu, 6 Nov 2025 19:39:53 -0700 Subject: [PATCH 3/5] Added some basic changes nend to implement route fixes --- shatter-backend/src/models/user_model.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shatter-backend/src/models/user_model.ts b/shatter-backend/src/models/user_model.ts index baf8986..3fb1880 100644 --- a/shatter-backend/src/models/user_model.ts +++ b/shatter-backend/src/models/user_model.ts @@ -9,6 +9,7 @@ import { Schema, model } from 'mongoose'; export interface IUser { name: string; email: string; + password: string; } // Create the Mongoose Schema (the database blueprint) @@ -28,6 +29,11 @@ const UserSchema = new Schema( trim: true, lowercase: true, // converts all emails to lowercase before saving for consistency unique: true // enforce uniqueness, error 11000 if duplicate is detected + }, + password: { + type: String, + required: true, + select: false // exclude password field by default when querying users for security } }, { From 955980865d5e3b3be7bed992ef8703ba6a98c39f Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Sat, 15 Nov 2025 18:09:35 -0700 Subject: [PATCH 4/5] Updated the user data model to include passwords. --- shatter-backend/package-lock.json | 6 ++++++ shatter-backend/src/controllers/user_controller.ts | 8 ++++---- shatter-backend/src/server.ts | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/shatter-backend/package-lock.json b/shatter-backend/package-lock.json index a80d3cc..90ce0dd 100644 --- a/shatter-backend/package-lock.json +++ b/shatter-backend/package-lock.json @@ -470,6 +470,7 @@ "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -596,6 +597,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -827,6 +829,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1325,6 +1328,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2035,6 +2039,7 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -3216,6 +3221,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/shatter-backend/src/controllers/user_controller.ts b/shatter-backend/src/controllers/user_controller.ts index 17189c4..ea0732e 100644 --- a/shatter-backend/src/controllers/user_controller.ts +++ b/shatter-backend/src/controllers/user_controller.ts @@ -24,16 +24,16 @@ export const createUser = async (req: Request, res: Response) => { try { // Destructure the req body sent by the client // The ?? {} ensures we don't get error if req.body is undefined - const { name, email } = req.body ?? {}; + const { name, email, password} = req.body ?? {}; // Basic validation to ensure both name and email are provided // if not respond with bad request and stop further processes - if (!name || !email) { - return res.status(400).json({ error: 'name and email required' }); + if (!name || !email || !password) { + return res.status(400).json({ error: 'name, email required, and password' }); } // create a new user doc in DB using Mongoose's .create() - const user = await User.create({ name, email }); + const user = await User.create({ name, email, password}); // respond with "created" and send back created user as JSON res.status(201).json(user); } catch(err: any) { diff --git a/shatter-backend/src/server.ts b/shatter-backend/src/server.ts index c23c38c..44a0e8b 100644 --- a/shatter-backend/src/server.ts +++ b/shatter-backend/src/server.ts @@ -21,7 +21,7 @@ async function start() { // start listening for incoming HTTP requests on chosen port app.listen(PORT, () => { - console.log('Server running on http://localhost:${PORT}'); + console.log(`Server running on http://localhost:${PORT}`); }); } catch (err) { // if connection goes wrong, log the error console.error('Failed to start server:', err); From 53b233f25023d375edd79d741a729d13ad603315 Mon Sep 17 00:00:00 2001 From: Inko-z Date: Thu, 6 Nov 2025 19:39:53 -0700 Subject: [PATCH 5/5] Added some basic changes nend to implement route fixes --- shatter-backend/src/app.ts | 2 + .../src/controllers/event_controller.ts | 63 +++++++++++++++++++ shatter-backend/src/models/event_model.ts | 56 +++++++++++++++++ .../src/models/participant_model.ts | 26 ++++++++ shatter-backend/src/models/user_model.ts | 27 ++------ shatter-backend/src/routes/event_routes.ts | 11 ++++ shatter-backend/src/utils/event_utils.ts | 18 ++++++ 7 files changed, 181 insertions(+), 22 deletions(-) create mode 100644 shatter-backend/src/controllers/event_controller.ts create mode 100644 shatter-backend/src/models/event_model.ts create mode 100644 shatter-backend/src/models/participant_model.ts create mode 100644 shatter-backend/src/routes/event_routes.ts create mode 100644 shatter-backend/src/utils/event_utils.ts diff --git a/shatter-backend/src/app.ts b/shatter-backend/src/app.ts index 11e7050..70d0daa 100644 --- a/shatter-backend/src/app.ts +++ b/shatter-backend/src/app.ts @@ -1,6 +1,7 @@ import express from 'express'; import userRoutes from './routes/user_route'; // these routes define how to handle requests to /api/users import authRoutes from './routes/auth_routes'; +import eventRoutes from './routes/event_routes'; const app = express(); @@ -12,5 +13,6 @@ app.get('/', (_req, res) => { app.use('/api/users', userRoutes); app.use('/api/auth', authRoutes); +app.use('/api/events', eventRoutes); export default app; diff --git a/shatter-backend/src/controllers/event_controller.ts b/shatter-backend/src/controllers/event_controller.ts new file mode 100644 index 0000000..4f06d9b --- /dev/null +++ b/shatter-backend/src/controllers/event_controller.ts @@ -0,0 +1,63 @@ +import { Request, Response } from "express"; +import { Event } from "../models/event_model"; +import "../models/participant_model"; + +import {generateEventId, generateJoinCode} from "../utils/event_utils"; + + +export async function createEvent(req: Request, res: Response) { + try { + const { name, description, startDate, endDate, maxParticipant, currentState, createdBy } = req.body; + + if (!createdBy) { + return res.status(400).json({ success: false, error: "createdBy email is required" }); + } + + const eventId = generateEventId(); + const joinCode = generateJoinCode(); + + const event = new Event({ + eventId, + name, + description, + joinCode, + startDate, + endDate, + maxParticipant, + participants: [], + currentState, + createdBy, // required email field + }); + + const savedEvent = await event.save(); + + res.status(201).json({ success: true, event: savedEvent }); + } catch (err: any) { + res.status(500).json({ success: false, error: err.message }); + } +} + + +export async function getEventByJoinCode(req: Request, res: Response) { + try { + const { joinCode } = req.params; + + if (!joinCode) { + return res.status(400).json({ success: false, error: "joinCode is required" }); + } + + // Find event by joinCode and populate participants + const event = await Event.findOne({ joinCode }).populate("participants"); + + if (!event) { + return res.status(404).json({ success: false, error: "Event not found" }); + } + + res.status(200).json({ + success: true, + event, + }); + } catch (err: any) { + res.status(500).json({ success: false, error: err.message }); + } +} \ No newline at end of file diff --git a/shatter-backend/src/models/event_model.ts b/shatter-backend/src/models/event_model.ts new file mode 100644 index 0000000..fd5d74b --- /dev/null +++ b/shatter-backend/src/models/event_model.ts @@ -0,0 +1,56 @@ +import mongoose, { Schema, model, Document, Types } from "mongoose"; +import {User} from "../models/user_model"; + +import { IParticipant } from "./participant_model"; + +export interface IEvent extends Document { + eventId: string; + name: string; + description: string; + joinCode: string; + startDate: Date; + endDate: Date; + maxParticipant: number; + participants: mongoose.Types.DocumentArray; + currentState: string; + createdBy: string; +} + +const EventSchema = new Schema( + { + eventId: { type: String, required: true, unique: true }, + name: { type: String, required: true }, + description: { type: String, required: true }, + joinCode: { type: String, required: true, unique: true }, + startDate: { type: Date, required: true }, + endDate: { type: Date, required: true }, + maxParticipant: { type: Number, required: true }, + participants: [{ type: Types.ObjectId, ref: "Participant" }], + currentState: { type: String, required: true }, + createdBy: { + type: String, + required: true, + validate: { + validator: async function (email: string) { + const user = await User.findOne({ email }); + return !!user; // true if user exists + }, + message: "User with this email does not exist" + } + } + }, + { + timestamps: true, + } +); + +// Optional validation: ensure endDate is after startDate +EventSchema.pre("save", function (next) { + if (this.endDate <= this.startDate) { + next(new Error("endDate must be after startDate")); + } else { + next(); + } +}); + +export const Event = model("Event", EventSchema); diff --git a/shatter-backend/src/models/participant_model.ts b/shatter-backend/src/models/participant_model.ts new file mode 100644 index 0000000..8de7400 --- /dev/null +++ b/shatter-backend/src/models/participant_model.ts @@ -0,0 +1,26 @@ +import { Schema, model, Document } from "mongoose"; + +export interface IParticipant extends Document { + participantId: string | null; + name: string; + eventId: string; +} + +const ParticipantSchema = new Schema({ + participantId: { + type: String, + default: null, + }, + + name: { + type: String, + required: true, + }, + + eventId: { + type: String, + required: true, + }, +}); + +export const Participant = model("Participant", ParticipantSchema); diff --git a/shatter-backend/src/models/user_model.ts b/shatter-backend/src/models/user_model.ts index 23c1633..a93c6de 100644 --- a/shatter-backend/src/models/user_model.ts +++ b/shatter-backend/src/models/user_model.ts @@ -9,11 +9,7 @@ import { Schema, model } from 'mongoose'; export interface IUser { name: string; email: string; - passwordHash: string; - lastLogin?: Date; - passwordChangedAt?: Date; - createdAt?: Date; - updatedAt?: Date; + password: string; } // Create the Mongoose Schema (the database blueprint) @@ -31,26 +27,13 @@ const UserSchema = new Schema( type: String, required: true, trim: true, - lowercase: true, - unique: true, - index: true, - match: [ - /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/, - 'Please provide a valid email address' - ] + lowercase: true, // converts all emails to lowercase before saving for consistency + unique: true // enforce uniqueness, error 11000 if duplicate is detected }, - passwordHash: { + password: { type: String, required: true, - select: false // Don't return in queries by default - }, - lastLogin: { - type: Date, - default: null - }, - passwordChangedAt: { - type: Date, - default: null + select: false // exclude password field by default when querying users for security } }, { diff --git a/shatter-backend/src/routes/event_routes.ts b/shatter-backend/src/routes/event_routes.ts new file mode 100644 index 0000000..fe95fb1 --- /dev/null +++ b/shatter-backend/src/routes/event_routes.ts @@ -0,0 +1,11 @@ +import { Router } from 'express'; +import { createEvent, getEventByJoinCode } from '../controllers/event_controller'; + +const router = Router(); + +// POST /api/events - create a new event +router.post('/createEvent', createEvent); +router.get("/event/:joinCode", getEventByJoinCode); + + +export default router; \ No newline at end of file diff --git a/shatter-backend/src/utils/event_utils.ts b/shatter-backend/src/utils/event_utils.ts new file mode 100644 index 0000000..58023d6 --- /dev/null +++ b/shatter-backend/src/utils/event_utils.ts @@ -0,0 +1,18 @@ +import crypto from "crypto"; + +/** + * Generates a random hash string for eventId + * Example: 3f5a9c7d2e8b1a6f4c9d0e2b7a1c8f3d + */ +export function generateEventId(): string { + return crypto.randomBytes(16).toString("hex"); +} + +/** + * Generates a random 8-digit number string for joinCode + * Example: "48392017" + */ +export function generateJoinCode(): string { + const code = Math.floor(10000000 + Math.random() * 90000000); + return code.toString(); +}