assignment 2

This commit is contained in:
SowinskiBraeden committed 2025-05-12 15:48:29 -07:00
1 parent 33e8ef230c
commit 942ad7add0
11 files changed
+199 -79

No files matched your search

-1
View File
@@ -1,4 +1,3 @@
node_modules/ node_modules/
package-lock.json package-lock.json
.env .env
*.sync-confcict-*
+53 -8
View File
@@ -5,6 +5,7 @@ const MongoStore = require("connect-mongo");
const MongoClient = require("mongodb").MongoClient; const MongoClient = require("mongodb").MongoClient;
const joi = require("joi"); const joi = require("joi");
const bcrypt = require("bcrypt"); const bcrypt = require("bcrypt");
const ObjectId = require("mongodb").ObjectId;
// Initialize app // Initialize app
const app = express(); const app = express();
@@ -19,6 +20,7 @@ async function connectMongo() {
try { try {
const connection = await MongoClient.connect(mongoURI, { connectTimeoutMS: 1000 }); const connection = await MongoClient.connect(mongoURI, { connectTimeoutMS: 1000 });
users = connection.db(config.mongo_database).collection("users"); users = connection.db(config.mongo_database).collection("users");
console.log("Connected to mongo");
} catch (err) { } catch (err) {
console.error(`Failed to connect to mongodb at (mongodb+srv://${config.mongo_host})`); console.error(`Failed to connect to mongodb at (mongodb+srv://${config.mongo_host})`);
} }
@@ -35,7 +37,7 @@ app.use(session({
store: MongoStore.create({ mongoUrl: `${mongoURI}/${config.mongo_database}`, crypto: { secret: config.mongo_secret } }), store: MongoStore.create({ mongoUrl: `${mongoURI}/${config.mongo_database}`, crypto: { secret: config.mongo_secret } }),
resave: true, resave: true,
saveUninitialized: false, saveUninitialized: false,
cookie: { maxAge: 60000 }, cookie: { maxAge: 3600000 },
})); }));
/**** Page routes ****/ /**** Page routes ****/
@@ -43,19 +45,19 @@ app.use(session({
app.get("/", async (req, res) => { app.get("/", async (req, res) => {
res.set('Content-Type', 'text/html'); res.set('Content-Type', 'text/html');
res.render("index", { authenticated: req.session.authenticated, username: req.session.username }); res.render("index", { authenticated: req.session.authenticated, user: req.session.user });
return res.status(200); return res.status(200);
}); });
app.get("/login", (req, res) => { app.get("/login", (req, res) => {
res.set('Content-Type', 'text/html'); res.set('Content-Type', 'text/html');
res.render("login", { message: req.session.message }); res.render("login", { authenticated: req.session.authenticated, message: req.session.message });
return res.status(200); return res.status(200);
}) })
app.get("/signup", (req, res) => { app.get("/signup", (req, res) => {
res.set('Content-Type', 'text/html'); res.set('Content-Type', 'text/html');
res.render("signup", { message: req.session.message }); res.render("signup", { authenticated: req.session.authenticated, message: req.session.message });
return res.status(200); return res.status(200);
}); });
@@ -70,10 +72,29 @@ app.get("/members", (req, res) => {
let name = names[Math.floor(Math.random() * names.length)]; let name = names[Math.floor(Math.random() * names.length)];
res.set('Content-Type', 'text/html'); res.set('Content-Type', 'text/html');
res.render("members", { name: name, username: req.session.username }); res.render("members", { authenticated: req.session.authenticated, user: req.session.user });
return res.status(200); return res.status(200);
}); });
app.get("/admin", async (req, res) => {
if (!req.session.authenticated) {
req.session.message = "Please login";
res.status(401);
return res.redirect("/login");
}
if (req.session.user.type != "admin") {
req.session.message = "You are not authorized to access that resource";
res.status(401);
return res.redirect("/members");
}
let userList = await users.find({}).toArray();
res.set('Content-Type', 'text/html');
res.render("admin", { authenticated: req.session.authenticated, user: req.session.user, users: userList });
});
/*** Authentication routes ***/ /*** Authentication routes ***/
app.post("/login", async (req, res) => { app.post("/login", async (req, res) => {
@@ -96,7 +117,7 @@ app.post("/login", async (req, res) => {
if (await bcrypt.compare(req.body.password, user.password)) { if (await bcrypt.compare(req.body.password, user.password)) {
req.session.authenticated = true; req.session.authenticated = true;
req.session.username = user.name; req.session.user = user;
req.session.message = ""; req.session.message = "";
res.status(200); res.status(200);
@@ -128,10 +149,16 @@ app.post("/signup", async (req, res) => {
name: req.body.name, name: req.body.name,
email: req.body.email, email: req.body.email,
password: password, password: password,
type: "user"
}); });
req.session.authenticated = true; req.session.authenticated = true;
req.session.username = req.body.name; req.session.user = {
name: req.body.name,
email: req.body.email,
password: password, // bad but oh well
type: "user"
};
req.session.message = ""; req.session.message = "";
res.status(200); res.status(200);
@@ -144,11 +171,29 @@ app.get("/logout", (req, res) => {
return res.redirect('/'); return res.redirect('/');
}); });
app.post("/promote", (req, res) => {
users.updateOne({ _id: new ObjectId(req.body.userId) }, { $set: { type: "admin" } }).then((results) => {
return res.redirect("/admin");
}).catch((err) => {
console.error(err);
return res.redirect("/admin");
});
});
app.post("/demote", (req, res) => {
users.updateOne({ _id: new ObjectId(req.body.userId) }, { $set: { type: "user" } }).then((results) => {
return res.redirect("/admin");
}).catch((err) => {
console.error(err);
return res.redirect("/admin");
});
});
/*** 404 Not found ***/ /*** 404 Not found ***/
app.get("/*splat", (req, res) => { app.get("/*splat", (req, res) => {
res.set('Content-Type', 'text/html'); res.set('Content-Type', 'text/html');
res.render("notFound"); res.render("notFound", { authenticated: req.session.authenticated });
return res.status(404); return res.status(404);
}); });
-47
View File
@@ -1,47 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Oswald&display=swap');
* {
margin: 0pt;
padding: 0pt;
box-sizing: border-box;
font-family: 'Oswald', sans-serif;
overflow-x: hidden;
overflow-y: hidden;
}
body {
width: 100vw;
height: 100vh;
}
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.center p {
margin: 12pt;
}
.center a {
padding-left: 6pt;
padding-right: 6pt;
margin-top: 2pt;
margin-bottom: 2pt;
font-size: 24px;
background-color: lightsteelblue;
border-radius: 8px;
}
.center form {
display: flex;
flex-direction: column;
justify-content: center;
}
.flash-msg {
color: red;
}
+53
View File
@@ -0,0 +1,53 @@
<%- include("./partials/header") %>
<div class="m-12 flex flex-row justify-center">
<div>
<div class="flex flex-row justify-between w-sm">
<h1 class="text-white font-bold text-xl">Welcome <%= user.name %></h1>
<a class="w-20 text-center py-1 bg-yellow-400 rounded-md font-semibold" href="/logout">Logout</a>
</div>
<br>
<h1 class="text-white text-lg mb-2">Users</h1>
<div class="flex flex-col justify-between">
<% users.forEach((u) => { %>
<div class="mb-4 bg-gray-200 p-4 flex flex-row justify-between rounded-md">
<div class="mr-6 mt-1"><p><%= u.type %></p></div>
<div class="mr-6 mt-1"><p><%= u.name %></p></div>
<div class="mr-6 mt-1"><p><%= u.email %></p></div>
<div>
<% if (u._id == user._id) { %>
<form method="POST" action="/demote">
<input hidden disabed name="userId" id="userId" value="<%= u._id %>">
<button disabled type="submit" class="w-3xs bg-green-600 rounded-md py-1 text-center text-white font-semibold cursor-not-allowed">
Cannot Demote Self
</button>
</form>
<% } else { %>
<% if (u.type == "admin") { %>
<form method="POST" action="/demote">
<input hidden disabed name="userId" id="userId" value="<%= u._id %>">
<button type="submit" class="w-3xs bg-green-600 rounded-md py-1 text-center text-white font-semibold cursor-pointer">
Demote to User
</button>
</form>
<% } else { %>
<form method="POST" action="/promote">
<input hidden disabed name="userId" id="userId" value="<%= u._id %>">
<button type="submit" class="w-3xs bg-red-400 rounded-md py-1 text-center text-white font-semibold cursor-pointer">
Promote to Admin
</button>
</form>
<% } %>
<% } %>
</div>
</div>
<% }) %>
</div>
</div>
</div>
<%- include("./partials/footer") %>
+29 -11
View File
@@ -1,15 +1,33 @@
<%- include("./partials/header") %> <%- include("./partials/header") %>
<div class="center"> <% if (authenticated) { %>
<% if (authenticated) { %> <div class="m-12 flex flex-row justify-center">
<h1>Welcome <%= username %></h1> <div class="flex flex-col justify-between">
<a href="/members">Go to members page</a>
<a style="background-color: grey; margin-top: 12pt;" href="/logout">Logout</a> <div class="flex flex-row justify-between mb-6">
<% } else { %> <h1 class="text-white font-bold text-xl mt-1">Welcome <%= user.name %></h1>
<a href="/login">Login</a> <a class="w-20 py-2 pb-2.5 bg-yellow-400 rounded-md font-semibold text-center" href="/logout">Logout</a>
<p>or</p> </div>
<a href="/signup">Signup</a>
<% } %> <div>
</div> <a class="text-center block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2 mb-4" href="/members">Go to members page</a>
<% if (user.type == "admin") { %>
<a class="text-center block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2 mb-4" href="/admin">Go to admin page</a>
<% } %>
</div>
</div>
</div>
<% } else { %>
<div class="m-12 flex flex-row justify-center">
<div class="flex flex-col justify-between">
<a class="text-center block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2 mb-4" href="/login">Login</a>
<p class="text-white text-center mb-4">or</p>
<a class="text-center block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2 mb-4" href="/signup">Signup</a>
</div>
</div>
<% } %>
<%- include("./partials/footer") %> <%- include("./partials/footer") %>
+4 -2
View File
@@ -1,12 +1,15 @@
<%- include("./partials/header") %> <%- include("./partials/header") %>
<div class="flex h-screen"> <div class="flex h-screen">
<div class="m-auto w-sm px-6 py-12 bg-white-200 rounded-lg space-y-6"> <div class="m-auto w-sm px-6 py-12 bg-white-200">
<div class="flex flex-row justify-center"> <div class="flex flex-row justify-center">
<h1 class="font-semibold text-xl mb-4 text-white">Login</h1> <h1 class="font-semibold text-xl mb-4 text-white">Login</h1>
</div> </div>
<form action="/login" method="POST"> <form action="/login" method="POST">
<div class="flex flex-col justify-center"> <div class="flex flex-col justify-center">
<% if (message) { %>
<div class="mb-6 block w-full py-3 bg-red-400 rounded-md outline-2 -outline-offset-1 outline-red-300"><h3 class="text-white text-center"><%= message %></h3></div>
<% } %>
<label for="username"> <label for="username">
<i class="fas fa-envelope"></i> <i class="fas fa-envelope"></i>
</label> </label>
@@ -20,7 +23,6 @@
<div class="flex flex-row justify-center"> <div class="flex flex-row justify-center">
<button type="submit" class="block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2">Login</button> <button type="submit" class="block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2">Login</button>
<h3 class="text-red-500 text-center"><%= message %></h3>
</div> </div>
</form> </form>
</div> </div>
+19 -7
View File
@@ -1,11 +1,23 @@
<%- include("./partials/header") %> <%- include("./partials/header") %>
<h3>Hello <%= username %></h3> <div class="m-12 flex flex-row justify-center">
<figure> <div class="flex flex-col justify-between">
<img style="width: 400pt;" src="/static/images/<%= name %>.png">
<figcaption><%= name %></figcaption> <div class="flex flex-row justify-between mb-6">
</figure> <h1 class="text-white font-bold text-xl mt-1">Welcome <%= user.name %></h1>
<br> <% if (user.type == "admin") { %>
<a style="background-color: grey; padding: 6px; margin-top: 12pt;" href="/logout">Logout</a> <a class="text-center block w-30 bg-blue-600 rounded-md text-white p-2 mx-4 pt-4.5 md:pt-2 lg:pt-2" href="/admin">Admin</a>
<% } %>
<a class="h-auto w-20 py-2 pb-2.5 bg-yellow-400 rounded-md font-semibold text-center pt-4.5 md:pt-2 lg:pt-2" href="/logout">Logout</a>
</div>
<div class="flex flex-col md:flex-row lg:flex-row justify-between">
<img style="width: 200pt;" src="/static/images/carl.png">
<img style="width: 200pt;" src="/static/images/gary.png">
<img style="width: 200pt;" src="/static/images/jebediah.png">
</div>
</div>
</div>
<%- include("./partials/footer") %> <%- include("./partials/footer") %>
+13 -2
View File
@@ -1,6 +1,17 @@
<%- include("./partials/header") %> <%- include("./partials/header") %>
<h1>404 - Not found</h1> <div >
<a href="/">Home</a> <h1 class="">404 - Not found</h1>
<a href="/">Home</a>
</div>
<div class="m-12 flex flex-row justify-center">
<div class="flex flex-col justify-between mt-40">
<h1 class="font-semibold text-xl mb-4 text-white text-center">404 - Not found</h1>
<a class="text-center block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2 mb-4" href="/">Home</a>
</div>
</div>
<%- include("./partials/footer") %> <%- include("./partials/footer") %>
+10
View File
@@ -1,2 +1,12 @@
<footer class="fixed bottom-0 w-full bg-gray-500 h-15 text-white flex flex-col justify-center">
<div class="flex flex-row justify-center">
<div class="w-full flex flex-row justify-center">
<div class="mr-20">Copyright 2025</div>
<div>Super duper cool app</div>
</div>
</div>
</footer>
</body> </body>
</html> </html>
+14
View File
@@ -8,3 +8,17 @@
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head> </head>
<body class="bg-gray-700"> <body class="bg-gray-700">
<nav class="w-full bg-gray-500 h-15 text-white flex flex-col justify-center">
<div class="flex flex-row justify-between px-10 lg:px-20">
<div class="w-full">
Super duper cool app!
</div>
<div class="flex flex-row justify-between">
<a class="mx-4 lg:mx-12" href="/">Home</a>
<% if (authenticated) { %>
<a href="/logout">Logout</a>
<% } %>
</div>
</div>
</nav>
+4 -1
View File
@@ -8,6 +8,10 @@
</div> </div>
<form action="/signup" method="POST"> <form action="/signup" method="POST">
<div class="flex flex-col justify-center"> <div class="flex flex-col justify-center">
<% if (message) { %>
<div class="mb-6 block w-full py-3 bg-red-400 rounded-md outline-2 -outline-offset-1 outline-red-300"><h3 class="text-white text-center"><%= message %></h3></div>
<% } %>
<label for="name"> <label for="name">
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
</label> </label>
@@ -26,7 +30,6 @@
<div class="flex flex-row justify-center"> <div class="flex flex-row justify-center">
<button type="submit" class="block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2">Signup</button> <button type="submit" class="block w-3xs bg-blue-600 rounded-md mx-8 text-white p-2">Signup</button>
<h3 class="text-red"><%= message %></h3>
</div> </div>
</form> </form>
</div> </div>