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
27 changes: 20 additions & 7 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 ImageRepo = require("../repository/image/ImageRepo");

const setDefaultImageDimensions = (tierName) => {
switch (tierName.toLowerCase()) {
Expand Down Expand Up @@ -31,7 +32,7 @@ class EventSponsorController {
            id: s.id,
            name: s.sponsorName,
            website: s.sponsorWebsite,
            image: s.sponsorImageId || "",
            imageUrl: s.Image?.url || "",
amount: s.amount ?? 0,
            tier: eventSponsor?.SponsorTier?.tier || "",
          };
Expand Down Expand Up @@ -65,7 +66,7 @@ class EventSponsorController {
            id: s.id,
            name: s.sponsorName,
            website: s.sponsorWebsite,
            image: s.sponsorImageId || "",
            imageUrl: s.Image?.url || "",
sponsorTierId: eventSponsor?.sponsorTierId
          };
        });
Expand All @@ -80,16 +81,22 @@ class EventSponsorController {
//    Add sponsor to an event
    static async addSponsorToEvent(req, res){
        try {
          const { sponsorName, sponsorWebsite, image, amount, sponsorTierId, eventId } = req.body;
          const { sponsorName, sponsorWebsite, imageUrl, amount, sponsorTierId, eventId } = req.body;

if (!eventId || !sponsorName) {
return res.status(400).json({ error: "Missing required fields: eventId and sponsorName are required." });
}

const imageId = await ImageRepo.createImage({url: imageUrl})

if (!imageId) {
return res.status(400).json({ error: "Image could not be stored." });
}

          const result = await EventSponsorRepo.addSponsorToEvent(eventId, {
            sponsorName,
            sponsorWebsite,
            image,
            sponsorImageId: imageId,
amount: Number(amount),
            sponsorTierId
          });
Expand All @@ -105,13 +112,19 @@ class EventSponsorController {
    static async updateEventSponsor(req, res){
        try{
            const sponsorId = req.params.id;
            const { sponsorName, sponsorWebsite, image, amount, sponsorTierId, eventId, ...otherUpdates } = req.body;

            const { sponsorName, sponsorWebsite, imageUrl, amount, sponsorTierId, eventId, ...otherUpdates } = req.body;

const imageId = await ImageRepo.createImage({url: imageUrl})

if (!imageId) {
return res.status(400).json({ error: "Image could not be stored." });
}

// --- 1. Separate Updates for Sponsor (Core) Table ---
const sponsorUpdates = {
...(sponsorName !== undefined && { sponsorName }),
...(sponsorWebsite !== undefined && { sponsorWebsite }),
...(image !== undefined && { sponsorImageId: image }),
...({ sponsorImageId: imageId }),
...(amount !== undefined && { amount: Number(amount) }),
};

Expand Down
103 changes: 103 additions & 0 deletions src/controllers/SponsorImagesController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const SponsorImage = require('../models/SponsorImage');
const SponsorRepo = require('../repository/sponsor/SponsorRepo');

const SponsorImagesController = {
/**
* Get all images for a specific sponsor.
* @param {object} req - Express request object (expects req.params.sponsorId)
* @param {object} res - Express response object
* @returns {Promise<void>}
*/
async getImagesBySponsorId(req, res) {
try {
const sponsorId = req.params.sponsorId;
// Assuming SponsorRepo has a method to fetch images by sponsor ID
const images = await SponsorRepo.getImagesBySponsorId(sponsorId);

if (!images || images.length === 0) {
// Return 200 with an empty array if no images are found, or 404 if you prefer strict REST
return res.status(200).json([]);
// return res.status(404).json({ message: "No images found for this sponsor" });
}

res.json(images);
} catch (err) {
console.error("Error fetching sponsor images: ", err);
res.status(500).json({
message: "Failed to fetch sponsor images",
error: err.message
});
}
},

/**
* Create a new image and link it to a sponsor.
* @param {object} req - Express request object (expects req.body: { imageUrl, sponsorId })
* @param {object} res - Express response object
* @returns {Promise<void>}
*/
async createImage(req, res) {
try {
const {
imageUrl,
sponsorId
} = req.body;

if (!imageUrl || !sponsorId) {
return res.status(400).json({
message: "imageUrl and sponsorId are required"
});
}

// Assuming SponsorRepo has a method to create a new sponsor image record
const newImage = await SponsorRepo.createSponsorImage({
imageUrl,
sponsorId
});

res.status(201).json({
message: "Image added successfully",
image: newImage
});
} catch (err) {
console.error("Error adding sponsor image: ", err);
res.status(500).json({
message: "Failed to add sponsor image",
error: err.message
});
}
},

/**
* Delete an image by its ID.
* @param {object} req - Express request object (expects req.params.id)
* @param {object} res - Express response object
* @returns {Promise<void>}
*/
async deleteImage(req, res) {
try {
const imageId = req.params.id;
// Assuming SponsorRepo has a method to delete a sponsor image record
const deleted = await SponsorRepo.deleteSponsorImage(imageId);

if (deleted === 0) {
return res.status(404).json({
message: "Image not found"
});
}

res.json({
message: "Image deleted successfully",
result: deleted
});
} catch (err) {
console.error("Error deleting sponsor image: ", err);
res.status(500).json({
message: "Failed to delete sponsor image",
error: err.message
});
}
}
};

module.exports = SponsorImagesController;
4 changes: 4 additions & 0 deletions src/middleware/validationMiddleware.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
const checkBodyForSpecialCharacters = (req, res, next) => {
// Fields we want to skip
const ignoreFields = ["imageUrl"];

// Blocks characters often used in attacks ($, %, #, <, >, etc.)
const specialCharRegex = /[^a-zA-Z0-9\s-',\.:/\?&_=()@]/g

for (const key in req.body) {
if (ignoreFields.includes(key)) continue; // Skip validation for fields we don't want to check
const value = req.body[key];

// Only check string values
Expand Down
34 changes: 34 additions & 0 deletions src/models/SponsorImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const Image = require('./Image');

class SponsorImage extends Image {

constructor(url, sponsorId, id = null) {
super(url);
this.id = id;
this.sponsorId = sponsorId;
}


validate() {
const errors = super.validate();

if (this.sponsorId === undefined || this.sponsorId === null) {
errors.push("Missing Sponsor ID");
}
if (typeof this.sponsorId !== 'number' || this.sponsorId <= 0) {
errors.push("Invalid Sponsor ID");
}

return errors;
}

toJSON() {
return {
id: this.id,
imageUrl: this.url,
sponsorId: this.sponsorId
};
}
}

module.exports = SponsorImage;
4 changes: 4 additions & 0 deletions src/repository/config/Models.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ Hardware.hasMany(HardwareImage, {
as: 'images'
});
HardwareImage.belongsTo(Hardware, { foreignKey: 'hardwareId' });

/* SPONSOR/IMAGE Association */
Sponsor.belongsTo(Image, { foreignKey: "sponsorImageId" });

// Function to attach model hooks
function attachAuditHooks() {
const { AuditLog } = sequelize.models; // Grab all the models
Expand Down
10 changes: 10 additions & 0 deletions src/repository/image/ImageRepo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const Image = require("./Image")

const ImageRepo = {
async createImage(image) {
const result = await Image.create(image);
return result.id;
}
}

module.exports = ImageRepo
10 changes: 8 additions & 2 deletions src/repository/sponsor/EventSponsorRepo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const EventSponsor = require("./EventSponsor");
const SponsorRepo = require("./SponsorRepo");
const Sponsor = require("./Sponsor");
const SponsorTier = require("./SponsorTier");
const Image = require("../image/Image");
const { Op } = require('sequelize');

class EventSponsorRepo {
Expand All @@ -20,7 +21,12 @@ class EventSponsorRepo {
required: false
}
]
}
},
{
model: Image,
attributes: ["id", "url"],
required: false
}
],
attributes: ["id", "sponsorName", "sponsorWebsite", "sponsorImageId", "amount"]
});
Expand All @@ -31,7 +37,7 @@ class EventSponsorRepo {
const sponsor = await SponsorRepo.createSponsor({
sponsorName: sponsorData.sponsorName,
sponsorWebsite: sponsorData.sponsorWebsite,
sponsorImageId: sponsorData.image || null,
sponsorImageId: sponsorData.sponsorImageId || null,
amount: sponsorData.amount
});

Expand Down
76 changes: 51 additions & 25 deletions src/repository/sponsor/SponsorRepo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,93 @@ const { Sponsor, SponsorTier, EventSponsor, Image } = require("../config/Models"

const SponsorRepo = {
//Sponsor
async findSponsorById(id){
async findSponsorById(id) {
return Sponsor.findOne({
where: { id },
where: {id},
attributes: ['id', 'sponsorName', 'sponsorWebsite'],
include: [{ model: SponsorTier, as: "tiers", attributes: ["tier"] }]
include: [{model: SponsorTier, as: "tiers", attributes: ["tier"]}]
});
},
async findAllSponsors() {
return Sponsor.findAll({
attributes: ['id', 'sponsorName', 'sponsorWebsite'],
include: [
{
model: EventSponsor,
as: 'EventSponsors',
return Sponsor.findAll({
attributes: ['id', 'sponsorName', 'sponsorWebsite'],
include: [
{ model: SponsorTier, as: 'SponsorTier', attributes: ['tier'] }
]
}
{
model: EventSponsor,
as: 'EventSponsors',
include: [
{model: SponsorTier, as: 'SponsorTier', attributes: ['tier']}
]
}
// { model: Image, as: 'image', attributes: ['url'] } // adjust field
]
});
]
});
},
async createSponsor(sponsor){
async createSponsor(sponsor) {
return Sponsor.create(sponsor);
},
async updateSponsor(id, updates){
async updateSponsor(id, updates) {
return Sponsor.update(updates, {where: {id}, individualHooks: true});
},
async deleteSponsorById(id){
async deleteSponsorById(id) {
return Sponsor.destroy({where: {id}, individualHooks: true});
},

//EventSponsor
async findEventSponsorsById(id){
async findEventSponsorsById(id) {
return EventSponsor.findAll({
where: { id },
where: {id},
include: [{model: Sponsor, include: {model: Image}}, {model: SponsorTier}]
});
},
async findEventSponsorsByEvent(eventId){
async findEventSponsorsByEvent(eventId) {
return EventSponsor.findAll({
where: {event_id: eventId},
include: [{model: Sponsor, include: {model: Image}}, {model: SponsorTier}]
});
},
async createEventSponsor(eventSponsor){
async createEventSponsor(eventSponsor) {
return EventSponsor.create(eventSponsor);
},

//SponsorTier
async createSponsorTier(sponsorTier){
async createSponsorTier(sponsorTier) {
return SponsorTier.create(sponsorTier);
},
async deleteSponsorTierById(id){
async deleteSponsorTierById(id) {
return SponsorTier.destroy({
where: { id },
where: {id},
individualHooks: true
});
},
async getAllSponsorTier(){
async getAllSponsorTier() {
return SponsorTier.findAll({});
},

async getImagesBySponsorId(sponsorId) {
return Image.findAll({
where: {sponsorId: sponsorId},
attributes: ['id', 'url', 'sponsorId']
});
},

/**
* Creates a new sponsor image record in the database.
* @param {object} imageDetails - { imageUrl: string, sponsorId: number }
* @returns {Promise<SponsorImage>} The newly created SponsorImage instance with its ID.
*/
async createSponsorImage({imageUrl, sponsorId}) {
return Image.create({
url: imageUrl,
sponsorId: sponsorId
});
},

async deleteSponsorImage(imageId) {
return Image.destroy({
where: {id: imageId},
individualHooks: true
});
}
}

Expand Down
Loading