203 lines
5.5 KiB
JavaScript
Executable File
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}`);
|
|
});
|