translate tutorial to node/sql framework

This commit is contained in:
andrzej 2024-05-27 16:12:11 +02:00
parent c749adff34
commit 5a406be06e
6 changed files with 414 additions and 369 deletions

View File

@ -1,52 +0,0 @@
import jwt from "jsonwebtoken";
import passport from "passport";
import jwtSecret from "../config/jwtConfig";
import { db } from "../db.mjs";
import logger from "../logger.mjs";
module.exports = (app) => {
app.post("/loginUser", (req, res, next) => {
passport.authenticate("login", (err, users, info) => {
if (err) {
logger.error(`error ${err}`);
}
if (info !== undefined) {
logger.error(info.message);
if (info.message === "bad username") {
res.status(401).send(info.message);
} else {
res.status(403).send(info.message);
}
} else {
req.logIn(users, async () => {
let user = await db("users")
.select("*")
.where({ username: req.body.username });
user = user[0];
const token = jwt.sign({ id: user.id }, jwtSecret.secret, {
expiresIn: 60 * 60,
});
res.status(200).send({
auth: true,
token,
message: "user found & logged in",
});
// User.findOne({
// where: {
// username: req.body.username,
// },
// }).then((user) => {
// const token = jwt.sign({ id: user.id }, jwtSecret.secret, {
// expiresIn: 60 * 60,
// });
// res.status(200).send({
// auth: true,
// token,
// message: "user found & logged in",
// });
// });
});
}
})(req, res, next);
});
};

77
auth/auth.mjs Normal file
View File

@ -0,0 +1,77 @@
import passport from "passport";
import * as passportLocal from "passport-local";
import { db } from "../db.mjs";
import logger from "../logger.mjs";
import bcrypt from "bcrypt";
//This code saves the information provided by the user to the database, and then sends the user information to the next middleware if successful.
passport.use(
"signup",
new localStrategy(
{
usernameField: "email",
passwordField: "password",
},
async (email, password, done) => {
try {
const user = await db("users").insert({ email, password });
return done(null, user);
} catch (error) {
done(error);
}
},
),
);
async function isValidPwd(user, pwd) {
return bcrypt.compare(pwd, user.password);
}
passport.use(
"login",
new localStrategy(
{
usernameField: "email",
passwordField: "password",
},
async (email, password, done) => {
try {
const user = await db("users").select("*").where({ email });
if (user.length === 0) {
return done(null, false, { message: "User not found" });
}
user = user[0];
const validate = await isValidPwd(user, password);
if (!validate) {
return done(null, false, { message: "Wrong Password" });
}
return done(null, user, { message: "Logged in Successfully" });
} catch (error) {
return done(error);
}
},
),
);
// ...
const JWTstrategy = require("passport-jwt").Strategy;
const ExtractJWT = require("passport-jwt").ExtractJwt;
//This code uses passport-jwt to extract the JWT from the query parameter. It then verifies that this token has been signed with the secret or key set during logging in (TOP_SECRET). If the token is valid, the user details are passed to the next middleware.
passport.use(
new JWTstrategy(
{
secretOrKey: "TOP_SECRET",
jwtFromRequest: ExtractJWT.fromUrlQueryParameter("secret_token"),
},
async (token, done) => {
try {
return done(null, token.user);
} catch (error) {
done(error);
}
},
),
);

View File

