-
Notifications
You must be signed in to change notification settings - Fork 2
Event Form - Wyatt #365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Event Form - Wyatt #365
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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; |
| 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; | ||
|
|
||
| //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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment.
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.