diff --git a/app.js b/app.js index 93873f2..fca4f53 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,5 @@ require("dotenv").config(); const express = require("express"); -const http = require("http"); const ejsMate = require("ejs-mate"); const cookieParser = require("cookie-parser"); const session = require("express-session"); @@ -12,12 +11,13 @@ const GoogleStrategy = require("passport-google-oauth2").Strategy; const { cspConfig, sessionConfig } = require("./config/general.config"); const db = require("./database/db"); const { fallible } = require("./middleware"); -const authRoutes = require("./routes/auth"); -const attendRoutes = require("./routes/attendance"); const adminRoutes = require("./routes/admin"); +const attendRoutes = require("./routes/attendance"); +const authRoutes = require("./routes/auth"); +const galleryRoutes = require("./routes/gallery"); +const presentationsRoutes = require("./routes/presentations"); const profileRoutes = require("./routes/profile"); const projectsRoutes = require("./routes/projects"); -const presentationsRoutes = require("./routes/presentations"); const scheduleRoutes = require("./routes/schedule"); const app = express(); @@ -101,6 +101,7 @@ app.use((req, res, next) => { app.use("/", adminRoutes); app.use("/", attendRoutes); app.use("/", authRoutes); +app.use("/", galleryRoutes); app.use("/", presentationsRoutes); app.use("/", profileRoutes); app.use("/", projectsRoutes); @@ -110,7 +111,7 @@ app.get( "/", fallible(async (req, res) => { const imageResp = await db.query( - "SELECT * FROM images ORDER BY RANDOM() LIMIT 1", + "SELECT * FROM images WHERE active = true ORDER BY RANDOM() LIMIT 1", ); const image = imageResp.rows[0]; @@ -166,11 +167,8 @@ app.use((req, res, next) => { app.use((err, req, res, next) => { let error; if (process.env.NODE_ENV === "development") { - // Possible security hazard if we expose the trace, so only display it - // in development mode. error = err.stack; } - res.status(500).render("error", { title: "Error", error: error }); }); diff --git a/database/init_database.sql b/database/init_database.sql index c4b18d6..23c0612 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -56,7 +56,8 @@ CREATE TABLE IF NOT EXISTS rsvps ( CREATE TABLE IF NOT EXISTS images ( "id" TEXT PRIMARY KEY, - "caption" TEXT + "caption" TEXT, + "active" BOOLEAN ); CREATE TABLE IF NOT EXISTS presentations ( diff --git a/middleware.js b/middleware.js index 67e0342..54b08c6 100644 --- a/middleware.js +++ b/middleware.js @@ -15,13 +15,22 @@ module.exports.isLoggedIn = (req, res, next) => { }; module.exports.isAdminAuthenticated = (req, res, next) => { - if (req.user == undefined || !req.user.is_admin || !req.isAuthenticated()) { - req.session.returnTo = req.originalUrl; - req.user = false; - req.flash("error", "You do not have permission to view this page!"); - return res.redirect("/"); - } else { + if (process.env.NODE_ENV === "development") { + // bypass admin auth in development mode next(); + } else { + if ( + req.user === undefined || + !req.user.is_admin || + !req.isAuthenticated() + ) { + req.session.returnTo = req.originalUrl; + req.user = false; + req.flash("error", "You do not have permission to view this page!"); + return res.redirect("/"); + } else { + next(); + } } }; diff --git a/package.json b/package.json index dff0e79..1fca2b7 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,12 @@ { "name": "acm-site", "version": "1.0.0", - "description": "The backend for the Mines ACM website.", + "description": "The Mines ACM club website, acm.mines.edu", "main": "app.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", "start": "sass --style=compressed public/stylesheets/styles.scss public/stylesheets/styles.css; node app.js" }, - "author": "", + "author": "Mines ACM Contributors", "license": "ISC", "dependencies": { "bootstrap": "^5.3.2", diff --git a/public/stylesheets/styles.scss b/public/stylesheets/styles.scss index 69f1cd4..bbf8e70 100644 --- a/public/stylesheets/styles.scss +++ b/public/stylesheets/styles.scss @@ -95,6 +95,13 @@ a { } // --- DEFAULT OVERRIDES --- +.btn-primary { + color: white; +} + +.btn:hover { + color: white; +} .container { @extend .py-5; @@ -139,6 +146,31 @@ nav .nav-item { } // --- NEW STYLES --- +.gallery-container { + background-color: white; + border-bottom: 4px solid $primary; + display: flex; + flex-wrap: wrap; + row-gap: 15px; + column-gap: 15px; +} + +.gallery-image { + object-fit: contain; +} + +.thumbnail { + display: flex; + height: 200px; +} + +.gallery-image-caption { + @extend .pt-2, .fs-5; + font-style: italic; + text-align: center; + margin-bottom: 2%; +} + .subhead-base { @extend .ps-3, .fs-5; border-left: 4px solid; @@ -155,6 +187,10 @@ nav .nav-item { border-left-color: $secondary; } +.subhead-text { + font-size: 18px; +} + .scroll { height: 50vh; overflow-y: scroll; @@ -176,6 +212,7 @@ nav .nav-item { .large-card-title { @extend .m-0, .text-secondary; font-weight: bold; + display: inline-block; } .large-card-sidetitle { @@ -251,6 +288,7 @@ nav .nav-item { @extend .rounded-5; width: 128px; height: 128px; + object-fit: cover; } .project-image { @@ -280,11 +318,16 @@ nav .nav-item { @extend .rounded-5, .w-100, .mb-3; } -// for 'about us' cards styling .row-cols-auto > * { max-width: 20%; } +.rsvp { + // display: flex; + align-items: center; + margin-left: 1%; +} + // --- ANIMATIONS --- @keyframes fadein { diff --git a/routes/admin.js b/routes/admin.js index 7259e8f..582e1ae 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -65,6 +65,19 @@ router.post( }), ); +router.post( + "/meetings/remove", + isAdminAuthenticated, + fallible(async (req, res) => { + await db.query("DELETE FROM meetings WHERE id = $1", [req.body.meeting_id]); + req.flash( + "success", + "Successfully removed meeting with id " + req.body.meeting_id + ".", + ); + res.redirect("/admin"); + }), +); + router.post( "/officers", isAdminAuthenticated, diff --git a/routes/attendance.js b/routes/attendance.js index c7d39ca..8668202 100644 --- a/routes/attendance.js +++ b/routes/attendance.js @@ -86,7 +86,7 @@ router.post( router.get( "/attend", fallible(async (req, res) => { - // Find active meeting if possible (2 hour buffer) TODO there's probably a better way to do this + // Find active meeting if possible (2 hour buffer) const meetingResp = await db.query( "SELECT * FROM meetings WHERE date >= NOW() - INTERVAL '2 hours' and date <= NOW() + INTERVAL '2 hours'", ); diff --git a/routes/gallery.js b/routes/gallery.js new file mode 100644 index 0000000..7a74ab9 --- /dev/null +++ b/routes/gallery.js @@ -0,0 +1,28 @@ +const express = require("express"); +const db = require("../database/db"); +const { isAdminAuthenticated, fallible } = require("../middleware"); +const router = express.Router(); + +router.get( + "/gallery", + fallible(async (req, res) => { + const images = await db.query("SELECT * FROM images"); + res.render("gallery", { title: "Gallery", images: images.rows }); + }), +); + +router.post( + "/gallery", + isAdminAuthenticated, + fallible(async (req, res) => { + const active = + req.body.active === "true" || req.body.activeHidden === "false"; + await db.query("UPDATE images SET active = $1 WHERE id = $2", [ + active, + req.body.image_id, + ]); + res.redirect("/gallery"); + }), +); + +module.exports = router; diff --git a/views/about.ejs b/views/about.ejs index ceaf301..32143a8 100644 --- a/views/about.ejs +++ b/views/about.ejs @@ -1,25 +1,23 @@ <% layout('layouts/base')%> -
-
-
-
+
+
+

Join our Discord

-

The Discord server is a live chat where members of Mines ACM communicate with each other. Join to keep up-to-date with happenings!

+

The Discord is a live chat where members of Mines ACM communicate with each other. Join to keep up to date with our events!

  1. Create a Discord account: Use our invite link and create an account or sign in with an existing one.
  2. Verify your email: Make sure your email is verified so you can log in again!
  3. Download the Discord app on your mobile device or computer.
- +
-
diff --git a/views/admin.ejs b/views/admin.ejs index 66fa928..1a5fa8f 100644 --- a/views/admin.ejs +++ b/views/admin.ejs @@ -20,6 +20,10 @@

RSVPs: <%= meeting.rsvps %>

<% } %> +
+ + +
<% if(meeting.attendance.length > 0) { %> <% } %> @@ -47,7 +51,7 @@

Officers

<% for(let officer of officers) { %> -

<%= officer.name %> | <%= officer.title %>

+

<%= officer.name %>, <%= officer.title %>

<%= officer.id + "@mines.edu" %>

diff --git a/views/gallery.ejs b/views/gallery.ejs new file mode 100644 index 0000000..5b33712 --- /dev/null +++ b/views/gallery.ejs @@ -0,0 +1,48 @@ +<% layout('layouts/base')%> + +
+

Gallery

+ +
diff --git a/views/home.ejs b/views/home.ejs index 77d3a21..a7bfda1 100644 --- a/views/home.ejs +++ b/views/home.ejs @@ -26,10 +26,10 @@ <% if (typeof user === 'object' && user) { %>
- +
<% } else { %> - + <% } %> <% } %> <% } else { %> @@ -59,7 +59,7 @@ <% if(meetings.length > 0) { %> <% for(let meeting of meetings) { %>
-
+

<%= meeting.title %>

<% if(meeting.rsvped) { %>

(RSVP'ed)

@@ -74,7 +74,6 @@ <% } %> <% } %>
-

<%= meeting.date %>,<%= meeting.duration %> in <%= meeting.location %>

<%= meeting.description %>

diff --git a/views/layouts/base.ejs b/views/layouts/base.ejs index 728c91d..6aad336 100644 --- a/views/layouts/base.ejs +++ b/views/layouts/base.ejs @@ -14,6 +14,7 @@ + <%- include('../partials/navbar')%> diff --git a/views/partials/footer.ejs b/views/partials/footer.ejs index 0cfb1e6..7b756ae 100644 --- a/views/partials/footer.ejs +++ b/views/partials/footer.ejs @@ -40,6 +40,18 @@ class="nav-link p-0">Data Analytix @ Mines (DAAM)
+ +
+
Socials
+ +
diff --git a/views/presentations.ejs b/views/presentations.ejs index e1ad2ca..6e70b57 100644 --- a/views/presentations.ejs +++ b/views/presentations.ejs @@ -3,7 +3,7 @@

Presentations

-

Slides of all of our tech talks, workshops and related presentations can be found here!

+

Slides of all of our tech talks, workshops and related presentations can be found here!

<% if (typeof user == 'object' && user && user.is_admin) { %>