restructured backend project, added migration, seeders and tests

This commit is contained in:
Sascha Kühl 2025-03-16 22:21:12 +01:00
parent e171e01123
commit f7f80ac690
43 changed files with 879 additions and 373 deletions

View File

@ -15,17 +15,17 @@ app.use(express.json());
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
// database // database
const db = require("./models"); const db = require("./models/index");
const Role = db.role; const Role = db.Role;
const User = db.user; const User = db.User;
const PitchType = db.pitchType; const PitchType = db.pitchType;
// db.sequelize.sync(); db.sequelize.sync();
// force: true will drop the table if it already exists // force: true will drop the table if it already exists
db.sequelize.sync({force: true}).then(() => { // db.sequelize.sync({force: true}).then(() => {
console.log('Drop and Re-sync Database with { force: true }'); // console.log('Drop and Re-sync Database with { force: true }');
initial(); // initial();
}); // });
// simple route // simple route
app.get("/", (req, res) => { app.get("/", (req, res) => {

View File

@ -12,11 +12,11 @@ module.exports = {
}, },
test: { test: {
dialect: 'postgres', dialect: 'postgres',
username: 'tester', username: 'test',
password: 'test123', password: 'test123!',
database: 'bullpen', database: 'bullpen',
host: 'localhost', host: 'localhost',
port: 15432, port: 5432,
logging: false, logging: false,
}, },
staging: { staging: {

View File

@ -1,6 +1,6 @@
const db = require("../models"); const db = require("../models/index");
const config = require("../config/auth.config"); const config = require("../config/auth.config");
const { user: User, role: Role, refreshToken: RefreshToken } = db; const { User: User, Role: Role, RefreshToken: RefreshToken } = db;
const Op = db.Sequelize.Op; const Op = db.Sequelize.Op;
@ -32,6 +32,8 @@ exports.signup = (req, res) => {
// user role = 1 // user role = 1
user.setRoles([1]).then(() => { user.setRoles([1]).then(() => {
res.send({ message: "User registered successfully!" }); res.send({ message: "User registered successfully!" });
}, (error) => {
console.log(JSON.stringify(error, null, 2));
}); });
} }
}) })

View File

@ -1,4 +1,4 @@
const db = require("../models"); const db = require("../models/index");
const PitchType = db.pitchType; const PitchType = db.pitchType;
const Op = db.Sequelize.Op; const Op = db.Sequelize.Op;

View File

@ -1,4 +1,4 @@
const db = require("../models"); const db = require("../models/index");
const User = db.user; const User = db.user;
const Op = db.Sequelize.Op; const Op = db.Sequelize.Op;

View File

@ -1,6 +1,6 @@
const jwt = require("jsonwebtoken"); const jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js"); const config = require("../config/auth.config.js");
const db = require("../models"); const db = require("../models/index");
const User = db.user; const User = db.user;
const { TokenExpiredError } = jwt; const { TokenExpiredError } = jwt;

View File

@ -1,6 +1,5 @@
const db = require("../models"); const db = require("../models/index");
const ROLES = db.ROLES; const User = db.User;
const User = db.user;
checkDuplicateUsernameOrEmail = (req, res, next) => { checkDuplicateUsernameOrEmail = (req, res, next) => {
// Username // Username

View File

@ -0,0 +1,45 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
allowNull: false,
type: Sequelize.STRING
},
lastName: {
allowNull: false,
type: Sequelize.STRING
},
email: {
allowNull: false,
type: Sequelize.STRING
},
dateOfBirth: {
allowNull: false,
type: Sequelize.DATE
},
password: {
allowNull: false,
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Users');
}
};

View File

@ -0,0 +1,28 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Roles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Roles');
}
};

View File

