├── .DS_Store ├── .gitignore ├── .sequelizerc ├── Dockerfile ├── LICENSE ├── README.md ├── app.js ├── bin └── www ├── config └── config.js ├── controllers ├── classroom.js ├── course.js ├── index.js ├── lecturer.js └── student.js ├── docker-compose.yml ├── gitads.png ├── images └── gitads-ads-image-2.png ├── migrations ├── 20180723034950-create-classroom.js ├── 20180723040654-create-student.js ├── 20180723040702-create-lecturer.js ├── 20180723040709-create-course.js └── 20180723043238-create-student-course.js ├── models ├── classroom.js ├── course.js ├── index.js ├── lecturer.js ├── student.js └── studentcourse.js ├── package-lock.json ├── package.json ├── public └── stylesheets │ └── style.css ├── routes ├── index.js └── users.js └── views ├── error.ejs └── index.ejs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didinj/node-express-postgresql-sequelize/1f9d087b32ef9fb5c201af3a9d8d437f20319e9e/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | "config": path.resolve('./config', 'config.json'), 5 | "models-path": path.resolve('./models'), 6 | "seeders-path": path.resolve('./seeders'), 7 | "migrations-path": path.resolve('./migrations') 8 | }; 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10-slim 2 | 3 | RUN apt-get update \ 4 | && mkdir -p /usr/share/man/man1 \ 5 | && mkdir -p /usr/share/man/man7 \ 6 | && apt-get install -y --no-install-recommends postgresql-client libpq-dev \ 7 | && rm -rf /var/lib/apt/lists/* \ 8 | && apt-get clean 9 | 10 | WORKDIR /usr/src/app 11 | 12 | COPY package* ./ 13 | 14 | RUN npm install -g 15 | 16 | COPY . ./ 17 | 18 | CMD npm start 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Didin Jamaludin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js, Express.js, Sequelize.js and PostgreSQL RESTful API 2 | 3 | This source code is part of [Node.js, Express.js, Sequelize.js and PostgreSQL RESTful API](https://www.djamware.com/post/5b56a6cc80aca707dd4f65a9/nodejs-expressjs-sequelizejs-and-postgresql-restful-api) tutorial. 4 | 5 | To run locally: 6 | 7 | * Make sure you have install and run PostgreSQL server 8 | * Create database with the name same as in config file 9 | * Run `npm install` or `yarn install` 10 | * Run `sequelize db:migrate` 11 | * Run `nodemon` or `npm start` 12 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | 7 | var indexRouter = require('./routes/index'); 8 | var usersRouter = require('./routes/users'); 9 | 10 | var app = express(); 11 | 12 | // view engine setup 13 | app.set('views', path.join(__dirname, 'views')); 14 | app.set('view engine', 'ejs'); 15 | 16 | app.use(logger('dev')); 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: false })); 19 | app.use(cookieParser()); 20 | app.use(express.static(path.join(__dirname, 'public'))); 21 | 22 | app.use('/', indexRouter); 23 | app.use('/users', usersRouter); 24 | 25 | // catch 404 and forward to error handler 26 | app.use(function(req, res, next) { 27 | res.status(404).send({ error: 'Not found' }) 28 | }); 29 | 30 | // error handler 31 | app.use(function(err, req, res, next) { 32 | // set locals, only providing error in development 33 | res.locals.message = err.message; 34 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 35 | res.status(err.status || 500).send({ error: err }) 36 | }); 37 | 38 | module.exports = app; 39 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('node-sequelize:server'); 9 | var http = require('http'); 10 | var models = require('../models'); 11 | 12 | /** 13 | * Sync Database 14 | */ 15 | 16 | models.sequelize.sync() 17 | 18 | /** 19 | * Get port from environment and store in Express. 20 | */ 21 | 22 | var port = normalizePort(process.env.PORT || '3000'); 23 | app.set('port', port); 24 | 25 | /** 26 | * Create HTTP server. 27 | */ 28 | 29 | var server = http.createServer(app); 30 | 31 | /** 32 | * Listen on provided port, on all network interfaces. 33 | */ 34 | 35 | server.listen(port); 36 | server.on('error', onError); 37 | server.on('listening', onListening); 38 | 39 | /** 40 | * Normalize a port into a number, string, or false. 41 | */ 42 | 43 | function normalizePort(val) { 44 | var port = parseInt(val, 10); 45 | 46 | if (isNaN(port)) { 47 | // named pipe 48 | return val; 49 | } 50 | 51 | if (port >= 0) { 52 | // port number 53 | return port; 54 | } 55 | 56 | return false; 57 | } 58 | 59 | /** 60 | * Event listener for HTTP server "error" event. 61 | */ 62 | 63 | function onError(error) { 64 | if (error.syscall !== 'listen') { 65 | throw error; 66 | } 67 | 68 | var bind = typeof port === 'string' 69 | ? 'Pipe ' + port 70 | : 'Port ' + port; 71 | 72 | // handle specific listen errors with friendly messages 73 | switch (error.code) { 74 | case 'EACCES': 75 | console.error(bind + ' requires elevated privileges'); 76 | process.exit(1); 77 | break; 78 | case 'EADDRINUSE': 79 | console.error(bind + ' is already in use'); 80 | process.exit(1); 81 | break; 82 | default: 83 | throw error; 84 | } 85 | } 86 | 87 | /** 88 | * Event listener for HTTP server "listening" event. 89 | */ 90 | 91 | function onListening() { 92 | var addr = server.address(); 93 | var bind = typeof addr === 'string' 94 | ? 'pipe ' + addr 95 | : 'port ' + addr.port; 96 | debug('Listening on ' + bind); 97 | } 98 | -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | const creds = { 2 | development: { 3 | username: process.env.DB_USERNAME, 4 | password: process.env.DB_PASSWORD, 5 | database: process.env.DB_NAME, 6 | host: process.env.DB_HOSTNAME, 7 | dialect: 'postgresql' 8 | }, 9 | test: { 10 | username: process.env.DB_USERNAME, 11 | password: process.env.DB_PASSWORD, 12 | database: process.env.DB_NAME, 13 | host: process.env.DB_HOSTNAME, 14 | dialect: 'postgresql' 15 | }, 16 | production: { 17 | username: process.env.DB_USERNAME, 18 | password: process.env.DB_PASSWORD, 19 | database: process.env.DB_NAME, 20 | host: process.env.DB_HOSTNAME, 21 | dialect: 'postgresql' 22 | } 23 | }; 24 | 25 | module.exports = creds; 26 | -------------------------------------------------------------------------------- /controllers/classroom.js: -------------------------------------------------------------------------------- 1 | const Classroom = require('../models').Classroom; 2 | const Student = require('../models').Student; 3 | 4 | module.exports = { 5 | list(req, res) { 6 | return Classroom 7 | .findAll({ 8 | include: [{ 9 | model: Student, 10 | as: 'students' 11 | }], 12 | order: [ 13 | ['createdAt', 'DESC'], 14 | [{ model: Student, as: 'students' }, 'createdAt', 'DESC'], 15 | ], 16 | }) 17 | .then((classrooms) => res.status(200).send(classrooms)) 18 | .catch((error) => { res.status(400).send(error); }); 19 | }, 20 | 21 | getById(req, res) { 22 | return Classroom 23 | .findByPk(req.params.id, { 24 | include: [{ 25 | model: Student, 26 | as: 'students' 27 | }], 28 | }) 29 | .then((classroom) => { 30 | if (!classroom) { 31 | return res.status(404).send({ 32 | message: 'Classroom Not Found', 33 | }); 34 | } 35 | return res.status(200).send(classroom); 36 | }) 37 | .catch((error) => { 38 | console.log(error); 39 | res.status(400).send(error); 40 | }); 41 | }, 42 | 43 | add(req, res) { 44 | return Classroom 45 | .create({ 46 | class_name: req.body.class_name, 47 | }) 48 | .then((classroom) => res.status(201).send(classroom)) 49 | .catch((error) => res.status(400).send(error)); 50 | }, 51 | 52 | addWithStudents(req, res) { 53 | return Classroom 54 | .create({ 55 | class_name: req.body.class_name, 56 | students: req.body.students, 57 | }, { 58 | include: [{ 59 | model: Student, 60 | as: 'students' 61 | }] 62 | }) 63 | .then((classroom) => res.status(201).send(classroom)) 64 | .catch((error) => res.status(400).send(error)); 65 | }, 66 | 67 | update(req, res) { 68 | return Classroom 69 | .findByPk(req.params.id, { 70 | include: [{ 71 | model: Student, 72 | as: 'students' 73 | }], 74 | }) 75 | .then(classroom => { 76 | if (!classroom) { 77 | return res.status(404).send({ 78 | message: 'Classroom Not Found', 79 | }); 80 | } 81 | return classroom 82 | .update({ 83 | class_name: req.body.class_name || classroom.class_name, 84 | }) 85 | .then(() => res.status(200).send(classroom)) 86 | .catch((error) => res.status(400).send(error)); 87 | }) 88 | .catch((error) => res.status(400).send(error)); 89 | }, 90 | 91 | delete(req, res) { 92 | return Classroom 93 | .findByPk(req.params.id) 94 | .then(classroom => { 95 | if (!classroom) { 96 | return res.status(400).send({ 97 | message: 'Classroom Not Found', 98 | }); 99 | } 100 | return classroom 101 | .destroy() 102 | .then(() => res.status(204).send()) 103 | .catch((error) => res.status(400).send(error)); 104 | }) 105 | .catch((error) => res.status(400).send(error)); 106 | }, 107 | }; 108 | -------------------------------------------------------------------------------- /controllers/course.js: -------------------------------------------------------------------------------- 1 | const Course = require('../models').Course; 2 | const Student = require('../models').Student; 3 | const Lecturer = require('../models').Lecturer; 4 | 5 | module.exports = { 6 | list(req, res) { 7 | return Course 8 | .findAll({ 9 | include: [{ 10 | model: Student, 11 | as: 'students' 12 | },{ 13 | model: Lecturer, 14 | as: 'lecturer' 15 | }], 16 | order: [ 17 | ['createdAt', 'DESC'], 18 | [{ model: Student, as: 'students' }, 'createdAt', 'DESC'], 19 | ], 20 | }) 21 | .then((courses) => res.status(200).send(courses)) 22 | .catch((error) => { res.status(400).send(error); }); 23 | }, 24 | 25 | getById(req, res) { 26 | return Course 27 | .findByPk(req.params.id, { 28 | include: [{ 29 | model: Course, 30 | as: 'course' 31 | }], 32 | }) 33 | .then((course) => { 34 | if (!course) { 35 | return res.status(404).send({ 36 | message: 'Course Not Found', 37 | }); 38 | } 39 | return res.status(200).send(course); 40 | }) 41 | .catch((error) => res.status(400).send(error)); 42 | }, 43 | 44 | add(req, res) { 45 | return Course 46 | .create({ 47 | course_name: req.body.course_name, 48 | }) 49 | .then((course) => res.status(201).send(course)) 50 | .catch((error) => res.status(400).send(error)); 51 | }, 52 | 53 | update(req, res) { 54 | return Course 55 | .findByPk(req.params.id, { 56 | include: [{ 57 | model: Course, 58 | as: 'course' 59 | }], 60 | }) 61 | .then(course => { 62 | if (!course) { 63 | return res.status(404).send({ 64 | message: 'Course Not Found', 65 | }); 66 | } 67 | return course 68 | .update({ 69 | course_name: req.body.course_name || classroom.course_name, 70 | }) 71 | .then(() => res.status(200).send(course)) 72 | .catch((error) => res.status(400).send(error)); 73 | }) 74 | .catch((error) => res.status(400).send(error)); 75 | }, 76 | 77 | delete(req, res) { 78 | return Course 79 | .findByPk(req.params.id) 80 | .then(course => { 81 | if (!course) { 82 | return res.status(400).send({ 83 | message: 'Course Not Found', 84 | }); 85 | } 86 | return course 87 | .destroy() 88 | .then(() => res.status(204).send()) 89 | .catch((error) => res.status(400).send(error)); 90 | }) 91 | .catch((error) => res.status(400).send(error)); 92 | }, 93 | }; 94 | -------------------------------------------------------------------------------- /controllers/index.js: -------------------------------------------------------------------------------- 1 | const classroom = require('./classroom'); 2 | const student = require('./student'); 3 | const lecturer = require('./lecturer'); 4 | const course = require('./course'); 5 | 6 | module.exports = { 7 | classroom, 8 | student, 9 | lecturer, 10 | course, 11 | }; 12 | -------------------------------------------------------------------------------- /controllers/lecturer.js: -------------------------------------------------------------------------------- 1 | const Lecturer = require('../models').Lecturer; 2 | const Course = require('../models').Course; 3 | 4 | module.exports = { 5 | list(req, res) { 6 | return Lecturer 7 | .findAll({ 8 | include: [{ 9 | model: Course, 10 | as: 'course' 11 | }], 12 | order: [ 13 | ['createdAt', 'DESC'], 14 | [{ model: Course, as: 'course' }, 'createdAt', 'DESC'], 15 | ], 16 | }) 17 | .then((lecturers) => res.status(200).send(lecturers)) 18 | .catch((error) => { res.status(400).send(error); }); 19 | }, 20 | 21 | getById(req, res) { 22 | return Lecturer 23 | .findByPk(req.params.id, { 24 | include: [{ 25 | model: Course, 26 | as: 'course' 27 | }], 28 | }) 29 | .then((lecturer) => { 30 | if (!lecturer) { 31 | return res.status(404).send({ 32 | message: 'Lecturer Not Found', 33 | }); 34 | } 35 | return res.status(200).send(lecturer); 36 | }) 37 | .catch((error) => res.status(400).send(error)); 38 | }, 39 | 40 | add(req, res) { 41 | return Lecturer 42 | .create({ 43 | lecturer_name: req.body.lecturer_name, 44 | }) 45 | .then((lecturer) => res.status(201).send(lecturer)) 46 | .catch((error) => res.status(400).send(error)); 47 | }, 48 | 49 | addWithCourse(req, res) { 50 | return Lecturer 51 | .create({ 52 | lecturer_name: req.body.lecturer_name, 53 | course: req.body.course 54 | }, { 55 | include: [{ 56 | model: Course, 57 | as: 'course' 58 | }] 59 | }) 60 | .then((lecturer) => res.status(201).send(lecturer)) 61 | .catch((error) => res.status(400).send(error)); 62 | }, 63 | 64 | update(req, res) { 65 | return Lecturer 66 | .findByPk(req.params.id, { 67 | include: [{ 68 | model: Course, 69 | as: 'course' 70 | }], 71 | }) 72 | .then(lecturer => { 73 | if (!lecturer) { 74 | return res.status(404).send({ 75 | message: 'Lecturer Not Found', 76 | }); 77 | } 78 | return lecturer 79 | .update({ 80 | lecturer_name: req.body.lecturer_name || classroom.lecturer_name, 81 | }) 82 | .then(() => res.status(200).send(lecturer)) 83 | .catch((error) => res.status(400).send(error)); 84 | }) 85 | .catch((error) => res.status(400).send(error)); 86 | }, 87 | 88 | delete(req, res) { 89 | return Lecturer 90 | .findByPk(req.params.id) 91 | .then(lecturer => { 92 | if (!lecturer) { 93 | return res.status(400).send({ 94 | message: 'Lecturer Not Found', 95 | }); 96 | } 97 | return lecturer 98 | .destroy() 99 | .then(() => res.status(204).send()) 100 | .catch((error) => res.status(400).send(error)); 101 | }) 102 | .catch((error) => res.status(400).send(error)); 103 | }, 104 | }; 105 | -------------------------------------------------------------------------------- /controllers/student.js: -------------------------------------------------------------------------------- 1 | const Student = require('../models').Student; 2 | const Classroom = require('../models').Classroom; 3 | const Course = require('../models').Course; 4 | 5 | module.exports = { 6 | list(req, res) { 7 | return Student 8 | .findAll({ 9 | include: [{ 10 | model: Classroom, 11 | as: 'classroom' 12 | },{ 13 | model: Course, 14 | as: 'courses' 15 | }], 16 | order: [ 17 | ['createdAt', 'DESC'], 18 | [{ model: Course, as: 'courses' }, 'createdAt', 'DESC'], 19 | ], 20 | }) 21 | .then((students) => res.status(200).send(students)) 22 | .catch((error) => { res.status(400).send(error); }); 23 | }, 24 | 25 | getById(req, res) { 26 | return Student 27 | .findByPk(req.params.id, { 28 | include: [{ 29 | model: Classroom, 30 | as: 'classroom' 31 | },{ 32 | model: Course, 33 | as: 'courses' 34 | }], 35 | }) 36 | .then((student) => { 37 | if (!student) { 38 | return res.status(404).send({ 39 | message: 'Student Not Found', 40 | }); 41 | } 42 | return res.status(200).send(student); 43 | }) 44 | .catch((error) => res.status(400).send(error)); 45 | }, 46 | 47 | add(req, res) { 48 | return Student 49 | .create({ 50 | classroom_id: req.body.classroom_id, 51 | student_name: req.body.student_name, 52 | }) 53 | .then((student) => res.status(201).send(student)) 54 | .catch((error) => res.status(400).send(error)); 55 | }, 56 | 57 | addCourse(req, res) { 58 | return Student 59 | .findByPk(req.body.student_id, { 60 | include: [{ 61 | model: Classroom, 62 | as: 'classroom' 63 | },{ 64 | model: Course, 65 | as: 'courses' 66 | }], 67 | }) 68 | .then((student) => { 69 | if (!student) { 70 | return res.status(404).send({ 71 | message: 'Student Not Found', 72 | }); 73 | } 74 | Course.findByPk(req.body.course_id).then((course) => { 75 | if (!course) { 76 | return res.status(404).send({ 77 | message: 'Course Not Found', 78 | }); 79 | } 80 | student.addCourse(course); 81 | return res.status(200).send(student); 82 | }) 83 | }) 84 | .catch((error) => res.status(400).send(error)); 85 | }, 86 | 87 | update(req, res) { 88 | return Student 89 | .findByPk(req.params.id, { 90 | include: [{ 91 | model: Classroom, 92 | as: 'classroom' 93 | },{ 94 | model: Course, 95 | as: 'courses' 96 | }], 97 | }) 98 | .then(student => { 99 | if (!student) { 100 | return res.status(404).send({ 101 | message: 'Student Not Found', 102 | }); 103 | } 104 | return student 105 | .update({ 106 | student_name: req.body.student_name || student.student_name, 107 | }) 108 | .then(() => res.status(200).send(student)) 109 | .catch((error) => res.status(400).send(error)); 110 | }) 111 | .catch((error) => res.status(400).send(error)); 112 | }, 113 | 114 | delete(req, res) { 115 | return Student 116 | .findByPk(req.params.id) 117 | .then(student => { 118 | if (!student) { 119 | return res.status(400).send({ 120 | message: 'Student Not Found', 121 | }); 122 | } 123 | return student 124 | .destroy() 125 | .then(() => res.status(204).send()) 126 | .catch((error) => res.status(400).send(error)); 127 | }) 128 | .catch((error) => res.status(400).send(error)); 129 | }, 130 | }; 131 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | api: 5 | build: . 6 | environment: 7 | DB_USERNAME: postgres 8 | DB_PASSWORD: test 9 | DB_NAME: test 10 | DB_HOSTNAME: db 11 | ports: 12 | - 3000:3000 13 | depends_on: 14 | db: 15 | condition: service_healthy 16 | db: 17 | image: postgres:10 18 | restart: always 19 | environment: 20 | POSTGRES_PASSWORD: test 21 | POSTGRES_DB: test 22 | ALLOW_IP_RANGE: 0.0.0.0/0 23 | healthcheck: 24 | test: ["CMD-SHELL", "pg_isready -U postgres"] 25 | interval: 10s 26 | timeout: 5s 27 | retries: 5 28 | -------------------------------------------------------------------------------- /gitads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didinj/node-express-postgresql-sequelize/1f9d087b32ef9fb5c201af3a9d8d437f20319e9e/gitads.png -------------------------------------------------------------------------------- /images/gitads-ads-image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didinj/node-express-postgresql-sequelize/1f9d087b32ef9fb5c201af3a9d8d437f20319e9e/images/gitads-ads-image-2.png -------------------------------------------------------------------------------- /migrations/20180723034950-create-classroom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Classrooms', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | class_name: { 12 | type: Sequelize.STRING 13 | }, 14 | createdAt: { 15 | allowNull: false, 16 | type: Sequelize.DATE 17 | }, 18 | updatedAt: { 19 | allowNull: false, 20 | type: Sequelize.DATE 21 | } 22 | }); 23 | }, 24 | down: (queryInterface, Sequelize) => { 25 | return queryInterface.dropTable('Classrooms'); 26 | } 27 | }; -------------------------------------------------------------------------------- /migrations/20180723040654-create-student.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Students', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | classroom_id: { 12 | type: Sequelize.INTEGER 13 | }, 14 | student_name: { 15 | type: Sequelize.STRING 16 | }, 17 | createdAt: { 18 | allowNull: false, 19 | type: Sequelize.DATE 20 | }, 21 | updatedAt: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | } 25 | }); 26 | }, 27 | down: (queryInterface, Sequelize) => { 28 | return queryInterface.dropTable('Students'); 29 | } 30 | }; -------------------------------------------------------------------------------- /migrations/20180723040702-create-lecturer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Lecturers', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | lecturer_name: { 12 | type: Sequelize.STRING 13 | }, 14 | createdAt: { 15 | allowNull: false, 16 | type: Sequelize.DATE 17 | }, 18 | updatedAt: { 19 | allowNull: false, 20 | type: Sequelize.DATE 21 | } 22 | }); 23 | }, 24 | down: (queryInterface, Sequelize) => { 25 | return queryInterface.dropTable('Lecturers'); 26 | } 27 | }; -------------------------------------------------------------------------------- /migrations/20180723040709-create-course.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Courses', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | lecturer_id: { 12 | type: Sequelize.INTEGER 13 | }, 14 | course_name: { 15 | type: Sequelize.STRING 16 | }, 17 | createdAt: { 18 | allowNull: false, 19 | type: Sequelize.DATE 20 | }, 21 | updatedAt: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | } 25 | }); 26 | }, 27 | down: (queryInterface, Sequelize) => { 28 | return queryInterface.dropTable('Courses'); 29 | } 30 | }; -------------------------------------------------------------------------------- /migrations/20180723043238-create-student-course.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('StudentCourses', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | student_id: { 12 | type: Sequelize.INTEGER 13 | }, 14 | course_id: { 15 | type: Sequelize.INTEGER 16 | }, 17 | createdAt: { 18 | allowNull: false, 19 | type: Sequelize.DATE 20 | }, 21 | updatedAt: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | } 25 | }); 26 | }, 27 | down: (queryInterface, Sequelize) => { 28 | return queryInterface.dropTable('StudentCourses'); 29 | } 30 | }; -------------------------------------------------------------------------------- /models/classroom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | Model 4 | } = require('sequelize'); 5 | module.exports = (sequelize, DataTypes) => { 6 | class Classroom extends Model { 7 | static associate(models) { 8 | Classroom.hasMany(models.Student, { 9 | foreignKey: 'classroom_id', 10 | as: 'students', 11 | }); 12 | } 13 | }; 14 | Classroom.init({ 15 | id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false }, 16 | class_name: DataTypes.STRING 17 | }, { 18 | sequelize, 19 | modelName: 'Classroom', 20 | }); 21 | return Classroom; 22 | }; 23 | -------------------------------------------------------------------------------- /models/course.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | Model 4 | } = require('sequelize'); 5 | module.exports = (sequelize, DataTypes) => { 6 | class Course extends Model { 7 | static associate(models) { 8 | Course.belongsToMany(models.Student, { 9 | through: 'StudentCourse', 10 | as: 'students', 11 | foreignKey: 'course_id' 12 | }); 13 | Course.belongsTo(models.Lecturer, { 14 | foreignKey: 'lecturer_id', 15 | as: 'lecturer' 16 | }); 17 | } 18 | }; 19 | Course.init({ 20 | lecturer_id: DataTypes.INTEGER, 21 | course_name: DataTypes.STRING 22 | }, { 23 | sequelize, 24 | modelName: 'Course', 25 | }); 26 | return Course; 27 | }; 28 | -------------------------------------------------------------------------------- /models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const Sequelize = require('sequelize'); 6 | const basename = path.basename(__filename); 7 | const env = process.env.NODE_ENV || 'development'; 8 | const config = require(__dirname + '/../config/config.json')[env]; 9 | const db = {}; 10 | 11 | let sequelize; 12 | if (config.use_env_variable) { 13 | sequelize = new Sequelize(process.env[config.use_env_variable], config); 14 | } else { 15 | sequelize = new Sequelize(config.database, config.username, config.password, config); 16 | } 17 | 18 | fs 19 | .readdirSync(__dirname) 20 | .filter(file => { 21 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 22 | }) 23 | .forEach(file => { 24 | const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); 25 | db[model.name] = model; 26 | }); 27 | 28 | Object.keys(db).forEach(modelName => { 29 | if (db[modelName].associate) { 30 | db[modelName].associate(db); 31 | } 32 | }); 33 | 34 | db.sequelize = sequelize; 35 | db.Sequelize = Sequelize; 36 | 37 | module.exports = db; 38 | -------------------------------------------------------------------------------- /models/lecturer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | Model 4 | } = require('sequelize'); 5 | module.exports = (sequelize, DataTypes) => { 6 | class Lecturer extends Model { 7 | static associate(models) { 8 | Lecturer.hasOne(models.Course, { 9 | foreignKey: 'lecturer_id', 10 | as: 'course', 11 | }); 12 | } 13 | }; 14 | Lecturer.init({ 15 | lecturer_name: DataTypes.STRING 16 | }, { 17 | sequelize, 18 | modelName: 'Lecturer', 19 | }); 20 | return Lecturer; 21 | }; 22 | -------------------------------------------------------------------------------- /models/student.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | Model 4 | } = require('sequelize'); 5 | module.exports = (sequelize, DataTypes) => { 6 | class Student extends Model { 7 | static associate(models) { 8 | Student.belongsTo(models.Classroom, { 9 | foreignKey: 'classroom_id', 10 | as: 'classroom' 11 | }); 12 | Student.belongsToMany(models.Course, { 13 | through: 'StudentCourse', 14 | as: 'courses', 15 | foreignKey: 'student_id' 16 | }); 17 | } 18 | }; 19 | Student.init({ 20 | classroom_id: DataTypes.INTEGER, 21 | student_name: DataTypes.STRING 22 | }, { 23 | sequelize, 24 | modelName: 'Student', 25 | }); 26 | return Student; 27 | }; 28 | -------------------------------------------------------------------------------- /models/studentcourse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | Model 4 | } = require('sequelize'); 5 | module.exports = (sequelize, DataTypes) => { 6 | class StudentCourse extends Model { 7 | static associate(models) { 8 | // define association here 9 | } 10 | }; 11 | StudentCourse.init({ 12 | student_id: DataTypes.INTEGER, 13 | course_id: DataTypes.INTEGER 14 | }, { 15 | sequelize, 16 | modelName: 'StudentCourse', 17 | }); 18 | return StudentCourse; 19 | }; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-sequelize", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "14.0.14", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.14.tgz", 10 | "integrity": "sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ==" 11 | }, 12 | "accepts": { 13 | "version": "1.3.7", 14 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 15 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 16 | "requires": { 17 | "mime-types": "~2.1.24", 18 | "negotiator": "0.6.2" 19 | } 20 | }, 21 | "ansi-styles": { 22 | "version": "3.2.1", 23 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 24 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 25 | "requires": { 26 | "color-convert": "^1.9.0" 27 | } 28 | }, 29 | "any-promise": { 30 | "version": "1.3.0", 31 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 32 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" 33 | }, 34 | "array-flatten": { 35 | "version": "1.1.1", 36 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 37 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 38 | }, 39 | "async": { 40 | "version": "0.9.2", 41 | "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", 42 | "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" 43 | }, 44 | "balanced-match": { 45 | "version": "1.0.0", 46 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 47 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 48 | }, 49 | "basic-auth": { 50 | "version": "2.0.1", 51 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 52 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 53 | "requires": { 54 | "safe-buffer": "5.1.2" 55 | } 56 | }, 57 | "body-parser": { 58 | "version": "1.19.0", 59 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 60 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 61 | "requires": { 62 | "bytes": "3.1.0", 63 | "content-type": "~1.0.4", 64 | "debug": "2.6.9", 65 | "depd": "~1.1.2", 66 | "http-errors": "1.7.2", 67 | "iconv-lite": "0.4.24", 68 | "on-finished": "~2.3.0", 69 | "qs": "6.7.0", 70 | "raw-body": "2.4.0", 71 | "type-is": "~1.6.17" 72 | }, 73 | "dependencies": { 74 | "debug": { 75 | "version": "2.6.9", 76 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 77 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 78 | "requires": { 79 | "ms": "2.0.0" 80 | } 81 | }, 82 | "http-errors": { 83 | "version": "1.7.2", 84 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 85 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 86 | "requires": { 87 | "depd": "~1.1.2", 88 | "inherits": "2.0.3", 89 | "setprototypeof": "1.1.1", 90 | "statuses": ">= 1.5.0 < 2", 91 | "toidentifier": "1.0.0" 92 | } 93 | }, 94 | "setprototypeof": { 95 | "version": "1.1.1", 96 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 97 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 98 | }, 99 | "statuses": { 100 | "version": "1.5.0", 101 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 102 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 103 | } 104 | } 105 | }, 106 | "brace-expansion": { 107 | "version": "1.1.11", 108 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 109 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 110 | "requires": { 111 | "balanced-match": "^1.0.0", 112 | "concat-map": "0.0.1" 113 | } 114 | }, 115 | "buffer-writer": { 116 | "version": "2.0.0", 117 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 118 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" 119 | }, 120 | "bytes": { 121 | "version": "3.1.0", 122 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 123 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 124 | }, 125 | "chalk": { 126 | "version": "2.4.2", 127 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 128 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 129 | "requires": { 130 | "ansi-styles": "^3.2.1", 131 | "escape-string-regexp": "^1.0.5", 132 | "supports-color": "^5.3.0" 133 | } 134 | }, 135 | "color-convert": { 136 | "version": "1.9.3", 137 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 138 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 139 | "requires": { 140 | "color-name": "1.1.3" 141 | } 142 | }, 143 | "color-name": { 144 | "version": "1.1.3", 145 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 146 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 147 | }, 148 | "concat-map": { 149 | "version": "0.0.1", 150 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 151 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 152 | }, 153 | "content-disposition": { 154 | "version": "0.5.3", 155 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 156 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 157 | "requires": { 158 | "safe-buffer": "5.1.2" 159 | }, 160 | "dependencies": { 161 | "safe-buffer": { 162 | "version": "5.1.2", 163 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 164 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 165 | } 166 | } 167 | }, 168 | "content-type": { 169 | "version": "1.0.4", 170 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 171 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 172 | }, 173 | "cookie": { 174 | "version": "0.4.0", 175 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 176 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 177 | }, 178 | "cookie-parser": { 179 | "version": "1.4.5", 180 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", 181 | "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", 182 | "requires": { 183 | "cookie": "0.4.0", 184 | "cookie-signature": "1.0.6" 185 | }, 186 | "dependencies": { 187 | "cookie": { 188 | "version": "0.4.0", 189 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 190 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 191 | } 192 | } 193 | }, 194 | "cookie-signature": { 195 | "version": "1.0.6", 196 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 197 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 198 | }, 199 | "debug": { 200 | "version": "4.1.1", 201 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 202 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 203 | "requires": { 204 | "ms": "^2.1.1" 205 | }, 206 | "dependencies": { 207 | "ms": { 208 | "version": "2.1.2", 209 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 210 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 211 | } 212 | } 213 | }, 214 | "depd": { 215 | "version": "1.1.2", 216 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 217 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 218 | }, 219 | "destroy": { 220 | "version": "1.0.4", 221 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 222 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 223 | }, 224 | "dottie": { 225 | "version": "2.0.2", 226 | "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", 227 | "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" 228 | }, 229 | "ee-first": { 230 | "version": "1.1.1", 231 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 232 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 233 | }, 234 | "ejs": { 235 | "version": "3.1.3", 236 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", 237 | "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", 238 | "requires": { 239 | "jake": "^10.6.1" 240 | } 241 | }, 242 | "encodeurl": { 243 | "version": "1.0.2", 244 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 245 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 246 | }, 247 | "escape-html": { 248 | "version": "1.0.3", 249 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 250 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 251 | }, 252 | "escape-string-regexp": { 253 | "version": "1.0.5", 254 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 255 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 256 | }, 257 | "etag": { 258 | "version": "1.8.1", 259 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 260 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 261 | }, 262 | "express": { 263 | "version": "4.17.1", 264 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 265 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 266 | "requires": { 267 | "accepts": "~1.3.7", 268 | "array-flatten": "1.1.1", 269 | "body-parser": "1.19.0", 270 | "content-disposition": "0.5.3", 271 | "content-type": "~1.0.4", 272 | "cookie": "0.4.0", 273 | "cookie-signature": "1.0.6", 274 | "debug": "2.6.9", 275 | "depd": "~1.1.2", 276 | "encodeurl": "~1.0.2", 277 | "escape-html": "~1.0.3", 278 | "etag": "~1.8.1", 279 | "finalhandler": "~1.1.2", 280 | "fresh": "0.5.2", 281 | "merge-descriptors": "1.0.1", 282 | "methods": "~1.1.2", 283 | "on-finished": "~2.3.0", 284 | "parseurl": "~1.3.3", 285 | "path-to-regexp": "0.1.7", 286 | "proxy-addr": "~2.0.5", 287 | "qs": "6.7.0", 288 | "range-parser": "~1.2.1", 289 | "safe-buffer": "5.1.2", 290 | "send": "0.17.1", 291 | "serve-static": "1.14.1", 292 | "setprototypeof": "1.1.1", 293 | "statuses": "~1.5.0", 294 | "type-is": "~1.6.18", 295 | "utils-merge": "1.0.1", 296 | "vary": "~1.1.2" 297 | }, 298 | "dependencies": { 299 | "debug": { 300 | "version": "2.6.9", 301 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 302 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 303 | "requires": { 304 | "ms": "2.0.0" 305 | } 306 | }, 307 | "safe-buffer": { 308 | "version": "5.1.2", 309 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 310 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 311 | }, 312 | "setprototypeof": { 313 | "version": "1.1.1", 314 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 315 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 316 | }, 317 | "statuses": { 318 | "version": "1.5.0", 319 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 320 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 321 | } 322 | } 323 | }, 324 | "filelist": { 325 | "version": "1.0.1", 326 | "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", 327 | "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", 328 | "requires": { 329 | "minimatch": "^3.0.4" 330 | } 331 | }, 332 | "finalhandler": { 333 | "version": "1.1.2", 334 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 335 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 336 | "requires": { 337 | "debug": "2.6.9", 338 | "encodeurl": "~1.0.2", 339 | "escape-html": "~1.0.3", 340 | "on-finished": "~2.3.0", 341 | "parseurl": "~1.3.3", 342 | "statuses": "~1.5.0", 343 | "unpipe": "~1.0.0" 344 | }, 345 | "dependencies": { 346 | "debug": { 347 | "version": "2.6.9", 348 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 349 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 350 | "requires": { 351 | "ms": "2.0.0" 352 | } 353 | }, 354 | "statuses": { 355 | "version": "1.5.0", 356 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 357 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 358 | } 359 | } 360 | }, 361 | "forwarded": { 362 | "version": "0.1.2", 363 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 364 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 365 | }, 366 | "fresh": { 367 | "version": "0.5.2", 368 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 369 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 370 | }, 371 | "has-flag": { 372 | "version": "3.0.0", 373 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 374 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 375 | }, 376 | "http-errors": { 377 | "version": "1.8.0", 378 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", 379 | "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", 380 | "requires": { 381 | "depd": "~1.1.2", 382 | "inherits": "2.0.4", 383 | "setprototypeof": "1.2.0", 384 | "statuses": ">= 1.5.0 < 2", 385 | "toidentifier": "1.0.0" 386 | }, 387 | "dependencies": { 388 | "inherits": { 389 | "version": "2.0.4", 390 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 391 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 392 | } 393 | } 394 | }, 395 | "iconv-lite": { 396 | "version": "0.4.24", 397 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 398 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 399 | "requires": { 400 | "safer-buffer": ">= 2.1.2 < 3" 401 | } 402 | }, 403 | "inflection": { 404 | "version": "1.12.0", 405 | "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", 406 | "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" 407 | }, 408 | "inherits": { 409 | "version": "2.0.3", 410 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 411 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 412 | }, 413 | "ipaddr.js": { 414 | "version": "1.9.1", 415 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 416 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 417 | }, 418 | "jake": { 419 | "version": "10.8.2", 420 | "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", 421 | "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", 422 | "requires": { 423 | "async": "0.9.x", 424 | "chalk": "^2.4.2", 425 | "filelist": "^1.0.1", 426 | "minimatch": "^3.0.4" 427 | } 428 | }, 429 | "lodash": { 430 | "version": "4.17.19", 431 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 432 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" 433 | }, 434 | "media-typer": { 435 | "version": "0.3.0", 436 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 437 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 438 | }, 439 | "merge-descriptors": { 440 | "version": "1.0.1", 441 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 442 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 443 | }, 444 | "methods": { 445 | "version": "1.1.2", 446 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 447 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 448 | }, 449 | "mime": { 450 | "version": "1.6.0", 451 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 452 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 453 | }, 454 | "mime-db": { 455 | "version": "1.44.0", 456 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 457 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 458 | }, 459 | "mime-types": { 460 | "version": "2.1.27", 461 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 462 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 463 | "requires": { 464 | "mime-db": "1.44.0" 465 | } 466 | }, 467 | "minimatch": { 468 | "version": "3.0.4", 469 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 470 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 471 | "requires": { 472 | "brace-expansion": "^1.1.7" 473 | } 474 | }, 475 | "moment": { 476 | "version": "2.27.0", 477 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", 478 | "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" 479 | }, 480 | "moment-timezone": { 481 | "version": "0.5.31", 482 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", 483 | "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", 484 | "requires": { 485 | "moment": ">= 2.9.0" 486 | } 487 | }, 488 | "morgan": { 489 | "version": "1.10.0", 490 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", 491 | "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", 492 | "requires": { 493 | "basic-auth": "~2.0.1", 494 | "debug": "2.6.9", 495 | "depd": "~2.0.0", 496 | "on-finished": "~2.3.0", 497 | "on-headers": "~1.0.2" 498 | }, 499 | "dependencies": { 500 | "debug": { 501 | "version": "2.6.9", 502 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 503 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 504 | "requires": { 505 | "ms": "2.0.0" 506 | } 507 | }, 508 | "depd": { 509 | "version": "2.0.0", 510 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 511 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 512 | } 513 | } 514 | }, 515 | "ms": { 516 | "version": "2.0.0", 517 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 518 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 519 | }, 520 | "negotiator": { 521 | "version": "0.6.2", 522 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 523 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 524 | }, 525 | "on-finished": { 526 | "version": "2.3.0", 527 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 528 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 529 | "requires": { 530 | "ee-first": "1.1.1" 531 | } 532 | }, 533 | "on-headers": { 534 | "version": "1.0.2", 535 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 536 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 537 | }, 538 | "packet-reader": { 539 | "version": "1.0.0", 540 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", 541 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" 542 | }, 543 | "parseurl": { 544 | "version": "1.3.3", 545 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 546 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 547 | }, 548 | "path-to-regexp": { 549 | "version": "0.1.7", 550 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 551 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 552 | }, 553 | "pg": { 554 | "version": "8.2.1", 555 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.2.1.tgz", 556 | "integrity": "sha512-DKzffhpkWRr9jx7vKxA+ur79KG+SKw+PdjMb1IRhMiKI9zqYUGczwFprqy+5Veh/DCcFs1Y6V8lRLN5I1DlleQ==", 557 | "requires": { 558 | "buffer-writer": "2.0.0", 559 | "packet-reader": "1.0.0", 560 | "pg-connection-string": "^2.2.3", 561 | "pg-pool": "^3.2.1", 562 | "pg-protocol": "^1.2.4", 563 | "pg-types": "^2.1.0", 564 | "pgpass": "1.x", 565 | "semver": "4.3.2" 566 | }, 567 | "dependencies": { 568 | "semver": { 569 | "version": "4.3.2", 570 | "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", 571 | "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" 572 | } 573 | } 574 | }, 575 | "pg-connection-string": { 576 | "version": "2.2.3", 577 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.3.tgz", 578 | "integrity": "sha512-I/KCSQGmOrZx6sMHXkOs2MjddrYcqpza3Dtsy0AjIgBr/bZiPJRK9WhABXN1Uy1UDazRbi9gZEzO2sAhL5EqiQ==" 579 | }, 580 | "pg-hstore": { 581 | "version": "2.3.3", 582 | "resolved": "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.3.tgz", 583 | "integrity": "sha512-qpeTpdkguFgfdoidtfeTho1Q1zPVPbtMHgs8eQ+Aan05iLmIs3Z3oo5DOZRclPGoQ4i68I1kCtQSJSa7i0ZVYg==", 584 | "requires": { 585 | "underscore": "^1.7.0" 586 | } 587 | }, 588 | "pg-int8": { 589 | "version": "1.0.1", 590 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 591 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" 592 | }, 593 | "pg-pool": { 594 | "version": "3.2.1", 595 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz", 596 | "integrity": "sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA==" 597 | }, 598 | "pg-protocol": { 599 | "version": "1.2.4", 600 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.4.tgz", 601 | "integrity": "sha512-/8L/G+vW/VhWjTGXpGh8XVkXOFx1ZDY+Yuz//Ab8CfjInzFkreI+fDG3WjCeSra7fIZwAFxzbGptNbm8xSXenw==" 602 | }, 603 | "pg-types": { 604 | "version": "2.2.0", 605 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 606 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 607 | "requires": { 608 | "pg-int8": "1.0.1", 609 | "postgres-array": "~2.0.0", 610 | "postgres-bytea": "~1.0.0", 611 | "postgres-date": "~1.0.4", 612 | "postgres-interval": "^1.1.0" 613 | } 614 | }, 615 | "pgpass": { 616 | "version": "1.0.2", 617 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", 618 | "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", 619 | "requires": { 620 | "split": "^1.0.0" 621 | } 622 | }, 623 | "postgres-array": { 624 | "version": "2.0.0", 625 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 626 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" 627 | }, 628 | "postgres-bytea": { 629 | "version": "1.0.0", 630 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 631 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" 632 | }, 633 | "postgres-date": { 634 | "version": "1.0.5", 635 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.5.tgz", 636 | "integrity": "sha512-pdau6GRPERdAYUQwkBnGKxEfPyhVZXG/JiS44iZWiNdSOWE09N2lUgN6yshuq6fVSon4Pm0VMXd1srUUkLe9iA==" 637 | }, 638 | "postgres-interval": { 639 | "version": "1.2.0", 640 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 641 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 642 | "requires": { 643 | "xtend": "^4.0.0" 644 | } 645 | }, 646 | "proxy-addr": { 647 | "version": "2.0.6", 648 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 649 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 650 | "requires": { 651 | "forwarded": "~0.1.2", 652 | "ipaddr.js": "1.9.1" 653 | } 654 | }, 655 | "qs": { 656 | "version": "6.7.0", 657 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 658 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 659 | }, 660 | "range-parser": { 661 | "version": "1.2.1", 662 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 663 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 664 | }, 665 | "raw-body": { 666 | "version": "2.4.0", 667 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 668 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 669 | "requires": { 670 | "bytes": "3.1.0", 671 | "http-errors": "1.7.2", 672 | "iconv-lite": "0.4.24", 673 | "unpipe": "1.0.0" 674 | }, 675 | "dependencies": { 676 | "http-errors": { 677 | "version": "1.7.2", 678 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 679 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 680 | "requires": { 681 | "depd": "~1.1.2", 682 | "inherits": "2.0.3", 683 | "setprototypeof": "1.1.1", 684 | "statuses": ">= 1.5.0 < 2", 685 | "toidentifier": "1.0.0" 686 | } 687 | }, 688 | "setprototypeof": { 689 | "version": "1.1.1", 690 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 691 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 692 | }, 693 | "statuses": { 694 | "version": "1.5.0", 695 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 696 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 697 | } 698 | } 699 | }, 700 | "retry-as-promised": { 701 | "version": "3.2.0", 702 | "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", 703 | "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==", 704 | "requires": { 705 | "any-promise": "^1.3.0" 706 | } 707 | }, 708 | "safe-buffer": { 709 | "version": "5.1.2", 710 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 711 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 712 | }, 713 | "safer-buffer": { 714 | "version": "2.1.2", 715 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 716 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 717 | }, 718 | "semver": { 719 | "version": "7.3.2", 720 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 721 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" 722 | }, 723 | "send": { 724 | "version": "0.17.1", 725 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 726 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 727 | "requires": { 728 | "debug": "2.6.9", 729 | "depd": "~1.1.2", 730 | "destroy": "~1.0.4", 731 | "encodeurl": "~1.0.2", 732 | "escape-html": "~1.0.3", 733 | "etag": "~1.8.1", 734 | "fresh": "0.5.2", 735 | "http-errors": "~1.7.2", 736 | "mime": "1.6.0", 737 | "ms": "2.1.1", 738 | "on-finished": "~2.3.0", 739 | "range-parser": "~1.2.1", 740 | "statuses": "~1.5.0" 741 | }, 742 | "dependencies": { 743 | "debug": { 744 | "version": "2.6.9", 745 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 746 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 747 | "requires": { 748 | "ms": "2.0.0" 749 | }, 750 | "dependencies": { 751 | "ms": { 752 | "version": "2.0.0", 753 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 754 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 755 | } 756 | } 757 | }, 758 | "http-errors": { 759 | "version": "1.7.3", 760 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", 761 | "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", 762 | "requires": { 763 | "depd": "~1.1.2", 764 | "inherits": "2.0.4", 765 | "setprototypeof": "1.1.1", 766 | "statuses": ">= 1.5.0 < 2", 767 | "toidentifier": "1.0.0" 768 | } 769 | }, 770 | "inherits": { 771 | "version": "2.0.4", 772 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 773 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 774 | }, 775 | "ms": { 776 | "version": "2.1.1", 777 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 778 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 779 | }, 780 | "setprototypeof": { 781 | "version": "1.1.1", 782 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 783 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 784 | }, 785 | "statuses": { 786 | "version": "1.5.0", 787 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 788 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 789 | } 790 | } 791 | }, 792 | "sequelize": { 793 | "version": "6.2.4", 794 | "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.2.4.tgz", 795 | "integrity": "sha512-pgb3CC2Dk3kjZ4knuonF6ixpdBxJwPyjmE8metIJ3D6LovAQK+5QG/DesA43OPsWhgo2TYPZst8YsBk/RE1ZyA==", 796 | "requires": { 797 | "debug": "^4.1.1", 798 | "dottie": "^2.0.0", 799 | "inflection": "1.12.0", 800 | "lodash": "^4.17.15", 801 | "moment": "^2.26.0", 802 | "moment-timezone": "^0.5.31", 803 | "retry-as-promised": "^3.2.0", 804 | "semver": "^7.3.2", 805 | "sequelize-pool": "^6.0.0", 806 | "toposort-class": "^1.0.1", 807 | "uuid": "^8.1.0", 808 | "validator": "^10.11.0", 809 | "wkx": "^0.5.0" 810 | } 811 | }, 812 | "sequelize-pool": { 813 | "version": "6.0.0", 814 | "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.0.0.tgz", 815 | "integrity": "sha512-D/VfOX2Z+6JTWqM73lhcqMXp1X4CeqRNVMlndvbOMtyjFAZ2kYzH7rGFGFrLO1r+RZQdc/h+3zQL4nd3cclNLg==" 816 | }, 817 | "serve-static": { 818 | "version": "1.14.1", 819 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 820 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 821 | "requires": { 822 | "encodeurl": "~1.0.2", 823 | "escape-html": "~1.0.3", 824 | "parseurl": "~1.3.3", 825 | "send": "0.17.1" 826 | } 827 | }, 828 | "setprototypeof": { 829 | "version": "1.2.0", 830 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 831 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 832 | }, 833 | "split": { 834 | "version": "1.0.1", 835 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 836 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 837 | "requires": { 838 | "through": "2" 839 | } 840 | }, 841 | "statuses": { 842 | "version": "1.5.0", 843 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 844 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 845 | }, 846 | "supports-color": { 847 | "version": "5.5.0", 848 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 849 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 850 | "requires": { 851 | "has-flag": "^3.0.0" 852 | } 853 | }, 854 | "through": { 855 | "version": "2.3.8", 856 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 857 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 858 | }, 859 | "toidentifier": { 860 | "version": "1.0.0", 861 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 862 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 863 | }, 864 | "toposort-class": { 865 | "version": "1.0.1", 866 | "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", 867 | "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" 868 | }, 869 | "type-is": { 870 | "version": "1.6.18", 871 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 872 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 873 | "requires": { 874 | "media-typer": "0.3.0", 875 | "mime-types": "~2.1.24" 876 | } 877 | }, 878 | "underscore": { 879 | "version": "1.10.2", 880 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", 881 | "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==" 882 | }, 883 | "unpipe": { 884 | "version": "1.0.0", 885 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 886 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 887 | }, 888 | "utils-merge": { 889 | "version": "1.0.1", 890 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 891 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 892 | }, 893 | "uuid": { 894 | "version": "8.2.0", 895 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", 896 | "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==" 897 | }, 898 | "validator": { 899 | "version": "10.11.0", 900 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", 901 | "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" 902 | }, 903 | "vary": { 904 | "version": "1.1.2", 905 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 906 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 907 | }, 908 | "wkx": { 909 | "version": "0.5.0", 910 | "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", 911 | "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", 912 | "requires": { 913 | "@types/node": "*" 914 | } 915 | }, 916 | "xtend": { 917 | "version": "4.0.2", 918 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 919 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 920 | } 921 | } 922 | } 923 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-sequelize", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "^1.4.5", 10 | "debug": "^4.1.1", 11 | "ejs": "^3.1.3", 12 | "express": "^4.17.1", 13 | "http-errors": "^1.8.0", 14 | "morgan": "^1.10.0", 15 | "pg": "^8.2.1", 16 | "pg-hstore": "^2.3.3", 17 | "sequelize": "^6.2.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | const classroomController = require('../controllers').classroom; 5 | const studentController = require('../controllers').student; 6 | const lecturerController = require('../controllers').lecturer; 7 | const courseController = require('../controllers').course; 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | res.render('index', { title: 'Express' }); 12 | }); 13 | 14 | /* Classroom Router */ 15 | router.get('/api/classroom', classroomController.list); 16 | router.get('/api/classroom/:id', classroomController.getById); 17 | router.post('/api/classroom', classroomController.add); 18 | router.put('/api/classroom/:id', classroomController.update); 19 | router.delete('/api/classroom/:id', classroomController.delete); 20 | 21 | /* Student Router */ 22 | router.get('/api/student', studentController.list); 23 | router.get('/api/student/:id', studentController.getById); 24 | router.post('/api/student', studentController.add); 25 | router.put('/api/student/:id', studentController.update); 26 | router.delete('/api/student/:id', studentController.delete); 27 | 28 | /* Lecturer Router */ 29 | router.get('/api/lecturer', lecturerController.list); 30 | router.get('/api/lecturer/:id', lecturerController.getById); 31 | router.post('/api/lecturer', lecturerController.add); 32 | router.put('/api/lecturer/:id', lecturerController.update); 33 | router.delete('/api/lecturer/:id', lecturerController.delete); 34 | 35 | /* Course Router */ 36 | router.get('/api/course', courseController.list); 37 | router.get('/api/course/:id', courseController.getById); 38 | router.post('/api/course', courseController.add); 39 | router.put('/api/course/:id', courseController.update); 40 | router.delete('/api/course/:id', courseController.delete); 41 | 42 | /* Advance Router */ 43 | router.post('/api/student/add_course', studentController.addCourse); 44 | router.post('/api/classroom/add_with_students', classroomController.addWithStudents); 45 | router.post('/api/lecturer/add_with_course', lecturerController.addWithCourse); 46 | 47 | module.exports = router; 48 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |
<%= error.stack %>4 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Welcome to <%= title %>
10 | 11 | 12 | --------------------------------------------------------------------------------