Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ DB_NAME=ycp_hacks
DB_PORT=3306
APP_PORT=3000
CORS='*'
APP_EMAIL=ycphacks.register@gmail.com
APP_EMAIL_PASSWORD=zter cbky dxdv ysfz
EMAIL_API_KEY=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
FROM_EMAIL=ycphacks.register@gmail.com
DO_ACCESS_KEY=DO801PR3ZXMYVT27FNLY
DO_SECRET_KEY=01XlfE+v4xtA0lLQJaNM9PYt9ZeuA+QvoGst9mcetNQ
DO_SPACE_ENDPOINT=https://nyc3.digitaloceanspaces.com
Expand Down
Binary file removed AWSCLIV2.pkg
Binary file not shown.
189 changes: 184 additions & 5 deletions src/controllers/EventParticipantsController.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const EventParticipantsRepo = require('../repository/team/EventParticipantRepo');
const TeamRepo = require('../repository/team/TeamRepo')
const EventRepo = require('../repository/event/EventRepo')
const UserRepo = require('../repository/user/UserRepo')

class EventParticipantController{
static async getUnassignedParticipants(req, res) {
Expand All @@ -8,15 +11,15 @@ class EventParticipantController{
const participants = await EventParticipantsRepo.findUnassignedParticipants(eventId);

const nonBannedParticipants = participants.filter(p =>
p.userDetails && p.userDetails.isBanned !== true && p.userDetails.isBanned !== 1
p.participants && p.participants.isBanned !== true && p.participants.isBanned !== 1
);

const formattedParticipants = nonBannedParticipants.map(p => ({
id: p.userId,
firstName: p.userDetails?.firstName,
lastName: p.userDetails?.lastName,
email: p.userDetails?.email,
checkIn: p.userDetails?.checkIn,
firstName: p.participants?.firstName,
lastName: p.participants?.lastName,
email: p.participants?.email,
checkIn: p.participants?.checkIn,
teamId: p.teamId
}));

Expand Down Expand Up @@ -94,6 +97,182 @@ class EventParticipantController{
return res.status(500).json({ message: 'Server error retrieving team status' });
}
}
async getTeamsByEvent(eventId) {
return await Team.findAll({
where: {
eventId: eventId
},
include: [
{
model: EventParticipant,
as: 'EventParticipants',
attributes: ['userId', 'teamId'],
include: [{
model: User,
as: 'participants',
attributes: ['id', 'firstName', 'lastName'],
required: true
}]
}
],
// Select the specific fields needed for the response mapping
attributes: [
'id',
'teamName',
'presentationLink',
'githubLink',
'projectName',
'projectDescription'
],
});
}
static async addParticipantToEvent(req, res){
const {userId, eventId} = req.body;

if(!userId || !eventId){
return res.status(400).json({ message: 'Missing userId or eventId in request body.' });
}

try {
// The repository method should handle creating the new EventParticipant record.
const newParticipant = await EventParticipantsRepo.addParticipant(userId, eventId);

return res.status(201).json({
message: `User ${userId} successfully added as participant to Event ${eventId}.`,
data: newParticipant
});
} catch (error) {
console.error("Error adding participant to event:", error);
// Check for specific error like duplicate entry if the repo provides it
if (error.message.includes('duplicate')) {
return res.status(409).json({ message: 'Participant is already registered for this event.', error: error.message });
}
res.status(500).json({
message: 'Failed to add participant to event.',
error: error.message
});
}
}
static async getTeamsByEvent(req, res) {
try {
// 1. Get eventId from query parameters
let { eventId } = req.query;

if (eventId === 'undefined' || eventId === '') {
const activeEvent = await EventRepo.findActiveEvent();

if (!activeEvent) {
return res.status(404).json({ error: "No eventId provided and no active event found." });
}
eventId = activeEvent.id;
}

if (!eventId) {
return res.status(500).json({ error: "Internal error: Failed to determine event ID." });
}

// 2. Call a new repository function to get teams filtered by event
const teams = await TeamRepo.getTeamsByEvent(eventId);

// 3. Map the data for the response
const teamData = teams.map(team => ({
id: team.id,
teamName: team.teamName,
presentationLink: team.presentationLink || null,
githubLink: team.githubLink || null,
projectName: team.projectName || null,
projectDescription: team.projectDescription || null,

// Map the team members list
participants: team.EventParticipants ?
team.EventParticipants.map(participant => {
const user = participant.participants;

// Safety check remains valid
if (!user) {
console.warn(`Participant record in team ${team.id} is missing User details.`);
return 'Unknown User';
}

return {
id: user.id,
firstName: user.firstName,
lastName: user.lastName
};
})
: []
}));

res.status(200).json({
message: `Successfully fetched teams for event ${eventId}`,
data: teamData
});

} catch (err) {
console.error('Error getting teams by event:', err);
res.status(500).json({ message: 'Error getting teams by event', error: err.message });
}
}
static async getUsersByEvent(req, res){
try {
// 1. Get eventId from query parameters
let { eventId } = req.query;
if (eventId === 'undefined' || eventId === '') {

const activeEvent = await EventRepo.findActiveEvent();

if (!activeEvent) {
return res.status(404).json({ error: "No eventId provided and no active event found." });
}
eventId = activeEvent.id;
}

        if (!eventId) {
return res.status(500).json({ error: "Internal error: Failed to determine active event ID." });
}

// 2. Call a new repository function to get users filtered by event
const users = await EventParticipantsRepo.getUsersByEvent(eventId);

// 3. Map the data
const userData = users.map(user => ({
id: user.dataValues.id,
firstName: user.dataValues.firstName,
lastName: user.dataValues.lastName,
age: user.dataValues.age,
email: user.dataValues.email,
phoneNumber: user.dataValues.phoneNumber,
school: user.dataValues.school,
tShirtSize: user.dataValues.tShirtSize,
dietaryRestrictions: user.dataValues.dietaryRestrictions,
role: user.dataValues.role,
checkIn: user.dataValues.checkIn,
isBanned: user.dataValues.isBanned
}));

res.status(200).json({ message: `Successfully fetched users for event ${eventId}`, data: userData });
} catch (err) {
res.status(500).json({ message: 'Error getting users by event', error: err.message });
}
}
static async getStaffForEvent(req, res){
// Assuming eventId is passed via URL params, e.g., /api/events/1/staff
const eventId = req.params.eventId;

if (!eventId) {
return res.status(400).json({ message: 'Missing event ID in request.' });
}

try {
const staff = await UserRepo.getStaffForEvent(eventId);

// Send the data back to the frontend
res.status(200).json(staff);
} catch (error) {
console.error("Staff route error:", error);
res.status(500).json({ message: "Internal server error: Failed to fetch staff list." });
}
};
}

module.exports = EventParticipantController;
12 changes: 10 additions & 2 deletions src/controllers/EventSponsorController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const EventSponsorRepo = require("../repository/sponsor/EventSponsorRepo");
const SponsorRepo = require("../repository/sponsor/SponsorRepo");
const EventRepo = require("../repository/event/EventRepo");
const ImageRepo = require("../repository/image/ImageRepo");

const setDefaultImageDimensions = (tierName) => {
Expand All @@ -21,8 +22,15 @@ class EventSponsorController {
//    Get all sponsors for a specific event
    static async getEventSponsors(req, res) {
      try {
        const { eventId } = req.query;
        if (!eventId) return res.status(400).json({ error: "eventId required" });
        let { eventId } = req.query;
        if (!eventId){
const activeEvent = await EventRepo.findActiveEvent();

if(!activeEvent){
return res.status(404).json({ error: "No eventId provided and no active event found." });
}
eventId = activeEvent.event.id;
}

        const sponsorsRaw = await EventSponsorRepo.getSponsorsByEvent(eventId);

Expand Down
4 changes: 2 additions & 2 deletions src/controllers/TeamController.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ class TeamController {
const participants = await EventParticipantsRepo.findParticipantsByTeamId(teamId);

const formattedParticipants = participants.map(p => ({
id: p.userDetails.id,
name: `${p.userDetails.firstName} ${p.userDetails.lastName}`
id: p.participants.id,
name: `${p.participants.firstName} ${p.participants.lastName}`
}));

return {
Expand Down
21 changes: 21 additions & 0 deletions src/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ const createUser = async (req, res) => {

// convert user into user model
const userData = req.body
const { eventId } = req.body;

if(!eventId){
return res.status(400).json({ message: 'Missing eventId in request body.' });
}

const isBanned = await UserRepo.checkIfBanned({
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.email
});

if (isBanned) {
return res.status(403).json({
message: 'Registration declined: This user has been banned from previous events.',
errors: { general: 'User is ineligible to register.' }
});
}

const hashedPassword = await bcrypt.hash(userData.password, SALT_ROUNDS);
const user = new User(
userData.firstName,
Expand Down Expand Up @@ -94,6 +113,8 @@ const createUser = async (req, res) => {
// persist user ONLY IF THE DATA IS VALID
const persistedUser = await UserRepo.create(userObj);

await EventParticipantRepo.addParticipant(persistedUser.id, eventId);

// generate JWT
const token = generateToken({ email: user.email });

Expand Down
10 changes: 5 additions & 5 deletions src/repository/config/Models.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ HackCategory.belongsTo(Event, {
onDelete: 'CASCADE',
});

Team.hasMany(EventParticipant, { foreignKey: 'teamId' });
EventParticipant.belongsTo(Team, { foreignKey: 'teamId' });
Team.hasMany(EventParticipant, { foreignKey: 'teamId', as: 'EventParticipants' });
EventParticipant.belongsTo(Team, { foreignKey: 'teamId', as: 'EventParticipants' });

EventParticipant.belongsTo(User, { foreignKey: 'userId', as: 'userDetails' });
User.hasMany(EventParticipant, { foreignKey: 'userId' });
EventParticipant.belongsTo(User, { foreignKey: 'userId', as: 'participants', targetKey: 'id' });
User.hasMany(EventParticipant, { foreignKey: 'userId', as:'participant' });

/* HARDWARE/IMAGE ASSOCIATIONS */
Hardware.hasMany(HardwareImage, {
Expand All @@ -103,7 +103,7 @@ function attachAuditHooks() {
return;
}

const ignored = ['AuditLog', 'User']; // We don't want to audit the audit table itself or include participant actions
const ignored = ['AuditLog', 'User', 'EventParticipant']; // We don't want to audit the audit table itself or include participant actions
const cleanData = (obj) => { // Remove sensitive/unnecessary information like password
if (!obj) return null;
const data = obj.toJSON();
Expand Down
2 changes: 1 addition & 1 deletion src/repository/sponsor/EventSponsorRepo.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class EventSponsorRepo {
{
model: EventSponsor,
where: { eventId },
required: false, // LEFT JOIN so sponsors without an EventSponsor row are included
required: true,
include: [
{
model: SponsorTier,
Expand Down
Loading