@ -39,26 +39,60 @@ export const getEndpoints = (dbObject) => {
return router;
};
export const postEndpoints = (db, data) => {
export const protectedEndpoints = (db, data) => {
const router = express.Router();
protectedEndpoint(router, Story, "create", "insert", db, data);
protectedEndpoint(router, Story, "edit", "update", db, data);
protectedEndpoint(router, Story, "delete", "update", db, data);
protectedEndpoint(router, Submission, "create", "insert", db, data);
protectedEndpoint(router, Submission, "edit", "update", db, data);
protectedEndpoint(router, Submission, "delete", "update", db, data);
protectedEndpoint(router, Publication, "create", "insert", db, data);
protectedEndpoint(router, Publication, "edit", "update", db, data);
protectedEndpoint(router, Publication, "delete", "del", db, data);
writeEndpoint(router, Story, "create", "insert", db, data);
writeEndpoint(router, Story, "edit", "update", db, data);
writeEndpoint(router, Story, "delete", "update", db, data);
writeEndpoint(router, Submission, "create", "insert", db, data);
writeEndpoint(router, Submission, "edit", "update", db, data);
writeEndpoint(router, Submission, "delete", "update", db, data);
writeEndpoint(router, Publication, "create", "insert", db, data);
writeEndpoint(router, Publication, "edit", "update", db, data);
writeEndpoint(router, Publication, "delete", "del", db, data);
//Auth endpoints
router.post(
"/signup",
passport.authenticate("signup", { session: false }),
async (req, res) => {
res.json({
message: "Signup successful",
user: req.user,
});
},
);
router.post("/login", async (req, res, next) => {
passport.authenticate("login", async (err, user, info) => {
try {
if (err || !user) {
const error = new Error("An error occurred.");
return next(error);
}
req.login(user, { session: false }, async (error) => {
if (error) return next(error);
const body = { _id: user._id, email: user.email };
const token = require("jsonwebtoken").sign(
{ user: body },
"TOP_SECRET",
);
return res.json({ token });
});
} catch (error) {
return next(error);
}
})(req, res, next);
});
return router;
};
const protectedEndpoint = (router, Entity, path, method, db, data) => {
const writeEndpoint = (router, Entity, path, method, db, data) => {
router.post(
`/${Entity.name.toLowerCase()}/${path}`,
passport.authenticate("jwt", { session: false }, (_, res) => {
res.json({ message: "protected endpoint" });
}),
passport.authenticate("jwt", { session: false }),
async (req, res) => {
try {
logger.trace({ data: req.body }, "POST request received");

19
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"chai": "^4.3.8",
"chai-as-promised": "^7.1.1",
@ -595,6 +596,24 @@
}
]
},
"node_modules/bcrypt": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
"integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",
"node-addon-api": "^5.0.0"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/bcrypt/node_modules/node-addon-api": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",

View File

@ -9,6 +9,7 @@
"author": "Andrzej Stepien",
"license": "GPL-3.0-or-later",
"dependencies": {
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"chai": "^4.3.8",
"chai-as-promised": "^7.1.1",

View File

@ -1,311 +1,277 @@
import {describe} from "mocha";
import chai, { expect } from "chai";
import { describe } from "mocha";
import chai, { expect } from "chai";
import bodyParser from "body-parser";
import express from 'express'
import express from "express";
import chaiHttp from "chai-http";
import { testDb as db } from "../db.mjs";
import { Data } from "../objects/Data.mjs";
import { beforeEach, afterEach } from "mocha";
import { postEndpoints, getEndpoints } from "../objects/Endpoints.mjs";
import { protectedEndpoints, getEndpoints } from "../objects/Endpoints.mjs";
chai.use(chaiHttp)
const app = express()
const data = new Data(db)
await data.init()
app.use(bodyParser.json())
app.use('/api',getEndpoints(data))
app.use('/api', postEndpoints(db,data))
chai.use(chaiHttp);
const app = express();
const data = new Data(db);
await data.init();
app.use(bodyParser.json());
app.use("/api", getEndpoints(data));
app.use("/api", protectedEndpoints(db, data));
describe("testing endpoints...", async function () {
describe("Testing GET endpoints", async function () {
describe("GET stories", async function () {
it("should return a status code of 200 and an array", async function () {
const res = await chai.request(app).get("/api/stories");
expect(res).to.have.status(200);
expect(res.body).to.be.a("array");
});
});
describe("GET submissions", async function () {
it("should return a status code of 200 and an array", async function () {
const res = await chai.request(app).get("/api/submissions");
expect(res).to.have.status(200);
expect(res.body).to.be.a("array");
});
});
describe("GET publications", async function () {
it("should return a status code of 200 and an array", async function () {
const res = await chai.request(app).get("/api/publications");
expect(res).to.have.status(200);
expect(res.body).to.be.a("array");
});
});
});
describe("testing /create endpoints", async function () {
describe("/story/create", async function () {
const goodData = {
title: "#test",
word_count: 111,
deleted: 0,
};
const badData = {
title: 1,
word_count: "not a number",
};
afterEach(async function () {
await db("stories").where("title", goodData.title).del();
});
it("should return 200 if a valid request is made", async function () {
const res = await chai
.request(app)
.post("/api/story/create")
.send(goodData);
expect(res).to.have.status(200);
});
it("should return 400 if an invalid request is made", async function () {
const res = await chai
.request(app)
.post("/api/story/create")
.send(badData);
expect(res).to.have.status(400);
});
it("the new entry should exist in the database", async function () {
await chai.request(app).post("/api/story/create").send(goodData);
const res = await db("stories")
.select("*")
.where("title", goodData.title);
expect(res[0].title).to.eql(goodData.title);
});
});
describe("/publication/create", async function () {
const goodData = {
title: "#test",
link: "www.internet.com",
deleted: 0,
};
const badData = {
title: 1,
link: 1,
};
afterEach(async function () {
await db("pubs").where("title", goodData.title).del();
});
it("should return 200 if a valid request is made", async function () {
const res = await chai
.request(app)
.post("/api/publication/create")
.send(goodData);
expect(res).to.have.status(200);
});
it("should return 400 if an invalid request is made", async function () {
const res = await chai
.request(app)
.post("/api/publication/create")
.send(badData);
expect(res).to.have.status(400);
});
it("the new entry should exist in the database", async function () {
await chai.request(app).post("/api/publication/create").send(goodData);
const res = await db("pubs").select("*").where("title", goodData.title);
expect(res[0].title).to.eql(goodData.title);
});
});
describe("/submission/create", async function () {
const goodData = {
story_id: 1,
pub_id: 1,
response_id: 1,
date_submitted: "1066-01-01",
date_responded: "1066-01-01",
};
const badData = {
story_id: "string",
pub_id: 1,
response_id: 1,
date_submitted: "1066-01-01",
date_responded: "1066-01-01",
};
afterEach(async function () {
await db("subs").where("date_submitted", goodData.date_submitted).del();
});
it("should return 200 if a valid request is made", async function () {
const res = await chai
.request(app)
.post("/api/submission/create")
.send(goodData);
expect(res).to.have.status(200);
});
it("should return 400 if an invalid request is made", async function () {
const res = await chai
.request(app)
.post("/api/submission/create")
.send(badData);
expect(res).to.have.status(400);
});
it("the new entry should exist in the database", async function () {
await chai.request(app).post("/api/submission/create").send(goodData);
const res = await db("subs")
.select("*")
.where("date_submitted", goodData.date_submitted);
expect(res[0].date_responded).to.eql(goodData.date_responded);
});
});
});
describe("testing /edit endpoints", async function () {
describe("/story/edit", async function () {
const goodData = {
id: 1,
title: "#test",
word_count: 111,
deleted: 0,
};
const badData = {
id: "string",
};
let prev = {};
beforeEach(async function () {
prev = await db("stories").select("*").where("id", 1);
prev = prev[0];
});
afterEach(async function () {
await db("stories").where("id", 1).update(prev);
});
it("should return 200 when sent valid data", async function () {
const res = await chai
.request(app)
.post("/api/story/edit")
.send(goodData);
expect(res).to.have.status(200);
});
it("should return 400 when sent invalid data", async function () {
const res = await chai
.request(app)
.post("/api/story/edit")
.send(badData);
expect(res).to.have.status(400);
});
it("the edit should be reflected in the database", async function () {
await chai.request(app).post("/api/story/edit").send(goodData);
const res = await db("stories").select("*").where("id", goodData.id);
expect(res[0]).to.eql(goodData);
});
});
describe("/publication/edit", async function () {
const goodData = {
id: 1,
title: "#test",
link: "link",
query_after_days: 90,
deleted: 0,
};
const badData = {
id: "string",
};
let prev = {};
beforeEach(async function () {
prev = await db("pubs").select("*").where("id", 1);
prev = prev[0];
});
afterEach(async function () {
await db("pubs").where("id", 1).update(prev);
});
it("should return 200 when sent valid data", async function () {
const res = await chai
.request(app)
.post("/api/publication/edit")
.send(goodData);
expect(res).to.have.status(200);
});
it("should return 400 when sent invalid data", async function () {
const res = await chai
.request(app)
.post("/api/publication/edit")
.send(badData);
expect(res).to.have.status(400);
});
it("the edit should be reflected in the database", async function () {
await chai.request(app).post("/api/publication/edit").send(goodData);
const res = await db("pubs").select("*").where("id", goodData.id);
expect(res[0]).to.eql(goodData);
});
});
describe("/submission/edit", async function () {
const goodData = {
id: 1,
story_id: 1,
pub_id: 1,
response_id: 1,
date_submitted: "1066-01-01",
date_responded: "1066-01-01",
};
const badData = {
story_id: "string",
pub_id: 1,
response_id: 1,
date_submitted: "1066-01-01",
date_responded: "1066-01-01",
};
let prev = {};
beforeEach(async function () {
prev = await db("subs").select("*").where("id", 1);
prev = prev[0];
});
afterEach(async function () {
await db("subs").where("id", 1).update(prev);
});
it("should return 200 when sent valid data", async function () {
const res = await chai
.request(app)
.post("/api/submission/edit")
.send(goodData);
expect(res).to.have.status(200);
});
it("should return 400 when sent invalid data", async function () {
const res = await chai
.request(app)
.post("/api/submission/edit")
.send(badData);
expect(res).to.have.status(400);
});
it("the edit should be reflected in the database", async function () {
await chai.request(app).post("/api/submission/edit").send(goodData);
const res = await db("subs").select("*").where("id", goodData.id);
expect(res[0]).to.eql(goodData);
});
});
});
});
describe("testing endpoints...",async function(){
describe("Testing GET endpoints", async function(){
describe("GET stories",async function(){
it("should return a status code of 200 and an array", async function(){
const res = await chai.request(app).get('/api/stories')
expect(res).to.have.status(200)
expect(res.body).to.be.a('array')
})
})
describe("GET submissions",async function(){
it("should return a status code of 200 and an array", async function(){
const res = await chai.request(app).get('/api/submissions')
expect(res).to.have.status(200)
expect(res.body).to.be.a('array')
})
})
describe("GET publications",async function(){
it("should return a status code of 200 and an array", async function(){
const res = await chai.request(app).get('/api/publications')
expect(res).to.have.status(200)
expect(res.body).to.be.a('array')
})
})
})
describe("testing /create endpoints", async function(){
describe("/story/create",async function(){
const goodData = {
title:"#test",
word_count:111,
deleted:0
}
const badData = {
title:1,
word_count:"not a number"
}
afterEach(async function(){
await db('stories')
.where('title',goodData.title)
.del()
})
it("should return 200 if a valid request is made",async function(){
const res = await chai.request(app)
.post('/api/story/create')
.send(goodData)
expect(res).to.have.status(200)
})
it("should return 400 if an invalid request is made",async function(){
const res = await chai.request(app)
.post('/api/story/create')
.send(badData)
expect(res).to.have.status(400)
})
it("the new entry should exist in the database",async function(){
await chai.request(app)
.post('/api/story/create')
.send(goodData)
const res = await db('stories')
.select('*')
.where('title',goodData.title)
expect(res[0].title).to.eql(goodData.title)
})
})
describe("/publication/create",async function(){
const goodData = {
title:"#test",
link:"www.internet.com",
deleted:0
}
const badData = {
title:1,
link:1
}
afterEach(async function(){
await db('pubs')
.where('title',goodData.title)
.del()
})
it("should return 200 if a valid request is made",async function(){
const res = await chai.request(app)
.post('/api/publication/create')
.send(goodData)
expect(res).to.have.status(200)
})
it("should return 400 if an invalid request is made",async function(){
const res = await chai.request(app)
.post('/api/publication/create')
.send(badData)
expect(res).to.have.status(400)
})
it("the new entry should exist in the database",async function(){
await chai.request(app)
.post('/api/publication/create')
.send(goodData)
const res = await db('pubs')
.select('*')
.where('title',goodData.title)
expect(res[0].title).to.eql(goodData.title)
})
})
describe("/submission/create",async function(){
const goodData = {
story_id:1,
pub_id:1,
response_id:1,
date_submitted:"1066-01-01",
date_responded:"1066-01-01"
}
const badData = {
story_id:"string",
pub_id:1,
response_id:1,
date_submitted:"1066-01-01",
date_responded:"1066-01-01"
}
afterEach(async function(){
await db('subs')
.where('date_submitted',goodData.date_submitted)
.del()
})
it("should return 200 if a valid request is made",async function(){
const res = await chai.request(app)
.post('/api/submission/create')
.send(goodData)
expect(res).to.have.status(200)
})
it("should return 400 if an invalid request is made",async function(){
const res = await chai.request(app)
.post('/api/submission/create')
.send(badData)
expect(res).to.have.status(400)
})
it("the new entry should exist in the database",async function(){
await chai.request(app)
.post('/api/submission/create')
.send(goodData)
const res = await db('subs')
.select('*')
.where('date_submitted',goodData.date_submitted)
expect(res[0].date_responded).to.eql(goodData.date_responded)
})
})
})
describe("testing /edit endpoints",async function(){
describe("/story/edit",async function(){
const goodData = {
id:1,
title:"#test",
word_count:111,
deleted:0
}
const badData = {
id:"string"
}
let prev = {}
beforeEach(async function(){
prev = await db('stories')
.select('*')
.where('id',1)
prev = prev[0]
})
afterEach(async function(){
await db('stories')
.where('id',1)
.update(prev)
})
it("should return 200 when sent valid data",async function(){
const res = await chai.request(app)
.post('/api/story/edit')
.send(goodData)
expect(res).to.have.status(200)
})
it("should return 400 when sent invalid data",async function(){
const res = await chai.request(app)
.post('/api/story/edit')
.send(badData)
expect(res).to.have.status(400)
})
it("the edit should be reflected in the database",async function(){
await chai.request(app)
.post('/api/story/edit')
.send(goodData)
const res = await db('stories').
select('*')
.where('id',goodData.id)
expect(res[0]).to.eql(goodData)
})
})
describe("/publication/edit",async function(){
const goodData = {
id:1,
title:"#test",
link:"link",
query_after_days:90,
deleted:0
}
const badData = {
id:"string"
}
let prev = {}
beforeEach(async function(){
prev = await db('pubs')
.select('*')
.where('id',1)
prev = prev[0]
})
afterEach(async function(){
await db('pubs')
.where('id',1)
.update(prev)
})
it("should return 200 when sent valid data",async function(){
const res = await chai.request(app)
.post('/api/publication/edit')
.send(goodData)
expect(res).to.have.status(200)
})
it("should return 400 when sent invalid data",async function(){
const res = await chai.request(app)
.post('/api/publication/edit')
.send(badData)
expect(res).to.have.status(400)
})
it("the edit should be reflected in the database",async function(){
await chai.request(app)
.post('/api/publication/edit')
.send(goodData)
const res = await db('pubs').
select('*')
.where('id',goodData.id)
expect(res[0]).to.eql(goodData)
})
})
describe("/submission/edit",async function(){
const goodData = {
id:1,
story_id:1,
pub_id:1,
response_id:1,
date_submitted:"1066-01-01",
date_responded:"1066-01-01"
}
const badData = {
story_id:"string",
pub_id:1,
response_id:1,
date_submitted:"1066-01-01",
date_responded:"1066-01-01"
}
let prev = {}
beforeEach(async function(){
prev = await db('subs')
.select('*')
.where('id',1)
prev = prev[0]
})
afterEach(async function(){
await db('subs')
.where('id',1)
.update(prev)
})
it("should return 200 when sent valid data",async function(){
const res = await chai.request(app)
.post('/api/submission/edit')
.send(goodData)
expect(res).to.have.status(200)
})
it("should return 400 when sent invalid data",async function(){
const res = await chai.request(app)
.post('/api/submission/edit')
.send(badData)
expect(res).to.have.status(400)
})
it("the edit should be reflected in the database",async function(){
await chai.request(app)
.post('/api/submission/edit')
.send(goodData)
const res = await db('subs').
select('*')
.where('id',goodData.id)
expect(res[0]).to.eql(goodData)
})
})
})
})