fully functioning
This commit is contained in:
parent
6dc16493e5
commit
fc87aaa0c8
20 changed files with 2624 additions and 0 deletions
65
.gitignore
vendored
Normal file
65
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# deta-space internal files
|
||||||
|
.space
|
||||||
|
Spacefile
|
||||||
51
app.js
Normal file
51
app.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
const session = require("express-session");
|
||||||
|
const passport = require("passport");
|
||||||
|
const passportInit = require("./passportInit.js");
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
|
// make app
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// get db
|
||||||
|
const mongoDB = `mongodb+srv://${process.env.USER}:${process.env.PASS}@odin.eftl02o.mongodb.net/membersOnly?retryWrites=true&w=majority`;
|
||||||
|
// or throw error
|
||||||
|
main().catch((err) => console.log(err));
|
||||||
|
async function main() {
|
||||||
|
await mongoose.connect(mongoDB);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set paths
|
||||||
|
app.use(
|
||||||
|
"/styles/css",
|
||||||
|
express.static(path.join(__dirname, "node_modules/bootstrap/dist/css"))
|
||||||
|
);
|
||||||
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
|
// view engine setup
|
||||||
|
app.set("views", path.join(__dirname, "views"));
|
||||||
|
app.set("view engine", "ejs");
|
||||||
|
|
||||||
|
// passport setup
|
||||||
|
passportInit();
|
||||||
|
app.use(
|
||||||
|
session({
|
||||||
|
secret: process.env.SECRET_KEY,
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.use(passport.initialize());
|
||||||
|
app.use(passport.session());
|
||||||
|
|
||||||
|
// URL parsing
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
|
||||||
|
// routing
|
||||||
|
const router = require("./routes.js");
|
||||||
|
app.use("/", router);
|
||||||
|
|
||||||
|
module.exports = app;
|
||||||
90
bin/www
Executable file
90
bin/www
Executable file
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require('../app');
|
||||||
|
var debug = require('debug')('express-inventory-application:server');
|
||||||
|
var http = require('http');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || '3000');
|
||||||
|
app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
debug('Listening on ' + bind);
|
||||||
|
}
|
||||||
12
controllers/index.js
Normal file
12
controllers/index.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
const Message = require("../models/message.js");
|
||||||
|
const asyncHandler = require("express-async-handler");
|
||||||
|
|
||||||
|
exports.index = asyncHandler(async (req, res, next) => {
|
||||||
|
// gets messages from database
|
||||||
|
const messages = await Message.find().lean().exec();
|
||||||
|
|
||||||
|
res.render("index", {
|
||||||
|
messages: messages,
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
});
|
||||||
69
controllers/message.js
Normal file
69
controllers/message.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
const Message = require("../models/message.js");
|
||||||
|
const asyncHandler = require("express-async-handler");
|
||||||
|
const { body, validationResult } = require("express-validator");
|
||||||
|
const { default: mongoose } = require("mongoose");
|
||||||
|
|
||||||
|
exports.index = asyncHandler(async (req, res, next) => {
|
||||||
|
// gets message from database
|
||||||
|
const message = await Message.findOne({ _id: req.params.message })
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
res.render("message", {
|
||||||
|
message: message,
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.message_create_get = (req, res, next) => {
|
||||||
|
res.render("createmessage");
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.message_create_post = [
|
||||||
|
// Validate and sanitize title
|
||||||
|
body("title", "Please enter Insight title!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize text
|
||||||
|
body("message", "Please enter your Insight!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Process request after validation and sanitization.
|
||||||
|
asyncHandler(async (req, res, next) => {
|
||||||
|
// Extract the validation errors from a request.
|
||||||
|
const errors = validationResult(req);
|
||||||
|
|
||||||
|
// if there are validation errors
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
// Render the creation form again with sanitized values/error messages.
|
||||||
|
res.render("createmessage", {
|
||||||
|
errors: errors.array(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Data from form is valid.
|
||||||
|
else {
|
||||||
|
// create new user with temporary password
|
||||||
|
let message = new Message({
|
||||||
|
title: req.body.title,
|
||||||
|
date: new Date(),
|
||||||
|
text: req.body.message,
|
||||||
|
author: req.user.name,
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await message.save();
|
||||||
|
// saved. Redirect to home page.
|
||||||
|
res.redirect(`/`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
exports.message_delete_get = asyncHandler(async (req, res, next) => {
|
||||||
|
// delete message from database
|
||||||
|
await Message.findByIdAndDelete(req.params.message);
|
||||||
|
res.redirect("/");
|
||||||
|
});
|
||||||
165
controllers/user.js
Normal file
165
controllers/user.js
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
const User = require("../models/user.js");
|
||||||
|
const asyncHandler = require("express-async-handler");
|
||||||
|
const { body, validationResult } = require("express-validator");
|
||||||
|
const { default: mongoose } = require("mongoose");
|
||||||
|
const bcrypt = require("bcryptjs");
|
||||||
|
|
||||||
|
exports.user_create_get = (req, res, next) => {
|
||||||
|
res.render("createuser");
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.user_create_post = [
|
||||||
|
// Validate and sanitize name
|
||||||
|
body("name", "Please enter your name!").trim().isLength({ min: 1 }).escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize username
|
||||||
|
body("username", "Please enter your username!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize password
|
||||||
|
body("password", "Please enter your password!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Validate and sanitize password confirmation
|
||||||
|
body("password_confirmation", "Please confirm your password!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.custom((value, { req }) => {
|
||||||
|
if (value !== req.body.password)
|
||||||
|
throw new Error("Passwords don't match!");
|
||||||
|
else return value;
|
||||||
|
})
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Process request after validation and sanitization.
|
||||||
|
asyncHandler(async (req, res, next) => {
|
||||||
|
// Extract the validation errors from a request.
|
||||||
|
const errors = validationResult(req);
|
||||||
|
// if there are validation errors
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
// Render the creation form again with sanitized values/error messages.
|
||||||
|
res.render("createuser", {
|
||||||
|
errors: errors.array(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// else data is valid
|
||||||
|
// create new user with hashed password
|
||||||
|
const user = new User({
|
||||||
|
name: req.body.name,
|
||||||
|
username: req.body.username,
|
||||||
|
password: await bcrypt.hash(req.body.password, 10),
|
||||||
|
isMember: false,
|
||||||
|
isAdmin: false,
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.save();
|
||||||
|
// saved. Redirect to home page.
|
||||||
|
res.redirect(`/`);
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
exports.user_signin_get = (req, res, next) => {
|
||||||
|
res.render("signin");
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.user_initiation_get = (req, res, next) => {
|
||||||
|
res.render("initiation");
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.user_initiation_post = [
|
||||||
|
// Validate and sanitize Secret Code
|
||||||
|
body("code", "Secret Code not entered!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.custom((value) => {
|
||||||
|
if (value !== process.env.INITIATION_CODE)
|
||||||
|
throw new Error("Incorrect Secret Code");
|
||||||
|
else return value;
|
||||||
|
})
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Process request after validation and sanitization.
|
||||||
|
asyncHandler(async (req, res, next) => {
|
||||||
|
// Extract the validation errors from a request.
|
||||||
|
const errors = validationResult(req);
|
||||||
|
|
||||||
|
// if there are validation errors
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
// Render the Initiation form again with sanitized values/error messages.
|
||||||
|
res.render("initiation", {
|
||||||
|
errors: errors.array(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Data from form is valid.
|
||||||
|
else {
|
||||||
|
// find user from DB
|
||||||
|
const dbUser = await User.findById(req.user._id).lean().exec();
|
||||||
|
// make a "new" user with same fields except member set to true
|
||||||
|
const user = new User({
|
||||||
|
name: dbUser.name,
|
||||||
|
username: dbUser.username,
|
||||||
|
password: dbUser.password,
|
||||||
|
isMember: true,
|
||||||
|
isAdmin: false, //always false if not a member
|
||||||
|
_id: dbUser._id,
|
||||||
|
});
|
||||||
|
await User.findByIdAndUpdate(dbUser._id, user, {});
|
||||||
|
// saved. Redirect to home page.
|
||||||
|
res.redirect(`/`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
exports.user_sudo_get = (req, res, next) => {
|
||||||
|
res.render("sudo");
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.user_sudo_post = [
|
||||||
|
// Validate and sanitize Great Secret
|
||||||
|
body("greatsecret", "Answer not entered!")
|
||||||
|
.trim()
|
||||||
|
.isLength({ min: 1 })
|
||||||
|
.custom((value) => {
|
||||||
|
if (value != process.env.GREAT_SECRET)
|
||||||
|
throw new Error("You are incorrect, Initiate");
|
||||||
|
else return value;
|
||||||
|
})
|
||||||
|
.escape(),
|
||||||
|
|
||||||
|
// Process request after validation and sanitization.
|
||||||
|
asyncHandler(async (req, res, next) => {
|
||||||
|
// Extract the validation errors from a request.
|
||||||
|
const errors = validationResult(req);
|
||||||
|
|
||||||
|
// if there are validation errors
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
// Render the Initiation form again with sanitized values/error messages.
|
||||||
|
res.render("sudo", {
|
||||||
|
errors: errors.array(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// else data from form is valid.
|
||||||
|
// find user from DB
|
||||||
|
const dbUser = await User.findById(req.user._id).lean().exec();
|
||||||
|
// make a "new" user with same fields except admin set to true
|
||||||
|
const user = new User({
|
||||||
|
name: dbUser.name,
|
||||||
|
username: dbUser.username,
|
||||||
|
password: dbUser.password,
|
||||||
|
isMember: true, // always true if admin
|
||||||
|
isAdmin: true,
|
||||||
|
_id: dbUser._id,
|
||||||
|
});
|
||||||
|
await User.findByIdAndUpdate(dbUser._id, user, {});
|
||||||
|
// saved. Redirect to home page.
|
||||||
|
res.redirect(`/`);
|
||||||
|
}),
|
||||||
|
];
|
||||||
20
models/message.js
Normal file
20
models/message.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const MessageSchema = new Schema({
|
||||||
|
title: { type: String, required: true },
|
||||||
|
date: { type: Date, required: true },
|
||||||
|
text: { type: String, required: true },
|
||||||
|
author: { type: String, required: true },
|
||||||
|
_id: { type: mongoose.ObjectId, required: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Virtual for message URL
|
||||||
|
MessageSchema.virtual("url").get(function () {
|
||||||
|
// We don't use an arrow function as we'll need the this object
|
||||||
|
return `/msg/${this._id}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Export model
|
||||||
|
module.exports = mongoose.model("Message", MessageSchema);
|
||||||
16
models/user.js
Normal file
16
models/user.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const UserSchema = new Schema({
|
||||||
|
name: { type: String, required: true },
|
||||||
|
username: { type: String, required: true },
|
||||||
|
password: { type: String, required: true },
|
||||||
|
isMember: { type: Boolean, required: true },
|
||||||
|
isAdmin: { type: Boolean, required: false },
|
||||||
|
_id: { type: mongoose.ObjectId, required: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
// we will NOT be exposing any URLs for users
|
||||||
|
|
||||||
|
// Export model
|
||||||
|
module.exports = mongoose.model("User", UserSchema);
|
||||||
1712
package-lock.json
generated
Normal file
1712
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
24
package.json
Normal file
24
package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "express-members-only",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./bin/www"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
|
"bootstrap": "^5.3.1",
|
||||||
|
"debug": "~2.6.9",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"ejs": "^3.1.9",
|
||||||
|
"ejs-lint": "^2.0.0",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"express-async-handler": "^1.2.0",
|
||||||
|
"express-session": "^1.17.3",
|
||||||
|
"express-validator": "^7.0.1",
|
||||||
|
"mongoose": "^7.5.1",
|
||||||
|
"passport": "^0.6.0",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
|
"passport-local-mongoose": "^8.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
passportInit.js
Normal file
35
passportInit.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
const passport = require("passport");
|
||||||
|
const LocalStrategy = require("passport-local").Strategy;
|
||||||
|
const bcrypt = require("bcryptjs");
|
||||||
|
const User = require("./models/user.js");
|
||||||
|
|
||||||
|
const passportInit = () => {
|
||||||
|
passport.use(
|
||||||
|
"local",
|
||||||
|
new LocalStrategy(async function verify(username, password, done) {
|
||||||
|
const user = await User.findOne({ username: username });
|
||||||
|
if (!user) {
|
||||||
|
return done(null, false, { message: "Incorrect username" });
|
||||||
|
}
|
||||||
|
const match = await bcrypt.compare(password, user.password);
|
||||||
|
if (!match) {
|
||||||
|
return done(null, false, { message: "Incorrect password" });
|
||||||
|
}
|
||||||
|
return done(null, user);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
passport.serializeUser((user, done) => {
|
||||||
|
done(null, user);
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.deserializeUser(async (user, done) => {
|
||||||
|
try {
|
||||||
|
done(null, user);
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = passportInit;
|
||||||
8
public/stylesheets/index.css
Normal file
8
public/stylesheets/index.css
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@900&display=swap");
|
||||||
|
|
||||||
|
.logotext {
|
||||||
|
color: gold;
|
||||||
|
text-shadow: 1px 1px 2px black;
|
||||||
|
font-family: "Cinzel Decorative", cursive;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
34
routes.js
Normal file
34
routes.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const passport = require("passport");
|
||||||
|
const index_controller = require("./controllers/index.js");
|
||||||
|
const user_controller = require("./controllers/user.js");
|
||||||
|
const message_controller = require("./controllers/message.js");
|
||||||
|
|
||||||
|
// home page
|
||||||
|
router.get("/", index_controller.index);
|
||||||
|
|
||||||
|
// user sign in page
|
||||||
|
router.get("/user/signin", user_controller.user_signin_get);
|
||||||
|
router.post("/user/signin", passport.authenticate("local"), (req, res) => {
|
||||||
|
res.redirect("/");
|
||||||
|
});
|
||||||
|
// user sign up page
|
||||||
|
router.get("/user/create", user_controller.user_create_get);
|
||||||
|
router.post("/user/create", user_controller.user_create_post);
|
||||||
|
// initiate a member with secret password
|
||||||
|
router.get("/user/initiation", user_controller.user_initiation_get);
|
||||||
|
router.post("/user/initiation", user_controller.user_initiation_post);
|
||||||
|
// member enters all the names of God to become 33rd degree Illumined Grandmaster
|
||||||
|
router.get("/user/sudo", user_controller.user_sudo_get);
|
||||||
|
router.post("/user/sudo", user_controller.user_sudo_post);
|
||||||
|
|
||||||
|
// message functions
|
||||||
|
// create URL has to go first before :matchers
|
||||||
|
router.get("/msg/create", message_controller.message_create_get);
|
||||||
|
router.post("/msg/create", message_controller.message_create_post);
|
||||||
|
//
|
||||||
|
router.get("/msg/:message", message_controller.index);
|
||||||
|
router.get("/msg/:message/delete", message_controller.message_delete_get);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
43
views/createmessage.ejs
Normal file
43
views/createmessage.ejs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CVLT - New Insight</title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-4">Create Insight</h1>
|
||||||
|
<form class="d-flex flex-column align-items-center w-50" method="post">
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="title">Title:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="insight title"
|
||||||
|
aria-describedby="title"
|
||||||
|
name="title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="message">Insight:</span>
|
||||||
|
<textarea
|
||||||
|
class="form-control"
|
||||||
|
aria-label="insight"
|
||||||
|
aria-describedby="message"
|
||||||
|
name="message"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-light px-4 py-2">
|
||||||
|
Submit Insight
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
66
views/createuser.ejs
Normal file
66
views/createuser.ejs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CVLT - New User</title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-4">New User</h1>
|
||||||
|
<form class="d-flex flex-column align-items-center w-50" method="post">
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="name">Full Name:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="name"
|
||||||
|
aria-describedby="name"
|
||||||
|
name="name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="username">Username:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="username"
|
||||||
|
aria-describedby="username"
|
||||||
|
name="username"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="password">Password:</span>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="password"
|
||||||
|
aria-describedby="password"
|
||||||
|
name="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="password_confirmation"
|
||||||
|
>Confirm Password:</span
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="confirm password"
|
||||||
|
aria-describedby="password_confirmation"
|
||||||
|
name="password_confirmation"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-light px-4 py-2">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
61
views/index.ejs
Normal file
61
views/index.ejs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CVLT</title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="/stylesheets/index.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light min-vh-100">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="logotext mb-4">CVLT</h1>
|
||||||
|
<p class="fs-3">Insights:</p>
|
||||||
|
<% messages.forEach(message => { %>
|
||||||
|
<a
|
||||||
|
href="/msg/<%= message._id %>"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-light px-4"
|
||||||
|
>
|
||||||
|
<h3 class="pt-2"><%= message.title %></h3>
|
||||||
|
<% if (user && user.isMember) { %>
|
||||||
|
<p>Penned: <%= message.date %> by <%= message.author %></p>
|
||||||
|
<% } %>
|
||||||
|
<p><%= message.text %></p>
|
||||||
|
</a>
|
||||||
|
<div class="pb-4"></div>
|
||||||
|
<% }) %> <% if (user && user.isMember) { %>
|
||||||
|
<a href="/msg/create" type="button" class="btn btn-success px-4">
|
||||||
|
+ Post Message
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<% if (user && user.isMember === true && user.isAdmin === false) { %>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/user/sudo" type="button" class="btn btn-light px-4">
|
||||||
|
Join the Light
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
<% } %> <% if (user && user.isMember === false) { %>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="/user/initiation"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-light px-4"
|
||||||
|
>
|
||||||
|
Become an Initiate
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
<% } %> <% if (!user) { %>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/user/signin" type="button" class="btn btn-outline-light px-4">
|
||||||
|
Sign In
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
<% } %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
34
views/initiation.ejs
Normal file
34
views/initiation.ejs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CVLT - Initiation</title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-4">Initiation</h1>
|
||||||
|
<form class="d-flex flex-column align-items-center w-50" method="post">
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="code">Enter the Secret Code:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="secret code"
|
||||||
|
aria-describedby="code"
|
||||||
|
name="code"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-success px-4 py-2">
|
||||||
|
✓ I wish to be an Initiate
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
34
views/message.ejs
Normal file
34
views/message.ejs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CVLT - <%= message.title %></title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-0"><%= message.title %></h1>
|
||||||
|
<% if (user && user.isMember) { %>
|
||||||
|
<p>Penned: <%= message.date %> by <%= message.author %></p>
|
||||||
|
<% } %>
|
||||||
|
<div class="pb-4"></div>
|
||||||
|
<p><%= message.text %></p>
|
||||||
|
<div class="pb-4"></div>
|
||||||
|
<% if (user && user.isAdmin) { %>
|
||||||
|
<a
|
||||||
|
href="/msg/<%= message._id %>/delete"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-danger px-4"
|
||||||
|
>
|
||||||
|
🗙 Countermand this Insight
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
51
views/signin.ejs
Normal file
51
views/signin.ejs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CVLT - Sign In</title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-4">Sign In</h1>
|
||||||
|
<form class="d-flex flex-column align-items-center w-50" method="post">
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="username-aria">Username:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="username"
|
||||||
|
aria-describedby="username-aria"
|
||||||
|
name="username"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="password-aria">Password:</span>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="password"
|
||||||
|
aria-describedby="password-aria"
|
||||||
|
name="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-light px-4 py-2">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div class="pb-4"></div>
|
||||||
|
<div class="d-flex flex-column align-items-center">
|
||||||
|
<p class="mb-2">Don't have an account?</p>
|
||||||
|
<a href="/user/create" type="button" class="btn btn-success px-4">
|
||||||
|
Join Us!
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
34
views/sudo.ejs
Normal file
34
views/sudo.ejs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CVLT - Ascension</title>
|
||||||
|
<link rel="stylesheet" href="/styles/css/bootstrap.min.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="d-flex flex-column py-4 px-4 align-items-center">
|
||||||
|
<h1 class="mb-4">Ascension</h1>
|
||||||
|
<form class="d-flex flex-column align-items-center w-50" method="post">
|
||||||
|
<div class="input-group mb-4" data-bs-theme="dark">
|
||||||
|
<span class="input-group-text" id="greatsecret-label"
|
||||||
|
>Life, the Universe and Everything:</span
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
aria-label="great secret"
|
||||||
|
aria-describedby="greatsecret-label"
|
||||||
|
name="greatsecret"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-light px-4 py-2">ASCEND</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
class="position-fixed bottom-0 border-light border-top w-100 py-2 d-flex justify-content-center bg-dark"
|
||||||
|
>
|
||||||
|
<a href="/" type="button" class="btn btn-outline-light px-4 py-1">
|
||||||
|
Back to Home
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Reference in a new issue