@ -0,0 +1,45 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('UserRoles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
roleId: {
type: Sequelize.INTEGER,
references: {
model: 'Roles',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('UserRoles');
}
};

View File

@ -0,0 +1,40 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('RefreshTokens', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
token: {
type: Sequelize.STRING
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
expiryDate: {
type: Sequelize.DATE
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('RefreshTokens');
}
};

View File

@ -0,0 +1,31 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('PitchTypes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
abbreviation: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('PitchTypes');
}
};

View File

@ -0,0 +1,34 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('BullpenSessions', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
pitcherId: {
type: Sequelize.INTEGER
},
startedAt: {
type: Sequelize.DATE
},
finishedAt: {
type: Sequelize.DATE
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('BullpenSessions');
}
};

View File

@ -0,0 +1,49 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Pitches', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
pitchTypeId: {
type: Sequelize.INTEGER,
references: {
model: 'PitchTypes',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
bullpenSessionId: {
type: Sequelize.INTEGER,
references: {
model: 'BullpenSessions',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
aimedArea: {
type: Sequelize.INTEGER
},
hitArea: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Pitches');
}
};

View File

@ -0,0 +1,45 @@
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class BullpenSession extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
BullpenSession.hasMany(models.Pitch, {
foreignKey: 'bullpenSessionId',
as: 'pitches',
});
BullpenSession.belongsTo(models.User, {
as: "pitcher"
});
}
}
BullpenSession.init({
pitcherId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'User',
key: 'id'
}
},
startedAt: {
type: DataTypes.DATE,
allowNull: false,
},
finishedAt: {
type: DataTypes.DATE,
allowNull: false,
}
}, {
sequelize,
modelName: 'BullpenSession',
tableName: 'BullpenSessions'
});
return BullpenSession;
};

41
backend/models/index.js Normal file
View File

@ -0,0 +1,41 @@
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const process = require('process');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.js')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (
file.indexOf('.') !== 0 &&
file !== basename &&
file.slice(-3) === '.js' &&
file.indexOf('.test.js') === -1
);
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;

View File

@ -0,0 +1,51 @@
'use strict';
const {
Model, DataTypes
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Pitch extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
Pitch.belongsTo(models.BullpenSession, {
foreignKey: 'bullpenSessionId',
as: 'bullpenSession'
});
Pitch.belongsTo(models.PitchType);
}
}
Pitch.init({
pitchTypeId: {
type: DataTypes.INTEGER,
allowNull: false,
references: { // User belongsTo PitchType 1:1
model: 'PitchTypes',
key: 'id'
}
},
bullpenSessionId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'BullpenSessions',
key: 'id'
}
},
aimedArea: {
type: DataTypes.INTEGER,
allowNull: false
},
hitArea: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
sequelize,
modelName: 'Pitch',
tableName: 'Pitches'
});
return Pitch;
};

View File

@ -0,0 +1,30 @@
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class PitchType extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
PitchType.init({
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
abbreviation: {
type: DataTypes.STRING,
allowNull: false
}
}, {
sequelize,
modelName: 'PitchType',
tableName: 'PitchTypes'
});
return PitchType;
};

View File

@ -0,0 +1,55 @@
const { Model } = require('sequelize');
const config = require("../config/auth.config");
const {v4: uuidv4} = require("uuid");
module.exports = (sequelize, DataTypes) => {
class RefreshToken extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
RefreshToken.belongsTo(models.User, {
foreignKey: 'userId',
targetKey: 'id'
});
}
}
RefreshToken.init({
token: {
type: DataTypes.STRING,
allowNull: false,
},
expiryDate: {
type: DataTypes.DATE,
allowNull: false,
},
}, {
sequelize,
modelName: 'RefreshToken',
tableName: 'RefreshTokens'
});
RefreshToken.createToken = async function (user) {
let expiredAt = new Date();
expiredAt.setSeconds(expiredAt.getSeconds() + config.jwtRefreshExpiration);
let _token = uuidv4();
let refreshToken = await this.create({
token: _token,
userId: user.id,
expiryDate: expiredAt.getTime(),
});
return refreshToken.token;
};
RefreshToken.verifyExpiration = (token) => {
return token.expiryDate.getTime() < new Date().getTime();
};
return RefreshToken;
};

