Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"moment": "^2.24.0",
"mongoose": "^5.9.3",
"morgan": "^1.9.1",
"multer": "^1.4.5-lts.2",
"newrelic": "^9.15.0",
"pm2": "^5.2.2",
"twilio": "^4.10.0"
Expand Down
2 changes: 2 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const notifications = require("./routes/notifications");
const settings = require("./routes/settings");
const slotting = require("./routes/slotting");
const feedback = require("./routes/feedback");
const teams = require("./routes/teams");

app.use("/colleges", collegesRouter);
app.use("/events", eventsRouter);
Expand All @@ -90,6 +91,7 @@ app.use("/notifications", notifications);
app.use("/settings", settings);
app.use("/slotting", slotting);
app.use("/feedback", feedback)
app.use("/teams", teams);

// Error handlers
app.use(handle404);
Expand Down
73 changes: 73 additions & 0 deletions src/controllers/teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const TeamModel = require("../models/Team");
const EventModel = require("../models/Event");
const ParticipantModel = require("../models/Participant");
const path = require("path");
const fs = require("fs");
const WinnerSubmission = require("../models/Winners");

/**
* Add new team into the system.
Expand Down Expand Up @@ -128,9 +131,79 @@ const deleteOne = async (req, res) => {
}
};


const getTeamByCollegeIdAndEventId = async (req, res) => {
try {
const { collegeId, eventId } = req.params;
if(!collegeId || !eventId){
return res.status(400).json({
status: 400,
message: "Bad Request",
});
}
let team = await TeamModel.findOne({ event: eventId, college: collegeId });

return res.status(200).json({
status: 200,
message: "Success",
data: team,
});
} catch (e) {
return res.status(500).json({
status: 500,
message: "Internal Server Error",
});
}
};

// Regular expression for PAN validation (case-insensitive)
const VALID_PAN_REGEX = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/i;
const submitWinners = async (req, res) => {
console.log("Received files array:", JSON.stringify(req.files, null, 2));
try {
// Parse the participants string into an array
const participants = JSON.parse(req.body.participants);

// Map frontend fields to schema fields
const processedParticipants = participants.map((p, i) => {
const panFile = req.files.find(file => file.fieldname === `panPhoto-${i}`);
const chequeFile = req.files.find(file => file.fieldname === `chequePhoto-${i}`);

return {
name: p.name,
regNo: p.regNumber,
pan: p.panNumber,
accountNumber: p.bankAccount,
bankName: p.bankName,
branch: p.branch,
ifsc: p.ifsc,
phone: p.phone,
panPhotoPath: panFile ? panFile.path : null,
chequePhotoPath: chequeFile ? chequeFile.path : null
};
});

// Create and save the WinnerSubmission document
const winnerSubmission = new WinnerSubmission({
collegeId: req.body.collegeId,
eventId: req.body.eventId,
participants: processedParticipants
});

await winnerSubmission.save();
res.status(200).json({ message: 'Winners submitted successfully' });
} catch (error) {
console.error('Winner submission error:', error);
res.status(500).json({ error: 'Failed to submit winners' });
}
};


module.exports = {
create,
get,
getAll,
deleteOne,
submitWinners,
getTeamByCollegeIdAndEventId,
};
145 changes: 145 additions & 0 deletions src/middlewares/winnerForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
const path = require('path');
const multer = require('multer');
const fs = require('fs'); // Require fs for directory check

/**
* Multer disk storage configuration.
* Defines how uploaded files are stored.
*/
const storage = multer.diskStorage({
/**
* Sets the destination directory for uploaded files.
* @param {object} req - The request object.
* @param {object} file - The file object.
* @param {function} cb - The callback function.
*/
destination: function (req, file, cb) {
const uploadPath = 'uploads/';
// Ensure the upload directory exists
fs.mkdirSync(uploadPath, { recursive: true }); // Create directory if it doesn't exist
cb(null, uploadPath);
},

/**
* Generates a unique filename for the uploaded file.
* Filename format: eventId-registrationNumber-fieldname.ext (CHANGED)
* @param {object} req - The request object.
* @param {object} file - The file object.
* @param {function} cb - The callback function.
*/
filename: function (req, file, cb) {
try {
// Get the file extension
const ext = path.extname(file.originalname);
// Extract the index from the fieldname (e.g., 'panPhoto-0' -> '0')
const fieldnameParts = file.fieldname.split('-');
const index = fieldnameParts[fieldnameParts.length - 1]; // Get last part as index

// Validate index
if (index === undefined || isNaN(parseInt(index))) {
return cb(new Error(`Invalid file fieldname format: ${file.fieldname}. Expected format like 'fieldName-index'.`));
}
const participantIndex = parseInt(index);

// Get eventId from the request body
const eventId = req.body.eventId;

// --- Access Participants Data ---
// NOTE: Accessing req.body here can be fragile with upload.any() as
// body might not be fully parsed when this runs for the *first* file.
// It seems to work in your case, but consider alternatives if issues arise.
let participants;
try {
// Assume participants is sent as a JSON string
participants = JSON.parse(req.body.participants);
if (!Array.isArray(participants)) throw new Error("Not an array");
} catch (parseError) {
console.error("Multer filename function failed to parse req.body.participants:", parseError);
// Cannot reliably get regNumber, use a fallback naming scheme
const fallbackFilename = `${Date.now()}-${file.fieldname}${ext}`;
console.warn(`Using fallback filename: ${fallbackFilename}`);
return cb(null, fallbackFilename);
// Or return an error: return cb(new Error("Could not parse participants data to generate filename"));
}

// --- Input Validation ---
if (!eventId) {
// Use fallback if eventId is missing
const fallbackFilename = `${Date.now()}-${file.fieldname}${ext}`;
console.warn(`Missing eventId, using fallback filename: ${fallbackFilename}`);
return cb(null, fallbackFilename);
// return cb(new Error("Missing event ID in request body"));
}

if (participantIndex >= participants.length || participantIndex < 0) {
return cb(new Error(`Participant index ${participantIndex} is out of bounds (Array length: ${participants.length})`));
}

// Get the specific participant object using the index
const participant = participants[participantIndex];

if (!participant || typeof participant !== 'object') {
return cb(new Error(`Invalid participant data found at index ${participantIndex}`));
}

// Get the registration number from the participant object
const registrationNumber = participant.regNumber;

if (!registrationNumber) {
return cb(new Error(`Missing registration number for participant at index ${participantIndex}`));
}

// --- Construct the filename (ADDED fieldname for uniqueness) ---
// Format: eventId-regNumber-fieldname.ext
// Example: 67cb...-240970053-panPhoto-0.png
const uniqueFilename = `${eventId}-${registrationNumber}-${file.fieldname}${ext}`;

console.log(`Generated filename for ${file.originalname}: ${uniqueFilename}`); // Log generated name
cb(null, uniqueFilename);

} catch (error) {
// Catch any unexpected errors during filename generation
console.error("Unexpected error in multer filename function:", error);
cb(error); // Pass error to multer
}
}
});

