Skip to content
Open
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
47 changes: 47 additions & 0 deletions 20260218_1200_create_main_tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
PRAGMA foreign_keys = ON;

CREATE TABLE IF NOT EXISTS labels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
label TEXT UNIQUE NOT NULL
);

CREATE TABLE IF NOT EXISTS printers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
location TEXT,
description TEXT,
latitude REAL,
longitude REAL
);

CREATE TABLE IF NOT EXISTS printer_labels (
printer_id INTEGER NOT NULL,
label_id INTEGER NOT NULL,
PRIMARY KEY (printer_id, label_id),
FOREIGN KEY (printer_id) REFERENCES printers(id) ON DELETE CASCADE,
FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS event_forms (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
netid TEXT NOT NULL,
event_type TEXT NOT NULL,
start_date DATETIME,
end_date DATETIME,
organization_name TEXT,
about TEXT,
location TEXT NOT NULL,
image_url TEXT,
approval_status TEXT NOT NULL DEFAULT 'pending' CHECK(approval_status IN ('pending', 'approved', 'rejected')),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TRIGGER trg_event_forms_updated_at
AFTER UPDATE ON event_forms
FOR EACH ROW
BEGIN
UPDATE event_forms
SET updated_at = CURRENT_TIMESTAMP
WHERE id = OLD.id;
END;
143 changes: 143 additions & 0 deletions EventFormsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import express from "express";
import multer from "multer";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { createEventForm, getAllEventForms, updateEventForm, getApprovedEventForms, toPublicEvent, eventsByNetid, getImageByFormId } from "../utils/EventFormsUtils.js";

const router = express.Router();
const upload = multer({ storage: multer.memoryStorage() });

// 1. Initialize the S3 Client for DigitalOcean
const s3Client = new S3Client({
endpoint: "https://nyc3.digitaloceanspaces.com",
forcePathStyle: false,
region: "us-east-1",
credentials: {
accessKeyId: process.env.FORM_S3_ACCESS_KEY, //MUST INCLUDE THIS FOR IT TO WORK (located in .env)
secretAccessKey: process.env.FORM_S3_SECRET_KEY
}
});

// Create an event form
router.post("/events/create-event", upload.single('image'), async (req, res) => {

try {
const { netid, name, eventType, startDate, endDate, organizationName, location, about } = req.body;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation looks solid, but for the netid, allowing the user to just input this in the body is not secure. We should instead extract the user's netid from their login information, hence this must be a protected route if I am understanding the purpose of this event forms correctly.

//access the image from req.file
const file = req.file;
let fileUrl = null;

//check if image was actually uploaded
if (file) {
const params = {
Bucket: process.env.FORM_IMAGE_BUCKET_NAME,
Key: `events/${Date.now()}_${file.originalname}`,
Body: file.buffer,
ACL: "public-read",
ContentType: file.mimetype
};
// Upload to DigitalOcean
await s3Client.send(new PutObjectCommand(params));
fileUrl = `https://${process.env.FORM_IMAGE_BUCKET_NAME}.nyc3.digitaloceanspaces.com/${params.Key}`;
//EDIT to have the correct path for correct folder

}

const eventForm = await createEventForm({ netid, name, eventType, startDate, endDate, organizationName, location, about, image_url: fileUrl});

// Broadcast a notification to the admin room that the event form has been created
const io = req.app.get("io");
io.to("admin").emit("eventForm:new", {message: "Event request submitted", event: eventForm});

// Return the event form to the requesting client
res.status(201).json({ success: true, message: "Your event request has been submitted", data: toPublicEvent(eventForm)});
} catch (error) {
console.error("Error creating event form:", error);
res.status(400).json({ success: false, message: "Error submitting event request", error: error.message });
}
});

// Get all event forms
router.get("/events/", async (req, res) => {
try {
const eventForms = await getAllEventForms();
res.status(200).json({ success: true, message: "All event requests retrieved successfully", data: eventForms.map(toPublicEvent)});
} catch (error) {
console.error("Error getting all event forms:", error.message);
res.status(400).json({ success: false, message: "Error getting all event requests", error: error.message });
}
});

// Update an event form
// NOTE: Only admins can update event forms
// NOTE: id is the event form's id, stored as the primary key in the database
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the check/auth to make sure the user is an admin?

router.put("/events/:id", async (req, res) => {
try {
const { id } = req.params; // id is found in the url path
const { approvalStatus } = req.body;

// Initalize the io instance
const io = req.app.get("io");

// Update the event form in the database
const eventForm = await updateEventForm({ id: parseInt(id), approvalStatus });

// Handle event approval (currentl assumes that an update is only for approval or rejection, excludes pending)
if (approvalStatus === "approved") {
// Send a notification to everyone (and the admin room)
io.to("public").emit("eventForm:update", {message: "Event approved", event: toPublicEvent(eventForm)});
} else {
// Send a notification to the submitting user (and the admin room) that the event was rejected
io.to(`netid:${eventForm.netid}`).emit("eventForm:update", {message: "Your event request has been rejected", event: toPublicEvent(eventForm)});
}

// Return the event form to the admin client that requested the update
res.status(200).json({ success: true, message: "Event request updated successfully", data: eventForm});
} catch (error) {
console.error("Error updating event form:", error.message);
res.status(400).json({ success: false, message: "Error updating event request", error: error.message });
}
});

// Get all approved event forms
router.get("/events/approved", async (req, res) => {
try {
const eventForms = await getApprovedEventForms();
res.status(200).json({ success: true, message: "All approved event requests retrieved successfully", data: eventForms.map(toPublicEvent)});
} catch (error) {
console.error("Error getting all approved event requests:", error.message);
res.status(400).json({ success: false, message: "Error getting all approved event requests", error: error.message });
}
});

// Get event forms by netID
router.get("/events/:netid", async (req, res) => {
try {
const {netid} = req.params; // netid is found in the url path
const eventForms = await eventsByNetid(netid);
res.status(200).json({ success: true, message: "All events matching the netid retrieved successfully", data: eventForms.map(toPublicEvent)});
} catch (error) {
console.error("Error getting all events matching the netid:", error.message);
res.status(400).json({ success: false, message: "Error getting all approved event requests", error: error.message });
}
});

// Get image by event form ID
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this route? The GET /events/ already has the image urls in the response. Unless we need this for testing

router.get("/events/images/:id", async (req, res) => {
try {
const {id} = req.params; // id is found in the url path
const url = await getImageByFormId(id);
if (url == null){
res.status(200).json({ success: true, message: "This form does not contain an image", data: null});
}
else
{
res.status(200).json({ success: true, message: "The image requested by id retrieved successfully", data: url});
}
} catch (error) {
console.error("Error getting all approved event requests:", error.message);
res.status(400).json({ success: false, message: "Error getting all approved event requests", error: error.message });
}
});

export default router;
Loading