View File

@ -0,0 +1,28 @@
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Role extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
Role.belongsToMany(models.User, {
through: "UserRoles",
foreignKey: 'roleId',
as: 'users',
});
}
}
Role.init({
name: {
type: DataTypes.STRING,
allowNull: false,
}
}, {
sequelize,
modelName: 'Role',
tableName: "Roles",
});
return Role;
};

View File

@ -0,0 +1,62 @@
const { Model } = require('sequelize');
const bcrypt = require("bcryptjs");
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
User.belongsToMany(models.Role, {
through: "UserRoles",
foreignKey: 'userId',
as: 'roles',
});
User.hasOne(models.RefreshToken, {
foreignKey: 'userId',
targetKey: 'id'
});
}
}
User.init({
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
},
dateOfBirth: {
type: DataTypes.DATE,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
}
}, {
sequelize,
modelName: 'User',
tableName: "Users",
hooks: {
beforeCreate: async (user) => {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
}
});
User.prototype.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
return User;
};

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,7 @@
"db:create:test": "cross-env NODE_ENV=test npx sequelize-cli db:create", "db:create:test": "cross-env NODE_ENV=test npx sequelize-cli db:create",
"db:reset": "npx sequelize-cli db:drop && npx sequelize-cli db:create && npx sequelize-cli db:migrate && npx sequelize-cli db:seed:all --debug", "db:reset": "npx sequelize-cli db:drop && npx sequelize-cli db:create && npx sequelize-cli db:migrate && npx sequelize-cli db:seed:all --debug",
"setup-db": "npx sequelize-cli db:drop && npx sequelize-cli db:create && npx sequelize-cli db:migrate && npx sequelize-cli db:seed:all --debug", "setup-db": "npx sequelize-cli db:drop && npx sequelize-cli db:create && npx sequelize-cli db:migrate && npx sequelize-cli db:seed:all --debug",
"start": "cross-env NODE_ENV=test node server.js", "start": "cross-env NODE_ENV=test node server.js"
"setup-example-db": "node database/setup.js"
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=10"
@ -26,6 +25,7 @@
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"pg": "^8.13.3", "pg": "^8.13.3",
"pg-hstore": "^2.3.4", "pg-hstore": "^2.3.4",
"process": "^0.11.10",
"sequelize": "^6.37.5", "sequelize": "^6.37.5",
"sqlite3": "^5.1.7", "sqlite3": "^5.1.7",
"uuid": "^11.1.0" "uuid": "^11.1.0"

View File

@ -0,0 +1,27 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
return queryInterface.bulkInsert('Roles', [{
id: 1,
name: 'user',
createdAt: new Date(),
updatedAt: new Date(),
}, {
id: 2,
name: 'administrator',
createdAt: new Date(),
updatedAt: new Date()
}]);
},
async down (queryInterface, Sequelize) {
/**
* Add commands to revert seed here.
*
* Example:
* await queryInterface.bulkDelete('People', null, {});
*/
}
};

View File

@ -0,0 +1,25 @@
const bcrypt = require("bcryptjs");
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
return queryInterface.bulkInsert('Users', [{
firstName: 'Nolan',
lastName: 'Ryan',
dateOfBirth: new Date(1947, 1, 31),
email: 'ryan.nolan@bullpen.com',
password: bcrypt.hashSync('nolan', 8),
createdAt: new Date(),
updatedAt: new Date(),
}]);
},
async down (queryInterface, Sequelize) {
/**
* Add commands to revert seed here.
*
* Example:
* await queryInterface.bulkDelete('People', null, {});
*/
}
};

View File

@ -0,0 +1,25 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
/**
* Add seed commands here.
*
* Example:
* await queryInterface.bulkInsert('People', [{
* name: 'John Doe',
* isBetaMember: false
* }], {});
*/
},
async down (queryInterface, Sequelize) {
/**
* Add commands to revert seed here.
*
* Example:
* await queryInterface.bulkDelete('People', null, {});
*/
}
};

View File

