revised to use httpOnly cookie to store jwt
comments can no longer be updated or deleted - out of scope users can now be created, updated and deleted
This commit is contained in:
parent
7ea24df56b
commit
e69fe69d90
12 changed files with 333 additions and 308 deletions
|
|
@ -1,5 +1,5 @@
|
|||
# express-blog-api
|
||||
|
||||
RESTful blog API implemented in Express. Has POST/GET/PUT/DELETE methods for all posts and comments.
|
||||
Post Creation, Updates and Deletion require jwt token returned when post completed at /api/login.
|
||||
Hosted on Deta Space at https://expressblogapi-1-v2871156.deta.app/api/
|
||||
RESTful blog API implemented in Express. Has POST/GET/PUT/DELETE methods for posts and users. Comments cannot be updated or deleted.
|
||||
Post Creation, Updates and Deletion require jwt token returned when login completed at /login. User Updates and Deletion require a matching jwt token.
|
||||
Hosted on Deta Space at https://expressblogapi-1-v2871156.deta.app/
|
||||
|
|
|
|||
6
app.js
6
app.js
|
|
@ -2,9 +2,10 @@ const express = require("express");
|
|||
const path = require("path");
|
||||
const cors = require("cors");
|
||||
const mongoose = require("mongoose");
|
||||
const cookieParser = require("cookie-parser");
|
||||
require("dotenv").config();
|
||||
|
||||
const apiRouter = require("./routes/api.js");
|
||||
const router = require("./routes.js");
|
||||
|
||||
const app = express();
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ app.set("views", path.join(__dirname, "views"));
|
|||
app.set("view engine", "ejs");
|
||||
|
||||
// parsing
|
||||
app.use(cookieParser());
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(express.static(path.join(__dirname, "public")));
|
||||
|
|
@ -36,6 +38,6 @@ app.use(express.static(path.join(__dirname, "public")));
|
|||
app.use(cors());
|
||||
|
||||
// routing
|
||||
app.use("/api", apiRouter);
|
||||
app.use("/", router);
|
||||
|
||||
module.exports = app;
|
||||
|
|
|
|||
|
|
@ -15,12 +15,6 @@ exports.post = [
|
|||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Validate and sanitize password
|
||||
body("password", "Please enter password for future comment modification!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Process request after authentication, validation and sanitization
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
|
|
@ -40,7 +34,6 @@ exports.post = [
|
|||
author: req.body.author,
|
||||
post: req.params.postID,
|
||||
_id: new mongoose.Types.ObjectId(),
|
||||
password: req.body.password,
|
||||
});
|
||||
|
||||
// save to DB
|
||||
|
|
@ -60,96 +53,9 @@ exports.get = asyncHandler(async (req, res, next) => {
|
|||
return res.status(200).json({ comment });
|
||||
});
|
||||
|
||||
// updates comment - U
|
||||
exports.put = [
|
||||
// Validate and sanitize text
|
||||
body("text", "Please enter comment!").isLength({ min: 1 }).trim().escape(),
|
||||
// no U(pdate)
|
||||
// comments cannot be edited as they can be made by anyone
|
||||
// best to prevent impersonation
|
||||
|
||||
// Validate and sanitize author name
|
||||
body("author", "Please enter comment author!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Validate and sanitize password
|
||||
body("password", "Please enter password to modify comment!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Process request after sanitization and validation
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
|
||||
// if there are validation errors, render with errors
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
message: "Comment produced validation errors!",
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
|
||||
const dbComment = await Comment.findOne({ _id: req.params.commentID })
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (req.body.author === dbComment.author) {
|
||||
if (req.body.password === dbComment.password) {
|
||||
const comment = {
|
||||
date: new Date(),
|
||||
text: req.body.text,
|
||||
author: req.body.author,
|
||||
post: dbComment.post,
|
||||
_id: dbComment._id,
|
||||
password: dbComment.password,
|
||||
};
|
||||
|
||||
await comment.save();
|
||||
return res.status(200).json({
|
||||
message: "Post updated!",
|
||||
});
|
||||
}
|
||||
return res.status(401).json({
|
||||
message: "Comments can only be updated with their original password!",
|
||||
});
|
||||
}
|
||||
return res.status(401).json({
|
||||
message: "Comments can only be updated by their original author!",
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// deletes a comment - D
|
||||
exports.delete = [
|
||||
// Validate and sanitize password
|
||||
body("password", "Please enter comment deletion password!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Process request after authentication, validation and sanitization
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
|
||||
// if there are validation errors, render with errors
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
message: "Comment password produced validation errors!",
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
|
||||
const dbComment = await Comment.findOne({ _id: req.params.commentID })
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (req.body.password === dbComment.password) {
|
||||
await Comment.findByIdAndDelete({ _id: req.params.commentID }).exec();
|
||||
return res.status(200).json({ message: "Comment deleted!" });
|
||||
}
|
||||
|
||||
return res.status(401).json({
|
||||
message: "Incorrect password!",
|
||||
});
|
||||
}),
|
||||
];
|
||||
// no D(elete)
|
||||
// again, would require authorization that is out of scope for this project
|
||||
|
|
|
|||
|
|
@ -6,19 +6,22 @@ const User = require("../models/user.js");
|
|||
let opts = {};
|
||||
|
||||
exports.post = asyncHandler(async (req, res, next) => {
|
||||
const admin = await User.findOne({}).lean().exec(); // only one user in DB - admin. pull it up, jamie!
|
||||
const { username, password } = req.body; // get fields from body
|
||||
if (username === admin.username) {
|
||||
console.log();
|
||||
const match = await bcrypt.compare(password, admin.password); // compare bcrypt hashed passwords
|
||||
const user = await User.findOne({ username: username }).lean().exec(); // gets user based on username
|
||||
if (user) {
|
||||
const match = await bcrypt.compare(password, user.password); // compare bcrypt hashed passwords
|
||||
if (match) {
|
||||
opts.expiresIn = 120;
|
||||
opts.expiresIn = "1d";
|
||||
const token = jwt.sign({ username }, process.env.SECRET_KEY, opts); // create token and return below
|
||||
return res.status(200).json({
|
||||
message: "Authentication complete",
|
||||
token,
|
||||
});
|
||||
return res
|
||||
.cookie("JWT_TOKEN", token, {
|
||||
httpOnly: true,
|
||||
})
|
||||
.status(200)
|
||||
.json({
|
||||
message: "Authentication complete", // a winrar is you
|
||||
});
|
||||
}
|
||||
}
|
||||
return res.status(401).json({ message: "Authentication failed" }); // get rekt nerd
|
||||
return res.status(401).json({ message: "Authentication failed" }); // epic fail
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@ const { body, validationResult } = require("express-validator");
|
|||
const Post = require("../models/post.js");
|
||||
const Comment = require("../models/comment.js");
|
||||
|
||||
const passport = require("passport");
|
||||
const jwtStrategy = require("../strategy/jwt.js");
|
||||
passport.use(jwtStrategy);
|
||||
|
||||
// returns json object with ALL posts and comments
|
||||
exports.index = asyncHandler(async (req, res, next) => {
|
||||
const dbPosts = await Post.find().lean().exec();
|
||||
|
|
@ -30,50 +26,54 @@ exports.index = asyncHandler(async (req, res, next) => {
|
|||
});
|
||||
|
||||
// makes new post - C
|
||||
(exports.post = passport.authenticate("jwt", { session: false })),
|
||||
[
|
||||
// Validate and sanitize title
|
||||
body("title", "Please enter blog post title!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
exports.post = [
|
||||
// Validate and sanitize title
|
||||
body("title", "Please enter blog post title!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Validate and sanitize text
|
||||
body("text", "Please enter blog post text!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
// Validate and sanitize text
|
||||
body("text", "Please enter blog post text!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Process request after authentication, validation and sanitization
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
|
||||
// if there are validation errors, render with errors
|
||||
if (!errors.isEmpty()) {
|
||||
res.render("/admin/create"),
|
||||
{
|
||||
errors: errors.array(),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// else data is valid, make post object
|
||||
const post = new Post({
|
||||
title: req.body.title,
|
||||
date: new Date(),
|
||||
text: req.body.text,
|
||||
author: req.user.username,
|
||||
_id: new mongoose.Types.ObjectId(),
|
||||
asyncHandler(async (req, res, next) => {
|
||||
// begin by authorizing token
|
||||
const token = req.cookies.JWT_TOKEN;
|
||||
if (!token) {
|
||||
return res.status(403).json({
|
||||
message: "Not authorized!",
|
||||
});
|
||||
}
|
||||
|
||||
// save to DB
|
||||
await post.save();
|
||||
|
||||
return res.status(200).json({
|
||||
message: "Post created!",
|
||||
// then return any validation errors
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
message: "Comment produced validation errors!",
|
||||
errors: errors.array(),
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
// else data is valid, make post object
|
||||
const post = new Post({
|
||||
title: req.body.title,
|
||||
date: new Date(),
|
||||
text: req.body.text,
|
||||
author: req.user.username,
|
||||
_id: new mongoose.Types.ObjectId(),
|
||||
});
|
||||
|
||||
// save to DB
|
||||
await post.save();
|
||||
|
||||
return res.status(200).json({
|
||||
message: "Post created!",
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// returns post in json format - R
|
||||
exports.get = asyncHandler(async (req, res, next) => {
|
||||
|
|
@ -82,43 +82,66 @@ exports.get = asyncHandler(async (req, res, next) => {
|
|||
});
|
||||
|
||||
// updates post - U
|
||||
(exports.put = passport.authenticate("jwt", { session: false })),
|
||||
[
|
||||
// Validate and sanitize title
|
||||
body("title", "Please enter blog post title!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
exports.put = [
|
||||
// Validate and sanitize title
|
||||
body("title", "Please enter blog post title!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Validate and sanitize text
|
||||
body("text", "Please enter blog post text!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
// Validate and sanitize text
|
||||
body("text", "Please enter blog post text!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Process request after sanitization and validation
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const dbPost = await Post.findOne({ _id: req.params.postID })
|
||||
.lean()
|
||||
.exec();
|
||||
const post = {
|
||||
title: req.body.title,
|
||||
date: new Date(),
|
||||
text: req.body.text,
|
||||
author: dbPost.author,
|
||||
published: dbPost.published,
|
||||
_id: dbPost._id,
|
||||
};
|
||||
await post.save();
|
||||
return res.status(200).json({
|
||||
message: "Post updated!",
|
||||
// Process request after sanitization and validation
|
||||
asyncHandler(async (req, res, next) => {
|
||||
// begin by authorizing token
|
||||
const token = req.cookies.JWT_TOKEN;
|
||||
if (!token) {
|
||||
return res.status(403).json({
|
||||
message: "Not authorized!",
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
// then return any validation errors
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
message: "Comment produced validation errors!",
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
|
||||
// else data is valid, update post object
|
||||
const dbPost = await Post.findOne({ _id: req.params.postID }).lean().exec();
|
||||
const post = new Post({
|
||||
title: req.body.title,
|
||||
date: new Date(),
|
||||
text: req.body.text,
|
||||
author: dbPost.author,
|
||||
published: dbPost.published,
|
||||
_id: dbPost._id,
|
||||
});
|
||||
|
||||
// save to DB
|
||||
await Post.findByIdAndUpdate(dbPost._id, post, {});
|
||||
|
||||
return res.status(200).json({
|
||||
message: "Post updated!",
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// deletes a post - D
|
||||
(exports.delete = passport.authenticate("jwt", { session: false })),
|
||||
asyncHandler(async (req, res, next) => {
|
||||
await Post.findByIdAndDelete({ _id: req.params.postID }).exec();
|
||||
return res.status(200).json({ message: "Post deleted!" });
|
||||
});
|
||||
exports.delete = asyncHandler(async (req, res, next) => {
|
||||
const token = req.cookies.JWT_TOKEN;
|
||||
if (!token) {
|
||||
return res.status(403).json({
|
||||
message: "Not authorized!",
|
||||
});
|
||||
}
|
||||
await Post.findByIdAndDelete({ _id: req.params.postID }).exec();
|
||||
return res.status(200).json({ message: "Post deleted!" });
|
||||
});
|
||||
|
|
|
|||
164
controllers/user.js
Normal file
164
controllers/user.js
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
const asyncHandler = require("express-async-handler");
|
||||
const bcrypt = require("bcryptjs");
|
||||
const User = require("../models/user.js");
|
||||
const jwt = require("jsonwebtoken");
|
||||
|
||||
// C
|
||||
exports.put = [
|
||||
// Validate and sanitize username
|
||||
body("username", "Please enter username!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Validate and sanitize password
|
||||
body("password", "Please enter password!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
// if there are validation errors, return them
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
message: "Comment produced validation errors!",
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
const { username, password } = req.body; // get fields from body
|
||||
const dbUser = await User.findOne({ username: req.params.username })
|
||||
.lean()
|
||||
.exec();
|
||||
const exists = await User.findOne({ username: username }).lean().exec();
|
||||
|
||||
if (exists) {
|
||||
return res.status(409).json({
|
||||
message: "Username is taken!",
|
||||
});
|
||||
}
|
||||
// else
|
||||
const user = new User({
|
||||
username: username,
|
||||
password: await bcrypt.hash(password, 10),
|
||||
_id: dbUser._id,
|
||||
});
|
||||
|
||||
await User.findByIdAndUpdate(dbUser._id, user, {});
|
||||
|
||||
return res.status(200).json({
|
||||
message: "User updated!",
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// R
|
||||
exports.get = asyncHandler(async (req, res, next) => {
|
||||
const user = await User.findOne({ username: req.params.username })
|
||||
.lean()
|
||||
.exec(); // gets user based on username
|
||||
return res.status(200).json({
|
||||
user: user,
|
||||
});
|
||||
});
|
||||
|
||||
// U
|
||||
exports.post = [
|
||||
// Validate and sanitize username
|
||||
body("username", "Please enter username!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
// Validate and sanitize password
|
||||
body("password", "Please enter password!")
|
||||
.isLength({ min: 1 })
|
||||
.trim()
|
||||
.escape(),
|
||||
|
||||
asyncHandler(async (req, res, next) => {
|
||||
// begin by authorizing token
|
||||
const token = req.cookies.JWT_TOKEN;
|
||||
if (!token) {
|
||||
return res.status(403).json({
|
||||
message: "Not authorized!",
|
||||
});
|
||||
}
|
||||
// if token is not for this user - compares by creating another token
|
||||
let opts = {
|
||||
expiresIn: "1d",
|
||||
};
|
||||
const originalUsername = req.params.username;
|
||||
const userToken = jwt.sign(
|
||||
{ originalUsername },
|
||||
process.env.SECRET_KEY,
|
||||
opts
|
||||
);
|
||||
if (token != userToken) {
|
||||
return res.status(403).json({
|
||||
message: "Not authorized!",
|
||||
});
|
||||
}
|
||||
|
||||
// then return any validation errors
|
||||
const errors = validationResult(req);
|
||||
// if there are validation errors, return them
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
message: "Comment produced validation errors!",
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
|
||||
// check for duplicates
|
||||
const { username, password } = req.body; // get fields from body
|
||||
const exists = await User.findOne({ username: originalUsername })
|
||||
.lean()
|
||||
.exec();
|
||||
if (exists) {
|
||||
return res.status(409).json({
|
||||
message: "Username is taken!",
|
||||
});
|
||||
}
|
||||
|
||||
// otherwise update user
|
||||
const user = new User({
|
||||
username: username,
|
||||
password: await bcrypt.hash(password, 10),
|
||||
});
|
||||
await user.save(); // make and save user
|
||||
return res.status(200).json({
|
||||
message: "User created!",
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// D
|
||||
exports.delete = asyncHandler(async (req, res, next) => {
|
||||
// begin by authorizing token
|
||||
const token = req.cookies.JWT_TOKEN;
|
||||
if (!token) {
|
||||
return res.status(403).json({
|
||||
message: "Not authorized!",
|
||||
});
|
||||
}
|
||||
// if token is not for this user - compares by creating another token
|
||||
let opts = {
|
||||
expiresIn: "1d",
|
||||
};
|
||||
const originalUsername = req.params.username;
|
||||
const userToken = jwt.sign(
|
||||
{ originalUsername },
|
||||
process.env.SECRET_KEY,
|
||||
opts
|
||||
);
|
||||
if (token != userToken) {
|
||||
return res.status(403).json({
|
||||
message: "Not authorized!",
|
||||
});
|
||||
}
|
||||
|
||||
// if everything is correct, delete user
|
||||
await User.findOneAndDelete({ username: originalUsername }).exec();
|
||||
return res.status(200).json({ message: "Post deleted!" });
|
||||
});
|
||||
|
|
@ -7,13 +7,12 @@ const CommentSchema = new Schema({
|
|||
author: { type: String, required: true },
|
||||
post: { type: mongoose.ObjectId, required: true },
|
||||
_id: { type: mongoose.ObjectId, required: true },
|
||||
password: { type: String, required: true },
|
||||
});
|
||||
|
||||
// Virtual for comment RESTful functions
|
||||
CommentSchema.virtual("url").get(function () {
|
||||
// We don't use an arrow function as we'll need the this object
|
||||
return `/api/${post}/${this._id}`;
|
||||
return `/${post}/${this._id}`;
|
||||
});
|
||||
|
||||
// Export model
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const PostSchema = new Schema({
|
|||
// Virtual for message URL
|
||||
PostSchema.virtual("url").get(function () {
|
||||
// We don't use an arrow function as we'll need the this object
|
||||
return `/api/${this._id}`;
|
||||
return `/${this._id}`;
|
||||
});
|
||||
|
||||
// Export model
|
||||
|
|
|
|||
100
package-lock.json
generated
100
package-lock.json
generated
|
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bootstrap": "^5.3.1",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"debug": "~2.6.9",
|
||||
"dotenv": "^16.3.1",
|
||||
|
|
@ -20,11 +21,7 @@
|
|||
"express-session": "^1.17.3",
|
||||
"express-validator": "^7.0.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mongoose": "^7.5.1",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-local-mongoose": "^8.0.0"
|
||||
"mongoose": "^7.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
|
|
@ -394,6 +391,26 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser": {
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
||||
"dependencies": {
|
||||
"cookie": "0.4.1",
|
||||
"cookie-signature": "1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
|
|
@ -829,11 +846,6 @@
|
|||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/generaterr": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/generaterr/-/generaterr-1.5.0.tgz",
|
||||
"integrity": "sha512-JgcGRv2yUKeboLvvNrq9Bm90P4iJBu7/vd5wSLYqMG5GJ6SxZT46LAAkMfNhQ+EK3jzC+cRBm7P8aUWYyphgcQ=="
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
|
|
@ -1373,64 +1385,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/passport": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
|
||||
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x",
|
||||
"pause": "0.0.1",
|
||||
"utils-merge": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jaredhanson"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-jwt": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz",
|
||||
"integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==",
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-local": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
||||
"integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-local-mongoose": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-local-mongoose/-/passport-local-mongoose-8.0.0.tgz",
|
||||
"integrity": "sha512-jgfN/B0j11WT5f96QlL5EBvxbIwmzd+tbwPzG1Vk8hzDOF68jrch5M+NFvrHjWjb3lfAU0DkxKmNRT9BjFZysQ==",
|
||||
"dependencies": {
|
||||
"generaterr": "^1.5.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"scmp": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
|
|
@ -1444,11 +1398,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pause": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
|
|
@ -1652,11 +1601,6 @@
|
|||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/scmp": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz",
|
||||
"integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bootstrap": "^5.3.1",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"debug": "~2.6.9",
|
||||
"dotenv": "^16.3.1",
|
||||
|
|
@ -18,10 +19,6 @@
|
|||
"express-session": "^1.17.3",
|
||||
"express-validator": "^7.0.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mongoose": "^7.5.1",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-local-mongoose": "^8.0.0"
|
||||
"mongoose": "^7.5.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,28 @@
|
|||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const login_controller = require("../controllers/login.js");
|
||||
const post_controller = require("../controllers/post.js");
|
||||
const comment_controller = require("../controllers/comment.js");
|
||||
const login_controller = require("./controllers/login.js");
|
||||
const post_controller = require("./controllers/post.js");
|
||||
const comment_controller = require("./controllers/comment.js");
|
||||
const user_controller = require("./controllers/user.js");
|
||||
|
||||
// list all posts and append comments to each post based on id, return as json
|
||||
router.get("/", post_controller.index);
|
||||
|
||||
// login page - should work with json
|
||||
// login page
|
||||
router.post("/login", login_controller.post);
|
||||
|
||||
// user get
|
||||
router.get("/:username", user_controller.get);
|
||||
|
||||
// user post
|
||||
router.post("/new_user", user_controller.post);
|
||||
|
||||
// user put
|
||||
router.put("/:username", user_controller.put);
|
||||
|
||||
// user delete
|
||||
router.delete("/:username", user_controller.delete);
|
||||
|
||||
// post post
|
||||
router.post("/new_post", post_controller.post);
|
||||
|
||||
|
|
@ -28,10 +41,4 @@ router.post("/:postID/new_comment", comment_controller.post);
|
|||
// comment get
|
||||
router.get("/:postID/:commentID", comment_controller.get);
|
||||
|
||||
// comment put
|
||||
router.put("/:postID/:commentID", comment_controller.put);
|
||||
|
||||
// comment delete
|
||||
router.delete("/:postID/:commentID", comment_controller.delete);
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
const User = require("../models/user.js");
|
||||
const JwtStrategy = require("passport-jwt").Strategy;
|
||||
const ExtractJwt = require("passport-jwt").ExtractJwt;
|
||||
|
||||
const opts = {};
|
||||
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
|
||||
opts.secretOrKey = process.env.SECRET_KEY;
|
||||
|
||||
const getAdmin = async () => {
|
||||
const admin = await User.findOne({}).lean().exec(); // only one user in DB - admin. pull it up, jamie!
|
||||
return admin;
|
||||
};
|
||||
|
||||
module.exports = new JwtStrategy(opts, (jwt_payload, done) => {
|
||||
const admin = getAdmin();
|
||||
if (jwt_payload.username === admin.username) {
|
||||
return done(null, true);
|
||||
}
|
||||
return done(null, false);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue