initial commit

This commit is contained in:
SowinskiBraeden committed 2025-05-05 10:17:17 -07:00
commit da746fc04f
15 files changed
+401

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
MONGODB_HOST=YourMongoDBClusterHere
MONGODB_USER=YourUsernameHere
MONGODB_PASSWORD=YourPasswordHere
MONGODB_DATABASE=YourMongoDBDatabaseHere
MONGODB_SESSION_SECRET=Some_Session_Key_Here1
NODE_SESSION_SECRET=Some_Session_Key_Here2
Executable
+4
View File
@@ -0,0 +1,4 @@
node_modules/
package-lock.json
.env
*.sync-confcict-*
+10
View File
@@ -0,0 +1,10 @@
require('dotenv').config();
module.exports = {
mongo_user: process.env.MONGODB_USER,
mongo_password: process.env.MONGODB_PASSWORD,
mongo_host: process.env.MONGODB_HOST,
mongo_database: process.env.MONGODB_DATABASE,
mongo_secret: process.env.MONGODB_SESSION_SECRET,
express_secret: process.env.NODE_SESSION_SECRET,
};
Executable
+154
View File
@@ -0,0 +1,154 @@
const express = require("express");
const path = require("path");
const session = require("express-session");
const MongoStore = require("connect-mongo");
const MongoClient = require("mongodb").MongoClient;
const joi = require("joi");
const bcrypt = require("bcrypt");
// Initialize app
const app = express();
const port = 8000;
// Connect MongoDB
const config = require("./config/config");
const mongoURI = `mongodb+srv://${config.mongo_user}:${config.mongo_password}@${config.mongo_host}`;
let users;
async function connectMongo() {
try {
const connection = await MongoClient.connect(mongoURI, { connectTimeoutMS: 1000 });
users = connection.db(config.mongo_database).collection("users");
} catch (err) {
console.error(`Failed to connect to mongodb at (mongodb+srv://${config.mongo_host})`);
}
}
connectMongo();
app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: false }));
app.use('/static', express.static(path.join(__dirname, "./public")));
// Sessions
app.use(session({
secret: config.express_secret,
store: MongoStore.create({ mongoUrl: `${mongoURI}/${config.mongo_database}`, crypto: { secret: config.mongo_secret } }),
resave: true,
saveUninitialized: false,
cookie: { maxAge: 60000 },
}));
/**** Page routes ****/
app.get("/", async (req, res) => {
res.set('Content-Type', 'text/html');
res.render("index", { authenticated: req.session.authenticated, username: req.session.username });
return res.status(200);
});
app.get("/login", (req, res) => {
res.set('Content-Type', 'text/html');
res.render("login", { 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 });
return res.status(200);
});
app.get("/members", (req, res) => {
if (!req.session.authenticated) {
req.session.message = "Please login";
res.status(401);
return res.redirect("/login");
}
let names = ["carl", "gary", "jebediah"];
let name = names[Math.floor(Math.random() * names.length)];
res.set('Content-Type', 'text/html');
res.render("members", { name: name, username: req.session.username });
return res.status(200);
});
/*** Authentication routes ***/
app.post("/login", async (req, res) => {
const schema = joi.string().email().required();
let valid = schema.validate(req.body.email);
if (valid.error) {
req.session.message = "Invalid input";
res.status(400);
return res.redirect("/login");
}
let user = await users.findOne({ email: req.body.email });
if (!user) {
req.session.message = "User not found";
res.status(404);
return res.redirect("/login");
}
if (await bcrypt.compare(req.body.password, user.password)) {
req.session.authenticated = true;
req.session.username = user.name;
req.session.message = "";
res.status(200);
return res.redirect('/members');
} else {
req.session.message = "Incorrect password";
res.status(401);
return res.redirect("/login");
}
});
app.post("/signup", async (req, res) => {
const schema = joi.object({
name: joi.string().alphanum().max(20).required(),
email: joi.string().email().required(),
password: joi.string().max(20).required()
});
let valid = schema.validate(req.body);
if (valid.error) {
req.session.message = "Invalid input";
res.status(400);
return res.redirect('/signup');
}
let password = await bcrypt.hash(req.body.password, 12);
await users.insertOne({
name: req.body.name,
email: req.body.email,
password: password,
});
req.session.message = "Please login";
res.status(200);
return res.redirect("/login");
});
app.get("/logout", (req, res) => {
req.session.destroy();
res.status(200);
return res.redirect('/');
});
/*** 404 Not found ***/
app.get("/*splat", (req, res) => {
res.set('Content-Type', 'text/html');
res.render("notFound");
return res.status(404);
});
app.listen(port, () => {
console.log(`Server listing on port ${port}`);
});
Executable
+33
View File
@@ -0,0 +1,33 @@
{
"name": "assignment1",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/SowinskiBraeden/comp2537-assignment1.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/SowinskiBraeden/comp2537-assignment1/issues"
},
"homepage": "https://github.com/SowinskiBraeden/comp2537-assignment1#readme",
"description": "",
"dependencies": {
"bcrypt": "^5.1.1",
"connect-mongo": "^5.1.0",
"dotenv": "^16.5.0",
"ejs": "^3.1.10",
"express": "^5.1.0",
"express-session": "^1.18.1",
"joi": "^17.13.3",
"mongodb": "^6.16.0"
},
"devDependencies": {
"nodemon": "^3.1.10"
}
}
+47
View File
@@ -0,0 +1,47 @@
@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;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 923 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