@ -1,4 +1,4 @@
const app = require('./src/index'); const app = require('./app');
// set port, listen for requests // set port, listen for requests
const PORT = process.env.PORT || 8080; const PORT = process.env.PORT || 8080;

View File

@ -1,13 +0,0 @@
module.exports = {
HOST: "localhost",
USER: "postgres",
PASSWORD: "123",
DB: "testdb",
dialect: "postgres",
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
};

View File

@ -1,6 +0,0 @@
module.exports = {
dialect: 'sqlite',
storage: 'database/example-db.sqlite',
logQueryParameters: false,
benchmark: false
}

View File

@ -1,34 +0,0 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const BullpenSession = sequelize.define('bullpenSession', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
// pitcher: {
// type: DataTypes.INTEGER,
// allowNull: false,
// references: {
// model: 'User',
// key: 'id'
// }
// },
aimedArea: {
type: DataTypes.INTEGER,
allowNull: false
},
hitArea: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
sequelize,
modelName: 'BullpenSession',
tableName: 'BullpenSessions',
});
return BullpenSession;
};

View File

@ -1,67 +0,0 @@
const fs = require('fs');
const path = require('path');
const env = process.env.NODE_ENV || 'development';
let config = require(path.join(__dirname, '../../config/config.js'))[env];
if (!config) config = require(path.join(__dirname, '../../config/config.js')).development;
const Sequelize = require("sequelize");
// const sequelize = new Sequelize({
// dialect: config.dialect,
// storage: config.storage,
// logQueryParameters: config.logQueryParameters,
// benchmark: config.benchmark
// });
const sequelize = new Sequelize(
config.database,
config.username,
config.password,
config
// {
// host: config.HOST,
// dialect: config.dialect,
// pool: {
// max: config.pool.max,
// min: config.pool.min,
// acquire: config.pool.acquire,
// idle: config.pool.idle
// }
// }
);
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.user = require("./user.model.js")(sequelize);
db.role = require("./role.model.js")(sequelize);
db.pitchType = require("./pitchType.model.js")(sequelize);
db.pitch = require("./pitch.model.js")(sequelize);
db.bullpenSession = require("./bullpenSession.model.js")(sequelize);
db.refreshToken = require("./refreshToken.model.js")(sequelize);
db.role.belongsToMany(db.user, {
through: "UserRoles"
});
db.user.belongsToMany(db.role, {
through: "UserRoles"
});
db.refreshToken.belongsTo(db.user, {
foreignKey: 'userId', targetKey: 'id'
});
db.user.hasOne(db.refreshToken, {
foreignKey: 'userId', targetKey: 'id'
});
db.ROLES = ["user", "admin", "moderator"];
db.bullpenSession.hasMany(db.pitch);
db.bullpenSession.belongsTo(db.user, {
as: "pitcher"
});
db.pitch.belongsTo(db.bullpenSession);
db.pitch.belongsTo(db.pitchType);
module.exports = db;

View File

@ -1,42 +0,0 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const Pitch = sequelize.define('pitch', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
type: {
type: DataTypes.INTEGER,
allowNull: false,
references: { // User belongsTo PitchType 1:1
model: 'PitchTypes',
key: 'id'
}
},
bullpenSessionId: {
type: DataTypes.INTEGER,
allowNull: false,
references: { // User belongsTo PitchType 1:1
model: 'BullpenSessions',
key: 'id'
}
},
aimedArea: {
type: DataTypes.INTEGER,
allowNull: false
},
hitArea: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
sequelize,
modelName: 'Pitch',
tableName: 'Pitches',
});
return Pitch;
};

View File

@ -1,27 +0,0 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const PitchType = sequelize.define('pitchType', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
abbreviation: {
type: DataTypes.STRING,
allowNull: false
}
}, {
sequelize,
modelName: 'PitchType',
tableName: 'PitchTypes'
});
return PitchType;
};

View File

