Files
2025-05-12 15:48:29 -07:00

203 lines
5.5 KiB
JavaScript
Executable File

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");
const ObjectId = require("mongodb").ObjectId;
// 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");
console.log("Connected to mongo");
} 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: 3600000 },
}));
/**** Page routes ****/
app.get("/", async (req, res) => {
res.set('Content-Type', 'text/html');
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", { 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", { authenticated: req.session.authenticated, 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", { 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) => {
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.user = user;
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,
type: "user"
});
req.session.authenticated = true;
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);
return res.redirect('/members');
});
app.get("/logout", (req, res) => {
req.session.destroy();
res.status(200);
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", { authenticated: req.session.authenticated });
return res.status(404);
});
app.listen(port, () => {
console.log(`Server listing on port ${port}`);
});