+22
View File
@@ -0,0 +1,22 @@
Self-graded Assignment 1 Checklist
Criteria
========
Student Name: Braeden Sowinski
Student Set: 1D
[x] A home page links to signup and login, if not logged in; and links to members and signout, if logged in.
[x] A members page that displays 1 of 3 random images stored on the server.
[x] The members page will redirect to the home page if no valid session is found.
[x] The signout buttons end the session.
[x] All secrets, encryption keys, database passwords are stored in a .env file.
[x] The .env file is NOT in your git repo.
[x] Password is BCrypted in the MongoDB database.
[x] Your site is hosted in a hosting service like Qoddi.
[x] A 404 page that "catches" all invalid page hits and that sets the status code to 404.
[x] Session information is stored in an encrypted MongoDB session database. Sessions expire after 1 hour.
50/50 (Total grade out of 50, 5 marks each x 10 items)
*Note items are considered *fully* complete (marked with an x inside the box: [x]), OR incomplete (box is left empty: [ ])
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>2537 Assignment 1</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link rel="stylesheet" href="/static/css/index.css">
</head>
<body>
<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>
<% } %>
</div>
</body>
</html>
+31
View File
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>2537 Assignment 1</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<link rel="stylesheet" href="/static/css/index.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
</head>
<body>
<div class="center">
<form action="/login" method="POST">
<label for="username">
<i class="fas fa-envelope"></i>
</label>
<input type="email" name="email" placeholder="Email" id="email" required>
<label for="password">
<i class="fas fa-lock"></i>
</label>
<input type="password" name="password" placeholder="Password" id="password" required>
<input id="login" type="submit" value="Login">
<h3 id="flash-msg" class="flash-msg"><%= message %></h3>
</form>
</div>
<script src="/static/scripts/loadFlash.js"></script>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>2537 Assignment 1</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head>
<body style="padding: 12pt;">
<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>
</body>
</html>
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>2537 Assignment 1</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head>
<body>
<h1>404 - Not found</h1>
<a href="/">Home</a>
</body>
</html>
+40
View File
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>2537 Assignment 1</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<link rel="stylesheet" href="/static/css/index.css">
<link rel="stylesheet" href="/static/css/login.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
</head>
<body>
<div class="background"></div>
<div class="center">
<form action="/signup" method="POST">
<label for="name">
<i class="fas fa-user"></i>
</label>
<input type="text" name="name" placeholder="Peter Venkman" id="name" required>
<label for="email">
<i class="fas fa-envelope"></i>
</label>
<input type="email" name="email" placeholder="peter_v@ghostbusters.com" id="email" required>
<label for="password">
<i class="fas fa-lock"></i>
</label>
<input type="password" name="password" placeholder="Password" id="password" required>
<input id="login" type="submit" value="Sign Up">
<h3 id="flash-msg" class="flash-msg"><%= message %></h3>
</form>
</div>
<script src="/static/scripts/loadFlash.js"></script>
</body>
</html>