assignment 2
This commit is contained in:
11 files changed
+198
-78
No files matched your search
@@ -1,4 +1,3 @@
|
||||
node_modules/
|
||||
package-lock.json
|
||||
.env
|
||||
*.sync-confcict-*
|
||||
@@ -5,6 +5,7 @@ const MongoStore = require("connect-mongo");
|
||||
const MongoClient = require("mongodb").MongoClient;
|
||||
const joi = require("joi");
|
||||
const bcrypt = require("bcrypt");
|
||||
const ObjectId = require("mongodb").ObjectId;
|
||||
|
||||
// Initialize app
|
||||
const app = express();
|
||||
@@ -19,6 +20,7 @@ async function connectMongo() {
|
||||
try {
|
||||
const connection = await MongoClient.connect(mongoURI, { connectTimeoutMS: 1000 });
|
||||
users = connection.db(config.mongo_database).collection("users");
|
||||
console.log("Connected to mongo");
|
||||
} catch (err) {
|
||||
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 } }),
|
||||
resave: true,
|
||||
saveUninitialized: false,
|
||||
cookie: { maxAge: 60000 },
|
||||
cookie: { maxAge: 3600000 },
|
||||
}));
|
||||
|
||||
/**** Page routes ****/
|
||||
@@ -43,19 +45,19 @@ app.use(session({
|
||||
app.get("/", async (req, res) => {
|
||||
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);
|
||||
});
|
||||
|
||||
app.get("/login", (req, res) => {
|
||||
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);
|
||||
})
|
||||
|
||||
app.get("/signup", (req, res) => {
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -70,10 +72,29 @@ app.get("/members", (req, res) => {
|
||||
let name = names[Math.floor(Math.random() * names.length)];
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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 ***/
|
||||
|
||||
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)) {
|
||||
req.session.authenticated = true;
|
||||
req.session.username = user.name;
|
||||
req.session.user = user;
|
||||
req.session.message = "";
|
||||
|
||||
res.status(200);
|
||||
@@ -128,10 +149,16 @@ app.post("/signup", async (req, res) => {
|
||||
name: req.body.name,
|
||||
email: req.body.email,
|
||||
password: password,
|
||||
type: "user"
|
||||
});
|
||||
|
||||
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 = "";
|
||||
|
||||
res.status(200);
|
||||
@@ -144,11 +171,29 @@ app.get("/logout", (req, res) => {
|
||||
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 ***/
|
||||
|
||||
app.get("/*splat", (req, res) => {
|
||||
res.set('Content-Type', 'text/html');
|
||||
res.render("notFound");
|
||||
res.render("notFound", { authenticated: req.session.authenticated });
|
||||
return res.status(404);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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") %>
|
||||
|
||||
+28
-10
@@ -1,15 +1,33 @@
|
||||
<%- include("./partials/header") %>
|
||||
|
||||
<div class="center">
|
||||
<% if (authenticated) { %>
|
||||
<h1>Welcome <%= username %></h1>
|
||||
<a href="/members">Go to members page</a>
|
||||
<a style="background-color: grey; margin-top: 12pt;" href="/logout">Logout</a>
|
||||
<% } else { %>
|
||||
<a href="/login">Login</a>
|
||||
<p>or</p>
|
||||
<a href="/signup">Signup</a>
|
||||
<% if (authenticated) { %>
|
||||
<div class="m-12 flex flex-row justify-center">
|
||||
<div class="flex flex-col justify-between">
|
||||
|
||||
<div class="flex flex-row justify-between mb-6">
|
||||
<h1 class="text-white font-bold text-xl mt-1">Welcome <%= user.name %></h1>
|
||||
<a class="w-20 py-2 pb-2.5 bg-yellow-400 rounded-md font-semibold text-center" href="/logout">Logout</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>
|
||||
</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") %>
|
||||
+4
-2
@@ -1,12 +1,15 @@
|
||||
<%- include("./partials/header") %>
|
||||
|
||||
<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">
|
||||
<h1 class="font-semibold text-xl mb-4 text-white">Login</h1>
|
||||
</div>
|
||||
<form action="/login" method="POST">
|
||||
<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">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</label>
|
||||
@@ -20,7 +23,6 @@
|
||||
|
||||
<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>
|
||||
<h3 class="text-red-500 text-center"><%= message %></h3>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+19
-7
@@ -1,11 +1,23 @@
|
||||
<%- include("./partials/header") %>
|
||||
|
||||
<h3>Hello <%= username %></h3>
|
||||
<figure>
|
||||
<img style="width: 400pt;" src="/static/images/<%= name %>.png">
|
||||
<figcaption><%= name %></figcaption>
|
||||
</figure>
|
||||
<br>
|
||||
<a style="background-color: grey; padding: 6px; margin-top: 12pt;" href="/logout">Logout</a>
|
||||
<div class="m-12 flex flex-row justify-center">
|
||||
<div class="flex flex-col justify-between">
|
||||
|
||||
<div class="flex flex-row justify-between mb-6">
|
||||
<h1 class="text-white font-bold text-xl mt-1">Welcome <%= user.name %></h1>
|
||||
<% if (user.type == "admin") { %>
|
||||
<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") %>
|
||||
+13
-2
@@ -1,6 +1,17 @@
|
||||
<%- include("./partials/header") %>
|
||||
|
||||
<h1>404 - Not found</h1>
|
||||
<a href="/">Home</a>
|
||||
<div >
|
||||
<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") %>
|
||||
@@ -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>
|
||||
</html>
|
||||
@@ -8,3 +8,17 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||
</head>
|
||||
<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
@@ -8,6 +8,10 @@
|
||||
</div>
|
||||
<form action="/signup" method="POST">
|
||||
<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">
|
||||
<i class="fas fa-user"></i>
|
||||
</label>
|
||||
@@ -26,7 +30,6 @@
|
||||
|
||||
<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>
|
||||
<h3 class="text-red"><%= message %></h3>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user