@ -1,40 +0,0 @@
const { DataTypes } = require('sequelize');
const config = require("../config/auth.config");
const { v4: uuidv4 } = require("uuid");
module.exports = (sequelize) => {
const RefreshToken = sequelize.define("refreshToken", {
token: {
type: DataTypes.STRING,
},
expiryDate: {
type: DataTypes.DATE,
},
}, {
sequelize,
modelName: 'RefreshToken',
tableName: 'RefreshTokens',
});
RefreshToken.createToken = async function (user) {
let expiredAt = new Date();
expiredAt.setSeconds(expiredAt.getSeconds() + config.jwtRefreshExpiration);
let _token = uuidv4();
let refreshToken = await this.create({
token: _token,
userId: user.id,
expiryDate: expiredAt.getTime(),
});
return refreshToken.token;
};
RefreshToken.verifyExpiration = (token) => {
return token.expiryDate.getTime() < new Date().getTime();
};
return RefreshToken;
};

View File

@ -1,22 +0,0 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const Role = sequelize.define("role", {
id: {
allowNull: false,
autoIncrement: true,
type: DataTypes.INTEGER,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false,
}
}, {
sequelize,
modelName: 'Role',
tableName: "Roles",
});
return Role;
};

View File

@ -1,50 +0,0 @@
const bcrypt = require('bcryptjs');
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const User = sequelize.define("user", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
},
dateOfBirth: {
type: DataTypes.DATE,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
}
}, {
sequelize,
modelName: 'User',
tableName: "Users",
hooks: {
beforeCreate: async (user) => {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
}
});
User.prototype.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
return User;
};

View File

@ -6,4 +6,6 @@ const signupUser = {
password: "secret123" password: "secret123"
} }
exports.signupUser = signupUser; module.exports = {
signupUser
}

View File

@ -7,64 +7,44 @@ const {
afterAll, afterAll,
} = require('@jest/globals'); } = require('@jest/globals');
const app = require("../src/index") const app = require("../app")
const db = require("../src/models");
const { signupUser } = require("./data/user.test.data") const { signupUser } = require("./data/user.test.data")
const res = require("express/lib/response");
describe("Test the root path", () => { describe("Test user authentication", () => {
beforeAll(() => {
db.sequelize.sync({force: true}).then(() => {
db.role.bulkCreate([
{ name: 'user' },
{ name: 'moderator' },
{ name: 'administrator' },
]);
})
});
afterAll(async () => {
try {
await db.sequelize.close();
} catch (error) {
console.error(error);
process.exit(1);
}
});
test("It should response the GET method", done => {
request(app)
.get("/")
.then(response => {
expect(response.statusCode).toBe(200);
done();
});
});
test("should signup a user", done => { test("should signup a user", done => {
request(app) request(app)
.post("/api/auth/signup") .post("/api/auth/signup")
.send(signupUser) .send(signupUser)
.then( res => { .then( res => {
console.log(JSON.stringify(res, null, 2));
expect(res.header['content-type']).toBe('application/json; charset=utf-8'); expect(res.header['content-type']).toBe('application/json; charset=utf-8');
expect(res.statusCode).toBe(400); expect(res.statusCode).toBe(200);
done(); done();
}, error => { done(); }) });
}); });
test("Test user login", done => {
let user = {};
request(app)
.post("/api/auth/signin")
.send({
email: 'ryan.nolan@bullpen.com',
password: 'nolan'
})
.then( res => {
expect(res.statusCode).toBe(200);
expect(res.body.accessToken).not.toBeNull();
console.log(res.body);
user = res.body;
done();
// }).then(() => {
// request(app)
// .get(`/api/users/${user.id}`)
// .then( res2 => {
// expect(res2.statusCode).toBe(200);
// })
//
});
});
}); });
// describe("GET /api/auth/signup", () => {
// it("should signup a user", async () => {
// return request(app)
// .post("/api/auth/signup")
// .send(signupUser)
// .expect('Content-Type', /json/)
// .expect(200)
// .then((res) => {
// expect(res.statusCode).toBe(200);
// })
// });
// });