Initial commit
TODO: change api.conf URL references to use environment variables and add these variables to the docker-compose configuration for host domain
This commit is contained in:
15
api/Dockerfile
Normal file
15
api/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
# nodejs container
|
||||
FROM node:latest
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /app
|
||||
|
||||
# Bundle app source
|
||||
# COPY . /app
|
||||
|
||||
# Install app dependencies
|
||||
# RUN npm install
|
||||
|
||||
EXPOSE $API_PORT
|
||||
CMD [ "npm", "run", "start" ]
|
||||
|
||||
19
api/db/config.js
Normal file
19
api/db/config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// dotenv
|
||||
// .config()
|
||||
|
||||
module.exports = {
|
||||
HOST: process.env.DB_HOST,
|
||||
PORT: process.env.DB_PORT,
|
||||
// USER: process.env.DB_USER,
|
||||
USER: 'root',
|
||||
// PASSWORD: process.env.DB_PASSWORD,
|
||||
PASSWORD: process.env.DB_ROOT_PASSWORD,
|
||||
DATABASE: process.env.DB_DATABASE,
|
||||
dialect: "mysql",
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
};
|
||||
70
api/db/controllers/Award.controller.js
Normal file
70
api/db/controllers/Award.controller.js
Normal file
@@ -0,0 +1,70 @@
|
||||
const db = require("..");
|
||||
const Award = db.Award;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
// Create and Save a new Award
|
||||
exports.create = (req, res) => {
|
||||
// Validate
|
||||
if (!req.body) {
|
||||
res.status(400).send({
|
||||
message: "Body content can not be empty!"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const awards = []
|
||||
|
||||
if (Array.isArray(req.body)) {
|
||||
awards.push(...req.body)
|
||||
} else {
|
||||
awards.push(req.body)
|
||||
}
|
||||
|
||||
// Save
|
||||
const promises = awards.map(award => {
|
||||
return Award.create(award)
|
||||
});
|
||||
|
||||
// Create
|
||||
const successes = []
|
||||
const failures = []
|
||||
Promise.allSettled(promises)
|
||||
.then(data => {
|
||||
if (data.every(result => result.status === 'fulfilled')) {
|
||||
res.status(201).send({
|
||||
message: "All awards were created successfully.",
|
||||
successes: data.map(result => result.value),
|
||||
failures: [],
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach(result => {
|
||||
if (result.status === 'fulfilled') {
|
||||
successes.push(result.value)
|
||||
} else {
|
||||
failures.push(result.reason.errors)
|
||||
}
|
||||
})
|
||||
if (successes.length === 0) {
|
||||
res.status(500).send({
|
||||
message:
|
||||
"Failed to create any Awards.",
|
||||
failures: failures,
|
||||
successes: successes,
|
||||
});
|
||||
return;
|
||||
}
|
||||
res.status(207).send({
|
||||
message: "Some Awards were created successfully.",
|
||||
successes: successes,
|
||||
failures: failures,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while creating the Award.",
|
||||
});
|
||||
});
|
||||
};
|
||||
176
api/db/controllers/Course.controller.js
Normal file
176
api/db/controllers/Course.controller.js
Normal file
@@ -0,0 +1,176 @@
|
||||
const db = require("../");
|
||||
const Course = db.Course;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
// Create and Save a new Course
|
||||
exports.create = (req, res) => {
|
||||
// Validate
|
||||
if (!req.body) {
|
||||
res.status(400).send({
|
||||
message: "Body content can not be empty!"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Create
|
||||
const courses = []
|
||||
|
||||
if (Array.isArray(req.body)) {
|
||||
courses.push(...req.body)
|
||||
} else {
|
||||
courses.push(req.body)
|
||||
}
|
||||
|
||||
// Save
|
||||
const promises = []
|
||||
courses.forEach(course => {
|
||||
promises.push(Course.create(course))
|
||||
});
|
||||
|
||||
const successes = []
|
||||
const failures = []
|
||||
Promise.allSettled(promises)
|
||||
.then(data => {
|
||||
if (data.every(result => result.status === 'fulfilled')) {
|
||||
res.status(201).send({
|
||||
message: "All courses were created successfully.",
|
||||
successes: data.map(result => result.value),
|
||||
failures: [],
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach(result => {
|
||||
if (result.status === 'fulfilled') {
|
||||
successes.push(result.value)
|
||||
} else {
|
||||
failures.push(result.reason)
|
||||
}
|
||||
})
|
||||
res.status(207).send({
|
||||
message: "Some courses were created successfully.",
|
||||
successes: successes,
|
||||
failures: failures,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while creating the Course.",
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Retrieve all Courses from the database.
|
||||
exports.findAll = (req, res) => {
|
||||
const name = req.query.name;
|
||||
var condition = name ? { name: { [Op.like]: `%${name}%` } } : null;
|
||||
|
||||
Course.findAll({ where: condition, include: ['trainingsHeld'] })
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving courses."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Find a single Course with an id
|
||||
exports.findOne = (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
Course.findByPk(id)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Error retrieving Course with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Update a Course by the id in the request
|
||||
exports.update = (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
Course.update(req.body, {
|
||||
where: { id: id }
|
||||
})
|
||||
.then(num => {
|
||||
if (num == 1) {
|
||||
res.send({
|
||||
message: "Course was updated successfully."
|
||||
});
|
||||
} else {
|
||||
res.send({
|
||||
message: `Cannot update Course with id=${id}. Maybe Course was not found or req.body is empty!`
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Error updating Course with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Delete a Course with the specified id in the request
|
||||
exports.delete = (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
Course.destroy({
|
||||
where: { id: id }
|
||||
})
|
||||
.then(num => {
|
||||
if (num == 1) {
|
||||
res.send({
|
||||
message: "Course was deleted successfully!"
|
||||
});
|
||||
} else {
|
||||
res.send({
|
||||
message: `Cannot delete Course with id=${id}. Maybe Course was not found!`
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Could not delete Course with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Delete all Courses from the database.
|
||||
exports.deleteAll = (req, res) => {
|
||||
Course.destroy({
|
||||
where: {},
|
||||
truncate: false
|
||||
})
|
||||
.then(nums => {
|
||||
res.send({ message: `${nums} Courses were deleted successfully!` });
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while removing all courses."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Find all published Courses
|
||||
exports.findAllPublished = (req, res) => {
|
||||
Course.findAll({ where: { published: true } })
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving courses."
|
||||
});
|
||||
});
|
||||
};
|
||||
100
api/db/controllers/Member.controller.js
Normal file
100
api/db/controllers/Member.controller.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const db = require("..");
|
||||
const Member = db.Member;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
// Create and Save a new Member
|
||||
exports.create = (req, res) => {
|
||||
// Validate
|
||||
if (!req.body) {
|
||||
res.status(400).send({
|
||||
message: "Body content can not be empty!"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Create
|
||||
const members = []
|
||||
|
||||
if (Array.isArray(req.body)) {
|
||||
members.push(...req.body)
|
||||
} else {
|
||||
members.push(req.body)
|
||||
}
|
||||
|
||||
// Save
|
||||
const promises = members.map(member => {
|
||||
return Member.create(member)
|
||||
.then(memberObj => {
|
||||
if (req.body.rankId) {
|
||||
db.Rank.findByPk(req.body.rankId)
|
||||
.then(rank => {
|
||||
memberObj.setRank(rank)
|
||||
})
|
||||
}
|
||||
if (req.body.statusId) {
|
||||
db.MemberStatus.findByPk(req.body.statusId)
|
||||
.then(status => {
|
||||
memberObj.setStatus(status)
|
||||
})
|
||||
}
|
||||
return memberObj
|
||||
})
|
||||
});
|
||||
|
||||
// Create
|
||||
const successes = []
|
||||
const failures = []
|
||||
Promise.allSettled(promises)
|
||||
.then(data => {
|
||||
if (data.every(result => result.status === 'fulfilled')) {
|
||||
res.status(201).send({
|
||||
message: "All members were created successfully.",
|
||||
successes: data.map(result => result.value),
|
||||
failures: [],
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach(result => {
|
||||
if (result.status === 'fulfilled') {
|
||||
successes.push(result.value)
|
||||
} else {
|
||||
failures.push(result.reason.errors)
|
||||
}
|
||||
})
|
||||
if (successes.length === 0) {
|
||||
res.status(500).send({
|
||||
message:
|
||||
"Failed to create any Members.",
|
||||
failures: failures,
|
||||
successes: successes,
|
||||
});
|
||||
return;
|
||||
}
|
||||
res.status(207).send({
|
||||
message: "Some Members were created successfully.",
|
||||
successes: successes,
|
||||
failures: failures,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while creating the Member.",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Retrieve all Members from the database.
|
||||
exports.findAll = (req, res) => {
|
||||
Member.findAll()
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members."
|
||||
});
|
||||
});
|
||||
};
|
||||
70
api/db/controllers/Rank.controller.js
Normal file
70
api/db/controllers/Rank.controller.js
Normal file
@@ -0,0 +1,70 @@
|
||||
const db = require("..");
|
||||
const Rank = db.Rank;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
// Create and Save a new Rank
|
||||
exports.create = (req, res) => {
|
||||
// Validate
|
||||
if (!req.body) {
|
||||
res.status(400).send({
|
||||
message: "Body content can not be empty!"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const ranks = []
|
||||
|
||||
if (Array.isArray(req.body)) {
|
||||
ranks.push(...req.body)
|
||||
} else {
|
||||
ranks.push(req.body)
|
||||
}
|
||||
|
||||
// Save
|
||||
const promises = ranks.map(rank => {
|
||||
return Rank.create(rank)
|
||||
});
|
||||
|
||||
// Create
|
||||
const successes = []
|
||||
const failures = []
|
||||
Promise.allSettled(promises)
|
||||
.then(data => {
|
||||
if (data.every(result => result.status === 'fulfilled')) {
|
||||
res.status(201).send({
|
||||
message: "All ranks were created successfully.",
|
||||
successes: data.map(result => result.value),
|
||||
failures: [],
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach(result => {
|
||||
if (result.status === 'fulfilled') {
|
||||
successes.push(result.value)
|
||||
} else {
|
||||
failures.push(result.reason.errors)
|
||||
}
|
||||
})
|
||||
if (successes.length === 0) {
|
||||
res.status(500).send({
|
||||
message:
|
||||
"Failed to create any Ranks.",
|
||||
failures: failures,
|
||||
successes: successes,
|
||||
});
|
||||
return;
|
||||
}
|
||||
res.status(207).send({
|
||||
message: "Some Ranks were created successfully.",
|
||||
successes: successes,
|
||||
failures: failures,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while creating the Rank.",
|
||||
});
|
||||
});
|
||||
};
|
||||
260
api/db/index.js
Normal file
260
api/db/index.js
Normal file
@@ -0,0 +1,260 @@
|
||||
// require dbconfig
|
||||
const dbConfig = require("./config.js");
|
||||
|
||||
|
||||
// create connection to database
|
||||
const Sequelize = require("sequelize");
|
||||
const sequelize = new Sequelize(
|
||||
dbConfig.DATABASE,
|
||||
dbConfig.USER,
|
||||
dbConfig.PASSWORD,
|
||||
{
|
||||
host: dbConfig.HOST,
|
||||
port: dbConfig.PORT,
|
||||
dialect: dbConfig.dialect,
|
||||
|
||||
pool: {
|
||||
max: dbConfig.pool.max,
|
||||
min: dbConfig.pool.min,
|
||||
acquire: dbConfig.pool.acquire,
|
||||
idle: dbConfig.pool.idle
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
sequelize.authenticate();
|
||||
} catch (error) {
|
||||
console.error('Unable to connect to the database:', error);
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
const db = {};
|
||||
|
||||
db.Sequelize = Sequelize;
|
||||
db.instance = sequelize;
|
||||
|
||||
db.Member = db.instance.define("Member", require("./models/Member.model.js"), { paranoid: true })
|
||||
db.Award = db.instance.define("Award", require("./models/Award.model.js"), { paranoid: true })
|
||||
db.Course = db.instance.define("Course", require("./models/Course.model.js"), { paranoid: true })
|
||||
db.Rank = db.instance.define("Rank", require("./models/Rank.model.js"), { paranoid: true })
|
||||
|
||||
db.AwardAction = db.instance.define("AwardAction", require("./junctionModels/AwardAction.js"), { paranoid: true })
|
||||
db.CourseEventTrainingReport = db.instance.define("CourseEventTrainingReport", require("./junctionModels/CourseEventTrainingReport.model.js"), { paranoid: true })
|
||||
db.CourseEvent = db.instance.define("CourseEvent", require("./models/CourseEvent.js"), { paranoid: true })
|
||||
|
||||
// Members have ranks
|
||||
db.Rank.hasMany(db.Member, {
|
||||
as: "members",
|
||||
foreignKey: "rankId"
|
||||
})
|
||||
db.Member.belongsTo(db.Rank, {
|
||||
as: "rank",
|
||||
foreignKey: "rankId"
|
||||
})
|
||||
|
||||
// Members have statuses
|
||||
db.MemberStatus = db.instance.define("MemberStatus", require("./models/MemberStatus.model.js"), { paranoid: true })
|
||||
db.MemberStatus.hasMany(db.Member, {
|
||||
as: "members",
|
||||
foreignKey: "statusId"
|
||||
})
|
||||
db.Member.belongsTo(db.MemberStatus, {
|
||||
as: "status",
|
||||
foreignKey: "statusId"
|
||||
})
|
||||
|
||||
|
||||
// * AWARDS
|
||||
// Awards have a creator
|
||||
db.Award.belongsTo(db.Member, {
|
||||
as: "createdBy",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
db.Member.hasMany(db.Award, {
|
||||
as: "awardsCreated",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
// Awards have a last modified by
|
||||
db.Award.belongsTo(db.Member, {
|
||||
as: "lastModifiedBy",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
db.Member.hasMany(db.Award, {
|
||||
as: "awardsLastModified",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
// Members are granted awards
|
||||
db.Award.belongsToMany(db.Member, {
|
||||
through: db.AwardAction,
|
||||
as: "awardHolders",
|
||||
foreignKey: "recipientId",
|
||||
});
|
||||
db.Member.belongsToMany(db.Award, {
|
||||
through: db.AwardAction,
|
||||
as: "awardsEarned",
|
||||
foreignKey: "awardId"
|
||||
});
|
||||
// * AWARDS GRANTED/REVOKED
|
||||
// Instances of award grants have a creator
|
||||
db.AwardAction.belongsTo(db.Member, {
|
||||
as: "createdBy",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
db.Member.hasMany(db.AwardAction, {
|
||||
as: "awardGrantsCreated",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
// Instances of award grants have a last modified by
|
||||
db.AwardAction.belongsTo(db.Member, {
|
||||
as: "lastModifiedBy",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
db.Member.hasMany(db.AwardAction, {
|
||||
as: "awardGrantsLastModified",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
// Instances of award grants have the acting member
|
||||
db.AwardAction.belongsTo(db.Member, {
|
||||
as: "actor",
|
||||
foreignKey: "actorId"
|
||||
})
|
||||
db.Member.hasMany(db.AwardAction, {
|
||||
as: "awardGrantsGiven",
|
||||
foreignKey: "actorId"
|
||||
})
|
||||
// Instances of award grants have the recipient
|
||||
db.AwardAction.belongsTo(db.Member, {
|
||||
as: "recipient",
|
||||
foreignKey: "recipientId"
|
||||
})
|
||||
db.Member.hasMany(db.AwardAction, {
|
||||
as: "awardGrantsReceived",
|
||||
foreignKey: "recipientId"
|
||||
})
|
||||
// Instances of award grants have the award
|
||||
db.AwardAction.belongsTo(db.Award, {
|
||||
as: "award",
|
||||
foreignKey: "awardId"
|
||||
})
|
||||
db.Award.hasMany(db.AwardAction, {
|
||||
as: "awardGrants",
|
||||
foreignKey: "awardId"
|
||||
})
|
||||
|
||||
|
||||
|
||||
// * COURSE EVENTS
|
||||
// Events have a creator
|
||||
db.CourseEvent.belongsTo(db.Member, {
|
||||
as: "createdBy",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
db.Member.hasMany(db.CourseEvent, {
|
||||
as: "courseEventsCreated",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
// Events have a last modified by
|
||||
db.CourseEvent.belongsTo(db.Member, {
|
||||
as: "lastModifiedBy",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
db.Member.hasMany(db.CourseEvent, {
|
||||
as: "courseEventsLastModified",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
// Events have a course that's taught
|
||||
db.CourseEvent.belongsTo(db.Course, {
|
||||
as: "courseTaught",
|
||||
foreignKey: "courseTaughtId"
|
||||
})
|
||||
db.Course.hasMany(db.CourseEvent, {
|
||||
as: "trainingsHeld",
|
||||
foreignKey: "courseTaughtId"
|
||||
})
|
||||
// Events have one or more trainer
|
||||
db.CourseEvent.belongsToMany(db.Member, {
|
||||
through: "CourseEventsTrainers",
|
||||
as: "trainers",
|
||||
foreignKey: "trainerId"
|
||||
})
|
||||
db.Member.belongsToMany(db.CourseEvent, {
|
||||
through: "CourseEventsTrainers",
|
||||
as: "courseEventsTaught",
|
||||
foreignKey: "courseEventId"
|
||||
})
|
||||
// Events have one or more observer
|
||||
db.CourseEvent.belongsToMany(db.Member, {
|
||||
through: "CourseEventsObservers",
|
||||
as: "observers",
|
||||
foreignKey: "courseEventId"
|
||||
})
|
||||
db.Member.belongsToMany(db.CourseEvent, {
|
||||
through: "CourseEventsObservers",
|
||||
as: "courseEventsObserved",
|
||||
foreignKey: "observerId"
|
||||
})
|
||||
// Events have one or more attendees, each of which passed or did not
|
||||
db.CourseEvent.belongsToMany(db.Member, {
|
||||
through: db.CourseEventTrainingReport,
|
||||
as: "attendees",
|
||||
foreignKey: "attendeeId"
|
||||
});
|
||||
db.Member.belongsToMany(db.CourseEvent, {
|
||||
through: db.CourseEventTrainingReport,
|
||||
as: "courseEventsAttended",
|
||||
foreignKey: "courseEventId"
|
||||
});
|
||||
|
||||
|
||||
// * COURSES
|
||||
// Courses have a creator
|
||||
db.Course.belongsTo(db.Member, {
|
||||
as: "createdBy",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
db.Member.hasMany(db.Course, {
|
||||
as: "coursesCreated",
|
||||
foreignKey: "createdById"
|
||||
})
|
||||
// Courses have a last modified by
|
||||
db.Course.belongsTo(db.Member, {
|
||||
as: "lastModifiedBy",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
db.Member.hasMany(db.Course, {
|
||||
as: "coursesLastModified",
|
||||
foreignKey: "lastModifiedById"
|
||||
})
|
||||
// Courses have SMEs
|
||||
db.Course.belongsToMany(db.Member, {
|
||||
through: "CoursesSME",
|
||||
as: "sme",
|
||||
foreignKey: "smeId"
|
||||
})
|
||||
db.Member.belongsToMany(db.Course, {
|
||||
through: "CoursesSME",
|
||||
as: "coursesSMEFor",
|
||||
foreignKey: "courseId"
|
||||
})
|
||||
|
||||
|
||||
|
||||
// Courses belong to award paths
|
||||
db.Award.belongsToMany(db.Course, {
|
||||
through: "CoursesAwards",
|
||||
as: "coursesRequired",
|
||||
foreignKey: "courseId"
|
||||
});
|
||||
// Awards have pre-requisite courses
|
||||
db.Course.belongsToMany(db.Award, {
|
||||
through: "CoursesAwards",
|
||||
as: "possibleAwards",
|
||||
foreignKey: "awardId"
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.exports = db;
|
||||
34
api/db/junctionModels/AwardAction.js
Normal file
34
api/db/junctionModels/AwardAction.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
actionDate: {
|
||||
type: Sequelize.DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.DataTypes.NOW
|
||||
},
|
||||
isGrantEvent: {
|
||||
type: Sequelize.DataTypes.BOOLEAN,
|
||||
allowNull: true,
|
||||
defaultValue: true
|
||||
},
|
||||
actorId: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
recipientId: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
awardId: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
createdById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
lastModifiedById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
}
|
||||
21
api/db/junctionModels/courseEventTrainingReport.model.js
Normal file
21
api/db/junctionModels/courseEventTrainingReport.model.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
attendeeId: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
courseEventId: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
passed: {
|
||||
type: Sequelize.DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false
|
||||
},
|
||||
notes: {
|
||||
type: Sequelize.DataTypes.STRING(500),
|
||||
allowNull: true,
|
||||
}
|
||||
}
|
||||
37
api/db/models/Award.model.js
Normal file
37
api/db/models/Award.model.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
name: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
},
|
||||
shortName: {
|
||||
type: Sequelize.DataTypes.STRING(70),
|
||||
allowNull: false
|
||||
},
|
||||
description: {
|
||||
type: Sequelize.DataTypes.STRING(1000),
|
||||
allowNull: true
|
||||
},
|
||||
category: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false
|
||||
},
|
||||
imageUrl: {
|
||||
type: Sequelize.DataTypes.STRING,
|
||||
allowNull: true,
|
||||
isUrl: true
|
||||
},
|
||||
footprint: {
|
||||
type: Sequelize.DataTypes.STRING(45),
|
||||
allowNull: false
|
||||
},
|
||||
createdById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
lastModifiedById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
};
|
||||
32
api/db/models/Course.model.js
Normal file
32
api/db/models/Course.model.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const Sequelize = require("sequelize");
|
||||
|
||||
module.exports = {
|
||||
name: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false
|
||||
},
|
||||
shortName: {
|
||||
type: Sequelize.DataTypes.STRING(70),
|
||||
allowNull: false
|
||||
},
|
||||
category: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false
|
||||
},
|
||||
description: {
|
||||
type: Sequelize.DataTypes.STRING(1000),
|
||||
allowNull: true
|
||||
},
|
||||
imageUrl: {
|
||||
type: Sequelize.DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
createdById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
lastModifiedById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
};
|
||||
25
api/db/models/CourseEvent.js
Normal file
25
api/db/models/CourseEvent.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
notes: {
|
||||
type: Sequelize.DataTypes.STRING(1000),
|
||||
allowNull: true,
|
||||
},
|
||||
runDate: {
|
||||
type: Sequelize.DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.DataTypes.NOW
|
||||
},
|
||||
courseTaughtId: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
createdById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
lastModifiedById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
}
|
||||
53
api/db/models/Member.model.js
Normal file
53
api/db/models/Member.model.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
name: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
email: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: true,
|
||||
// validate: {
|
||||
// isEmail: true
|
||||
// }
|
||||
},
|
||||
password: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: true
|
||||
},
|
||||
website: {
|
||||
type: Sequelize.DataTypes.STRING(240),
|
||||
allowNull: true,
|
||||
// validate: {
|
||||
// isUrl: true
|
||||
// }
|
||||
},
|
||||
steamId64: {
|
||||
type: Sequelize.DataTypes.STRING(17),
|
||||
allowNull: true,
|
||||
unique: true
|
||||
},
|
||||
steamProfileName: {
|
||||
type: Sequelize.DataTypes.STRING(32),
|
||||
allowNull: true
|
||||
},
|
||||
discordId: {
|
||||
type: Sequelize.DataTypes.STRING(18),
|
||||
unique: true,
|
||||
allowNull: true
|
||||
},
|
||||
discordUsername: {
|
||||
type: Sequelize.DataTypes.STRING(32),
|
||||
allowNull: true
|
||||
},
|
||||
createdById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
lastModifiedById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
};
|
||||
16
api/db/models/MemberStatus.model.js
Normal file
16
api/db/models/MemberStatus.model.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
name: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
},
|
||||
createdById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
lastModifiedById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
};
|
||||
0
api/db/models/Mission.model.js
Normal file
0
api/db/models/Mission.model.js
Normal file
35
api/db/models/Rank.model.js
Normal file
35
api/db/models/Rank.model.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
name: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
shortName: {
|
||||
type: Sequelize.DataTypes.STRING(70),
|
||||
allowNull: false
|
||||
},
|
||||
category: {
|
||||
type: Sequelize.DataTypes.STRING(100),
|
||||
allowNull: false
|
||||
},
|
||||
sortId: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
},
|
||||
imageUrl: {
|
||||
type: Sequelize.DataTypes.STRING(240),
|
||||
allowNull: true,
|
||||
isUrl: true
|
||||
},
|
||||
createdById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
lastModifiedById: {
|
||||
type: Sequelize.DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
};
|
||||
98
api/db/routes/Award.route.js
Normal file
98
api/db/routes/Award.route.js
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
const award = require("../controllers/Award.controller.js");
|
||||
|
||||
const db = require("..");
|
||||
|
||||
var router = require("express").Router();
|
||||
|
||||
// Create a new Award
|
||||
router.post("/", award.create);
|
||||
|
||||
// GET AWARD
|
||||
router.get("/", async (req, res) => {
|
||||
const id = req.query.id;
|
||||
if (!id) {
|
||||
return db.Award.findAll()
|
||||
.then(results => res.send(results))
|
||||
}
|
||||
|
||||
return db.Award.findByPk(id)
|
||||
.then(async (award) => {
|
||||
if (award === null) {
|
||||
res.status(404).send({
|
||||
message: `Award with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(award)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving awards."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// GET AWARD DETAILS
|
||||
router.get("/details", async (req, res) => {
|
||||
const id = req.query.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Award id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Award.findByPk(id, {
|
||||
include: [
|
||||
'awardHolders',
|
||||
'coursesRequired'
|
||||
]
|
||||
})
|
||||
.then(async (award) => {
|
||||
if (award === null) {
|
||||
res.status(404).send({
|
||||
message: `Award with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(award)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving awards."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// GET CATEGORIES
|
||||
router.get("/categories", async (req, res) => {
|
||||
return db.Award.findAll({
|
||||
attributes: ['category'],
|
||||
group: ['category']
|
||||
})
|
||||
.then(async (awardCategories) => {
|
||||
if (awardCategories === null) {
|
||||
res.status(404).send({
|
||||
message: `Award categories were not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(awardCategories.map(awardCategory => awardCategory.category))
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving award categories."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
apiPath: "/api/awards",
|
||||
apiRouter: router
|
||||
};
|
||||
96
api/db/routes/Course.route.js
Normal file
96
api/db/routes/Course.route.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const courses = require("../controllers/Course.controller.js");
|
||||
|
||||
const db = require("..");
|
||||
|
||||
var router = require("express").Router();
|
||||
|
||||
// Create a new Course
|
||||
router.post("/", courses.create);
|
||||
|
||||
// GET AWARD
|
||||
router.get("/", async (req, res) => {
|
||||
const id = req.query.id;
|
||||
if (!id) {
|
||||
return db.Course.findAll()
|
||||
.then(results => res.send(results))
|
||||
}
|
||||
|
||||
return db.Course.findByPk(id)
|
||||
.then(async (course) => {
|
||||
if (course === null) {
|
||||
res.status(404).send({
|
||||
message: `Course with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(course)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving courses."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// GET AWARD DETAILS
|
||||
router.get("/details", async (req, res) => {
|
||||
const id = req.query.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Course id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Course.findByPk(id, {
|
||||
include: [
|
||||
'trainingsHeld',
|
||||
'possibleAwards',
|
||||
'sme'
|
||||
]
|
||||
})
|
||||
.then(async (course) => {
|
||||
if (course === null) {
|
||||
res.status(404).send({
|
||||
message: `Course with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(course)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving courses."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// GET CATEGORIES
|
||||
router.get("/categories", async (req, res) => {
|
||||
return db.Course.findAll({
|
||||
attributes: ['category'],
|
||||
group: ['category']
|
||||
})
|
||||
.then(async (courseCategories) => {
|
||||
if (courseCategories === null) {
|
||||
res.status(404).send({
|
||||
message: `Course categories were not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(courseCategories.map(courseCategory => courseCategory.category))
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving course categories."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
apiPath: "/api/courses",
|
||||
apiRouter: router
|
||||
};
|
||||
231
api/db/routes/Member.route.js
Normal file
231
api/db/routes/Member.route.js
Normal file
@@ -0,0 +1,231 @@
|
||||
|
||||
const member = require("../controllers/Member.controller.js");
|
||||
|
||||
const db = require("..");
|
||||
|
||||
var router = require("express").Router();
|
||||
|
||||
// Create a new Member
|
||||
router.post("/", member.create);
|
||||
// Retrieve all Members
|
||||
router.get("/", member.findAll);
|
||||
|
||||
// GET MEMBER
|
||||
router.get("/:id", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Member id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Member.findByPk(id)
|
||||
.then(async (member) => {
|
||||
if (member === null) {
|
||||
res.status(404).send({
|
||||
message: `Member with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(member)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// GET MEMBER DETAILS
|
||||
router.get("/:id/details", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Member id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Member.findByPk(id, {
|
||||
include: [
|
||||
'rank',
|
||||
'status',
|
||||
'awards',
|
||||
'coursesSMEFor',
|
||||
'coursesTaught',
|
||||
'coursesAttended'
|
||||
]
|
||||
})
|
||||
.then(async (member) => {
|
||||
if (member === null) {
|
||||
res.status(404).send({
|
||||
message: `Member with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(member)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// COURSES TAUGHT
|
||||
router.get("/:id/courses/taught", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Member id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Member.findByPk(id)
|
||||
.then(async (member) => {
|
||||
if (member === null) {
|
||||
res.status(404).send({
|
||||
message: `Member with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
const courses = await member.getCoursesSMEFor()
|
||||
res.send(courses)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// COURSES ATTENDED
|
||||
router.get("/:id/courses/attended", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Member id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Member.findByPk(id)
|
||||
.then(async (member) => {
|
||||
if (member === null) {
|
||||
res.status(404).send({
|
||||
message: `Member with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
const courses = await member.getCoursesAttended()
|
||||
res.send(courses)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// COURSES SME FOR
|
||||
router.get("/:id/courses/sme", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Member id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Member.findByPk(id)
|
||||
.then(async (member) => {
|
||||
if (member === null) {
|
||||
res.status(404).send({
|
||||
message: `Member with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
const courses = await member.getCoursesSMEFor()
|
||||
res.send(courses)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// UPDATE MEMBER
|
||||
router.put("/:id", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Member id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Member.findByPk(id)
|
||||
.then(async (member) => {
|
||||
if (member === null) {
|
||||
res.status(404).send({
|
||||
message: `Member with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
member.set(req.body)
|
||||
await member.save()
|
||||
res.send(member)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members.",
|
||||
error: err
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// Delete a Member with id
|
||||
router.delete("/:id", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Member id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Member.findByPk(id)
|
||||
.then(async (member) => {
|
||||
if (member === null) {
|
||||
res.status(404).send({
|
||||
message: `Member with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
await member.destroy()
|
||||
res.send({
|
||||
deleted: member,
|
||||
message: `Member with id=${id} was deleted!`
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving members.",
|
||||
error: err
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// Delete all Members
|
||||
// router.delete("/", member.deleteAll);
|
||||
|
||||
module.exports = {
|
||||
apiPath: "/api/members",
|
||||
apiRouter: router
|
||||
};
|
||||
95
api/db/routes/Rank.route.js
Normal file
95
api/db/routes/Rank.route.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const rank = require("../controllers/Rank.controller.js");
|
||||
|
||||
const db = require("..");
|
||||
|
||||
var router = require("express").Router();
|
||||
|
||||
// Create a new Rank
|
||||
router.post("/", rank.create);
|
||||
|
||||
// GET RANK
|
||||
router.get("/", async (req, res) => {
|
||||
const id = req.query.id;
|
||||
if (!id) {
|
||||
return db.Rank.findAll()
|
||||
.then(results => res.send(results))
|
||||
}
|
||||
|
||||
return db.Rank.findByPk(id)
|
||||
.then(async (rank) => {
|
||||
if (rank === null) {
|
||||
res.status(404).send({
|
||||
message: `Rank with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(rank)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving ranks."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// GET RANK DETAILS
|
||||
router.get("/details", async (req, res) => {
|
||||
const id = req.query.id;
|
||||
if (!id) {
|
||||
res.status(400).send({
|
||||
message: "Rank id cannot be empty!"
|
||||
});
|
||||
return
|
||||
}
|
||||
return db.Rank.findByPk(id, {
|
||||
include: [
|
||||
'members',
|
||||
]
|
||||
})
|
||||
.then(async (rank) => {
|
||||
if (rank === null) {
|
||||
res.status(404).send({
|
||||
message: `Rank with id=${id} was not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(rank)
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving ranks."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// GET CATEGORIES
|
||||
router.get("/categories", async (req, res) => {
|
||||
return db.Rank.findAll({
|
||||
attributes: ['category'],
|
||||
group: ['category']
|
||||
})
|
||||
.then(async (rankCategories) => {
|
||||
if (rankCategories === null) {
|
||||
res.status(404).send({
|
||||
message: `Rank categories were not found!`
|
||||
});
|
||||
return
|
||||
}
|
||||
res.send(rankCategories.map(rankCategory => rankCategory.category))
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving rank categories."
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
module.exports = {
|
||||
apiPath: "/api/ranks",
|
||||
apiRouter: router
|
||||
};
|
||||
2529
api/db/sequelize-docgen.js
Normal file
2529
api/db/sequelize-docgen.js
Normal file
File diff suppressed because it is too large
Load Diff
103
api/index.js
Normal file
103
api/index.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// set up a basic API for MySQL
|
||||
// this is a simple API that will allow us to do basic CRUD operations on our MySQL database
|
||||
|
||||
// import the express module
|
||||
const express = require('express');
|
||||
const cors = require('cors')
|
||||
|
||||
// create an express app
|
||||
const app = express();
|
||||
|
||||
// enable cors
|
||||
var corsOptions = {
|
||||
// origin: "http://localhost:5173"
|
||||
};
|
||||
// app.use(cors(corsOptions));
|
||||
app.use(cors());
|
||||
|
||||
// parse requests of content-type - application/json
|
||||
app.use(express.json());
|
||||
|
||||
// parse requests of content-type - application/x-www-form-urlencoded
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// include all routes
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const routesPath = path.join(__dirname, 'db/routes');
|
||||
const routeFiles = fs.readdirSync(routesPath);
|
||||
|
||||
// auth providers
|
||||
const bearerToken = require('express-bearer-token');
|
||||
// const { expressjwt: jwt } = require("express-jwt");
|
||||
|
||||
for (const file of routeFiles) {
|
||||
const { apiPath, apiRouter } = require(path.join(routesPath, file));
|
||||
|
||||
app.use(
|
||||
apiPath,
|
||||
// JWT Bearer token
|
||||
// jwt({
|
||||
// secret: process.env.JWT_SECRET,
|
||||
// algorithms: ["HS256"],
|
||||
// }),
|
||||
// function (err, req, res, next) {
|
||||
// if (err.name === "UnauthorizedError") {
|
||||
// res.status(401).send("invalid token!");
|
||||
// } else {
|
||||
// next(err);
|
||||
// }
|
||||
// },
|
||||
// simple plaintext Bearer token
|
||||
bearerToken(),
|
||||
function (req, res, next) {
|
||||
if (req.token === process.env.BEARER_TOKEN) {
|
||||
next();
|
||||
} else {
|
||||
res.status(401).send("invalid token!");
|
||||
}
|
||||
},
|
||||
apiRouter
|
||||
);
|
||||
}
|
||||
|
||||
// sync/init database
|
||||
const db = require('./db');
|
||||
const dbConfig = require("./db/config.js");
|
||||
// if database doesn't exist, create it
|
||||
db.instance.query('CREATE DATABASE IF NOT EXISTS ' + dbConfig.DATABASE)
|
||||
.then(() => {
|
||||
console.log('Database created')
|
||||
db.instance.sync({
|
||||
force: true
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
// configure api docs
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const YAML = require('yamljs');
|
||||
const swaggerDocument = YAML.load('openapi.yaml');
|
||||
// local YAML
|
||||
// app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
|
||||
// explorer: true
|
||||
// }));
|
||||
|
||||
// converted from Postman
|
||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
|
||||
explorer: true
|
||||
}));
|
||||
|
||||
Promise.resolve()
|
||||
}).then(() => {
|
||||
|
||||
// set port, listen for requests
|
||||
app.listen(3000, function () {
|
||||
console.log('App running on port ' + 3000);
|
||||
});
|
||||
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
409
api/openapi.json
Normal file
409
api/openapi.json
Normal file
@@ -0,0 +1,409 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "17th Rangers Database API",
|
||||
"description": "An API for the 17th Rangers Database",
|
||||
"contact": {
|
||||
"email": "indigo@indigofox.dev",
|
||||
"name": "Indigo Fox"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://opensource.org/license/mit/"
|
||||
},
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://localhost:3001/api",
|
||||
"description": "Development"
|
||||
},
|
||||
{
|
||||
"url": "https://indigofox.dev:9230/api",
|
||||
"description": "Production"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "members",
|
||||
"description": "Operations on users/members"
|
||||
},
|
||||
{
|
||||
"name": "ranks",
|
||||
"description": "Rank information & related categories"
|
||||
},
|
||||
{
|
||||
"name": "awards",
|
||||
"description": "Badges & ribbons"
|
||||
},
|
||||
{
|
||||
"name": "courses",
|
||||
"description": "Training courses"
|
||||
},
|
||||
{
|
||||
"name": "courseEvents",
|
||||
"description": "Instances of trainings held for specific courses"
|
||||
},
|
||||
{
|
||||
"name": "member statuses",
|
||||
"description": "Member status indicating active, inactive, company membership, etc."
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/members": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"members"
|
||||
],
|
||||
"summary": "Get all members",
|
||||
"description": "Returns a list of all members",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A list of members",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Member"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"500": {
|
||||
"description": "Unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"members"
|
||||
],
|
||||
"summary": "Create a member",
|
||||
"description": "Creates a new member",
|
||||
"requestBody": {
|
||||
"description": "Member object that needs to be added",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MemberPut"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Member created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Member"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/ClientInputError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"500": {
|
||||
"description": "Unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/members/{memberId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"members"
|
||||
],
|
||||
"summary": "Get a member by ID",
|
||||
"description": "Returns a single member",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "memberId",
|
||||
"in": "path",
|
||||
"description": "ID of member to return",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Member found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Member"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/components/responses/NotFound"
|
||||
},
|
||||
"500": {
|
||||
"description": "Unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"members"
|
||||
],
|
||||
"summary": "Update a member by ID",
|
||||
"description": "Updates a member",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "memberId",
|
||||
"in": "path",
|
||||
"description": "ID of member to update",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"description": "Member object that needs to be updated",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MemberPut"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Member updated",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Member"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/ClientInputError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/components/responses/NotFound"
|
||||
},
|
||||
"500": {
|
||||
"description": "Unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/training-reports": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"training-reports"
|
||||
],
|
||||
"summary": "Get all training reports",
|
||||
"description": "Returns a list of all training reports",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A list of training reports",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TrainingReport"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"500": {
|
||||
"description": "Unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"training-reports"
|
||||
],
|
||||
"summary": "Create a training report",
|
||||
"description": "Creates a new training report",
|
||||
"requestBody": {
|
||||
"$ref": "#/components/requestBodies/CourseEventTrainingReport",
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Training report created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TrainingReport"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/ClientInputError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"500": {
|
||||
"description": "Unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/responses/InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Member": {
|
||||
"$ref": "schemas/member.json"
|
||||
},
|
||||
"MemberPut": {
|
||||
"$ref": "schemas/memberPut.json"
|
||||
},
|
||||
"Rank": {
|
||||
"$ref": "schemas/rank.json"
|
||||
},
|
||||
"Award": {
|
||||
"$ref": "schemas/award.json"
|
||||
},
|
||||
"AwardAction": {
|
||||
"$ref": "schemas/awardAction.json"
|
||||
},
|
||||
"CourseEvent": {
|
||||
"$ref": "schemas/courseEvent.json"
|
||||
},
|
||||
"Error": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"description": "A human readable error message",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestBodies": {
|
||||
"CourseEventTrainingReport": {
|
||||
"$ref": "requestBodies/courseEventTrainingReport.json"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"Unauthorized": {
|
||||
"description": "Unauthorized"
|
||||
},
|
||||
"ClientInputError": {
|
||||
"description": "Client Input Error"
|
||||
},
|
||||
"NotFound": {
|
||||
"description": "Not Found"
|
||||
},
|
||||
"InternalServerError": {
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
"bearerAuth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
"bearerFormat": "plain-text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
1565
api/openapi.yaml
Normal file
1565
api/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
1564
api/package-lock.json
generated
Normal file
1564
api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
api/package.json
Normal file
26
api/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "nodemon --legacy-watch -e js,json,yaml index.js",
|
||||
"prod": "node index.js"
|
||||
},
|
||||
"author": "IndigoFox",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"express-bearer-token": "^2.4.0",
|
||||
"express-jwt": "^8.4.1",
|
||||
"mysql2": "^3.2.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"sequelize": "^6.29.3",
|
||||
"swagger-ui-express": "^4.6.2",
|
||||
"yamljs": "^0.3.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user