/**
* File filter function to allow only specific file types.
* @param {object} req - The request object.
* @param {object} file - The file object.
* @param {function} cb - The callback function.
*/
const fileFilter = (req, file, cb) => {
// Define allowed MIME types
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
console.log(`Filtering file: ${file.originalname}, MIME type: ${file.mimetype}`); // Log file info

// Check if the file's MIME type is allowed
if (allowedTypes.includes(file.mimetype)) {
console.log(`Allowing file: ${file.originalname}`);
cb(null, true); // Accept the file
} else {
console.error(`Rejecting file (invalid type): ${file.originalname}`); // Log rejection
// Reject the file silently (won't appear in req.files, won't throw error)
cb(null, false);
// Alternatively, reject with an error:
// cb(new Error('Invalid file type. Only images (JPEG, PNG) and PDFs are allowed'), false);
}
};

/**
* Multer upload configuration.
* Combines storage, file filter, and size limits.
*/
const upload = multer({
storage: storage, // Use the defined disk storage
fileFilter: fileFilter, // Use the defined file filter
limits: {
fileSize: 2 * 1024 * 1024 // Set file size limit to 2 MB
}
});

// Export the configured multer instance
module.exports = upload;
67 changes: 67 additions & 0 deletions src/models/Winners.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const mongoose = require("mongoose");

const participantSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
regNo: {
type: String,
required: true
},
pan: {
type: String,
required: true
},
panPhotoPath: {
type: String,
required: true
},
accountNumber: {
type: String,
required: true
},
bankName: {
type: String,
required: true
},
branch: {
type: String,
required: true
},
ifsc: {
type: String,
required: true
},
phone: {
type: String,
required: true
},
chequePhotoPath: {
type: String,
required: true
}
}, { _id: false });

const winnerFormData = new mongoose.Schema({
collegeId: {
type: mongoose.Schema.Types.ObjectId,
ref: "College",
required: true
},
eventId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Event",
required: true
},
participants: {
type: [participantSchema],
required: true
},
submittedAt: {
type: Date,
default: Date.now
}
});

module.exports = mongoose.model("WinnerSubmission", winnerFormData);
4 changes: 4 additions & 0 deletions src/routes/teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ const express = require("express");
const router = express.Router();

const Teams = require("../controllers/teams");
const upload = require("../middlewares/winnerForm");

// Returns the list of all teams
router.get("/", Teams.getAll);
router.get("/:collegeId/:eventId", Teams.getTeamByCollegeIdAndEventId);
router.post("/submitWinnerForm", upload.any() ,Teams.submitWinners);


module.exports = router;