diff --git a/.gitignore b/.gitignore index 287d7f2735eaad5f58778131a4b1be47fac703f0..14e71094f35ce86e78e4414421a7607bea923540 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ +/built +/routes/cert /node_modules -sp-account-metadata.xml -.idea diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d65017186a6f34b98e10cb9cc077ac8940cb3ec..d11d9bcd8890c2e0f975366101dbb4416ed56419 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,13 +1,21 @@ deploy-testing: stage: deploy script: - - cat $configfiledev > ./config/config.js - - cat $cert > ./routes/cert/cert.pem - - cat $certidp > ./routes/cert/cert_idp.pem - - cat $key > ./routes/cert/key.pem - npm install + - npm run clean + - npm run build + - rm -rf ./built/public/default + - rm -rf ./built/routes/cert + - rm -rf ./built/views + - cp -R ./public/default ./built/public + - cp -R ./routes/cert ./built/routes + - cp -R ./views ./built + - cat $configfiledev > ./built/config/config.js + - cat $cert > ./built/routes/cert/cert.pem + - cat $certidp > ./built/routes/cert/cert_idp.pem + - cat $key > ./built/routes/cert/key.pem - "pm2 delete --silent account || :" - - pm2 start ./app.js --name=account + - pm2 start ./built/app.js --name=account - pm2 save tags: - testing diff --git a/__tests__/gitlab.unit.test.js b/__tests__/gitlab.unit.test.ts similarity index 95% rename from __tests__/gitlab.unit.test.js rename to __tests__/gitlab.unit.test.ts index 057f020d31d6acb893da43f021e16f4833125005..c1e5ad91d5c7352f48a02c56d26092030bdf4f50 100644 --- a/__tests__/gitlab.unit.test.js +++ b/__tests__/gitlab.unit.test.ts @@ -1,4 +1,4 @@ -const gitlab = require('../functions/gitlab') +import gitlab from '../functions/gitlab' //const axios = require('axios') //jest.mock('axios') diff --git a/__tests__/method.unit.test.js b/__tests__/method.unit.test.ts similarity index 97% rename from __tests__/method.unit.test.js rename to __tests__/method.unit.test.ts index 1673830dbb0fdeeceaf33a9769c76db0bdbc6c5a..2a11d1329a00806a4aeb0aab14e1cc40fe6e6ec7 100644 --- a/__tests__/method.unit.test.js +++ b/__tests__/method.unit.test.ts @@ -1,4 +1,4 @@ -const methods = require('../functions/methods') +import methods from '../functions/methods' describe("DB methohds test", () => { @@ -49,4 +49,4 @@ describe("DB methohds test", () => { expect(user).toBeNull() }) -}) +}) \ No newline at end of file diff --git a/app.js b/app.js deleted file mode 100644 index b5aac0bc6362999b0cb402096628af41d12a362c..0000000000000000000000000000000000000000 --- a/app.js +++ /dev/null @@ -1,82 +0,0 @@ -const express = require('express'); -const http = require('http'); -const path = require('path'); -const passport = require('passport'); -const morgan = require('morgan'); -const cookieParser = require('cookie-parser'); -const bodyParser = require('body-parser'); -const session = require('express-session'); -const errorhandler = require('errorhandler'); -const flash = require('express-flash-2'); -const fileUpload = require('express-fileupload'); -const helmet = require('helmet'); -const compression = require('compression'); -const methodOverride = require('method-override'); - -var env = process.env.NODE_ENV || 'testing'; -const config = require('./config/config')[env]; -const lang = 'DE'; - -var app = express(); - -app.set('port', config.app.port); -app.set('views', __dirname + '/views'); -app.set('view engine', 'pug'); - -// enable files upload -app.use(fileUpload({ - createParentPath: true, - limits: { - fileSize: 1000000 // 1 MB max. file size - } -})); - -app.use(methodOverride('_method')); -app.use(helmet()); -app.use(compression()); -app.use(morgan('combined')); -app.use(cookieParser()); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: false})); -app.use(express.static(path.join(__dirname, 'public'))); -app.use((req, res, next) => { - next(); -}); - -app.use(session( - { - resave: true, - saveUninitialized: true, - secret: config.app.sessionSecret - } -)); -app.use(flash()); -app.use(passport.initialize()); -app.use(passport.session()); - -// caching disabled for every route -// NOTE: Works in Firefox and Opera. Does not work in Edge -app.use(function(req, res, next) { - res.set('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'); - next(); -}); - -require('./routes/account')(app, config, passport, lang); -require('./routes/public')(app, config, lang); - -// Handle 404 -app.use(function (req, res) { - res.status(404).render(lang+'/404') -}) - -// Handle 500 - any server error -app.use(function (err, req, res, next) { - console.error(err.stack) - res.status(500).render(lang+'/500', { - error: err - }) -}) - -app.listen(app.get('port'), function () { - console.log('Express server listening on port ' + app.get('port')); -}); \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..89a8a359589fd6220aeab4f5ec4e14e0c742df46 --- /dev/null +++ b/app.ts @@ -0,0 +1,93 @@ +import express from 'express'; +import path from 'path'; +import passport from 'passport'; +import morgan from 'morgan'; +import cookieParser from 'cookie-parser'; +import bodyParser from 'body-parser'; +import session from 'express-session'; +import flash from 'express-flash-2'; +import fileUpload from 'express-fileupload'; +import helmet from 'helmet'; +import compression from 'compression'; +import methodOverride from 'method-override'; +import dotenv from 'dotenv' + +dotenv.config(); + +var env = process.env.NODE_ENV || 'testing'; +const config = require('./config/config')[env]; +const lang = 'DE'; + +var app = express(); +app.set('port', config.app.port); +app.set('views', path.join( __dirname + '/views')); +app.set('view engine', 'pug'); + +// enable files upload +app.use(fileUpload({ + createParentPath: true, + limits: { + fileSize: 1000000 // 1 MB max. file size + } +})); +app.use(methodOverride('_method')); +app.use( + helmet.contentSecurityPolicy({ + useDefaults: true, + directives: { + "font-src": ["'self'", "https://use.fontawesome.com"], + "img-src": ["'self'", "https://transfer.hft-stuttgart.de"], + "script-src": ["'self'", "https://code.jquery.com/jquery-3.3.1.min.js", "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js", + "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js", "https://unpkg.com/bootstrap-show-password@1.2.1/dist/bootstrap-show-password.min.js"], + "style-src": ["'self'", "https://use.fontawesome.com/releases/v5.8.2/css/all.css"], + "frame-src": ["'self'"] + }, + reportOnly: true, + }) +); + +app.use(compression()); +app.use(morgan('combined')); +app.use(cookieParser(config.app.sessionSecret)); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({extended: false})); +app.use(express.static(path.join(__dirname, 'public'))); +app.use((req, res, next) => { + next(); +}); + +app.use(session({ + resave: true, + saveUninitialized: true, + secret: config.app.sessionSecret +})); +app.use(flash()); +app.use(passport.initialize()); +app.use(passport.session()); + +// caching disabled for every route +// NOTE: Works in Firefox and Opera. Does not work in Edge +app.use(function(req, res, next) { + res.set('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'); + next(); +}); + +require('./routes/public')(app, config, lang); +require('./routes/account')(app, config, passport, lang); + +// Handle 404 +app.use(function (req:any, res:any) { + res.status(404).render(lang+'/404') +}) + +// Handle 500 - any server error +app.use(function (err:any, req:any, res:any, next:any) { + console.error(err.stack) + res.status(500).render(lang+'/500', { + error: err + }) +}) + +app.listen(app.get('port'), function () { + console.log('Express server listening on port ' + app.get('port')); +}); \ No newline at end of file diff --git a/classes/project.js b/classes/project.ts similarity index 63% rename from classes/project.js rename to classes/project.ts index 85dafab3830e16ea3db1c50802a4a3a38e2988ed..045e86a73cad5333ba58bf85f9be8aa19fa619ea 100644 --- a/classes/project.js +++ b/classes/project.ts @@ -1,9 +1,16 @@ class Project { - constructor(ownerGitlabId, id, name, desc, logo, path) { + ownerGitlabId:number + name:string + desc:string + id?:number + logo?:string + path?:string + + constructor(ownerGitlabId:number, name:string, desc:string, id?:number, logo?:string, path?:string) { this.ownerGitlabId = ownerGitlabId - this.id = id this.name = name this.desc = desc + this.id = id this.logo = logo this.path = path } @@ -28,24 +35,24 @@ class Project { return this.path } // setter - setOwnerGitlabId(newOwnerGitlabId){ + setOwnerGitlabId(newOwnerGitlabId:number){ this.ownerGitlabId = newOwnerGitlabId } - setId(newId) { + setId(newId:number) { this.id = newId } - setName(newName) { + setName(newName:string) { this.name = newName } - setDesc(newDesc) { + setDesc(newDesc:string) { this.desc = newDesc } - setLogo(newLogoUrl) { + setLogo(newLogoUrl:string) { this.logo = newLogoUrl } - setPath(newPath) { + setPath(newPath:string) { this.path = newPath } } -module.exports = Project \ No newline at end of file +export = Project \ No newline at end of file diff --git a/classes/repo.js b/classes/repo.js deleted file mode 100644 index c99ac66f6e4ada393426c40df94c2a25335d96d1..0000000000000000000000000000000000000000 --- a/classes/repo.js +++ /dev/null @@ -1,9 +0,0 @@ -const Project = require("./project"); - -class Repo extends Project { - constructor(ownerGitlabId, id, name, desc, logo, path) { - super(ownerGitlabId, id, name, desc, logo, path) - } -} - -module.exports = Repo \ No newline at end of file diff --git a/classes/repo.ts b/classes/repo.ts new file mode 100644 index 0000000000000000000000000000000000000000..b41a8fe9ad343c385383d8ede7f1f82a57f6162f --- /dev/null +++ b/classes/repo.ts @@ -0,0 +1,9 @@ +import Project from "./project" + +class Repo extends Project { + constructor(ownerGitlabId:number, name:string, desc:string, id?:number, logo?:string, path?:string) { + super(ownerGitlabId, name, desc, id, logo, path) + } +} + +export = Repo \ No newline at end of file diff --git a/classes/user.js b/classes/user.js deleted file mode 100644 index 94f07edd3f8c382b22504f0e356f84506011d662..0000000000000000000000000000000000000000 --- a/classes/user.js +++ /dev/null @@ -1,83 +0,0 @@ -class User { - constructor(id, email, salutation, title, firstName, lastName, industry, organisation, speciality, is_m4lab_idp, gitlabUserId, verificationStatus) { - this.id = id - this.email = email - this.salutation = salutation - this.title = title - this.firstName = firstName - this.lastName = lastName - this.industry = industry - this.organisation = organisation - this.speciality = speciality - this.is_m4lab_idp = is_m4lab_idp // 1 or 0 - this.gitlabUserId = gitlabUserId - this.verificationStatus = verificationStatus - } - - // getter - getId() { - return this.id - } - getEmail() { - return this.email - } - getFullName() { - return this.firstName+' '+this.lastName - } - getIdpStatus() { - return this.is_m4lab_idp - } - getGitlabUserId() { - return this.gitlabUserId - } - getVerificationStatus() { - return this.verificationStatus - } - // setter - setEmail(email) { - this.email = email - } - setSalutation(salutation) { - this.salutation = salutation - } - setTitle(title) { - this.title = title - } - setFirstName(firstName) { - this.firstName = firstName - } - setLastName(lastName) { - this.lastName = lastName - } - setIndustry(industry) { - this.industry = industry - } - setOrganisation(organisation) { - this.organisation = organisation - } - setSpeciality(speciality) { - this.speciality = speciality - } - setM4lab_idp(m4lab_idp) { - this.m4lab_idp = m4lab_idp - } - setGitlabUserId(newGitlabUserId) { - this.gitlabUserId = newGitlabUserId - } - setVerificationStatus(verificationStatus) { - this.verificationStatus = verificationStatus - } - - updateProfile(newSalutation, newTitle, newFirstname, newLastname, newEmail, newOrganisation, newIndustry, newSpeciality) { - this.salutation = newSalutation - this.title = newTitle - this.firstName = newFirstname - this.lastName = newLastname - this.email = newEmail - this.organisation = newOrganisation - this.industry = newIndustry - this.speciality = newSpeciality - } -} - -module.exports = User \ No newline at end of file diff --git a/classes/user.ts b/classes/user.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b26a694b4a95dd98109f976189561ff8a08a31d --- /dev/null +++ b/classes/user.ts @@ -0,0 +1,97 @@ +class User { + id:number + email:string + salutation:string // should be enum + title:string // should be enum + firstName:string + lastName:string + industry:string + organisation:string + speciality:string + is_m4lab_idp:number // 1 or 0 + verificationStatus:number // 1 or 0 - // should be boolean + gitlabUserId?:number + + constructor(id:number, email:string, salutation:string, title:string, firstName:string, lastName:string, industry:string, organisation:string, + speciality:string, is_m4lab_idp:number, verificationStatus:number, gitlabUserId?:number) { + this.id = id + this.email = email + this.salutation = salutation + this.title = title + this.firstName = firstName + this.lastName = lastName + this.industry = industry + this.organisation = organisation + this.speciality = speciality + this.is_m4lab_idp = is_m4lab_idp + this.verificationStatus = verificationStatus + this.gitlabUserId = gitlabUserId + } + + // getter + getId() { + return this.id + } + getEmail() { + return this.email + } + getFullName() { + return this.firstName+' '+this.lastName + } + getIdpStatus() { + return this.is_m4lab_idp + } + getVerificationStatus() { + return this.verificationStatus + } + getGitlabUserId() { + return this.gitlabUserId + } + // setter + setEmail(email:string) { + this.email = email + } + setSalutation(salutation:string) { + this.salutation = salutation + } + setTitle(title:string) { + this.title = title + } + setFirstName(firstName:string) { + this.firstName = firstName + } + setLastName(lastName:string) { + this.lastName = lastName + } + setIndustry(industry:string) { + this.industry = industry + } + setOrganisation(organisation:string) { + this.organisation = organisation + } + setSpeciality(speciality:string) { + this.speciality = speciality + } + setM4lab_idp(m4lab_idp:number) { + this.is_m4lab_idp = m4lab_idp + } + setVerificationStatus(verificationStatus:number) { + this.verificationStatus = verificationStatus + } + setGitlabUserId(newGitlabUserId:number) { + this.gitlabUserId = newGitlabUserId + } + + updateProfile(newSalutation:string, newTitle:string, newFirstname:string, newLastname:string, newEmail:string, newOrganisation:string, newIndustry:string, newSpeciality:string) { + this.salutation = newSalutation + this.title = newTitle + this.firstName = newFirstname + this.lastName = newLastname + this.email = newEmail + this.organisation = newOrganisation + this.industry = newIndustry + this.speciality = newSpeciality + } +} + +export = User \ No newline at end of file diff --git a/classes/website.js b/classes/website.js deleted file mode 100644 index 1d05c40059221e15d8fa04fc43abee51d7e2190f..0000000000000000000000000000000000000000 --- a/classes/website.js +++ /dev/null @@ -1,9 +0,0 @@ -const Project = require("./project"); - -class Website extends Project { - constructor(ownerGitlabId, id, name, desc, logo, path) { - super(ownerGitlabId, id, name, desc, logo, path) - } -} - -module.exports = Website \ No newline at end of file diff --git a/classes/website.ts b/classes/website.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd64140dde3774db0aeaba750bd07691ac348059 --- /dev/null +++ b/classes/website.ts @@ -0,0 +1,9 @@ +import Project from "./project" + +class Website extends Project { + constructor(ownerGitlabId:number, name:string, desc:string, id?:number, logo?:string, path?:string) { + super(ownerGitlabId, name, desc, id, logo, path) + } +} + +export = Website \ No newline at end of file diff --git a/config/config.js b/config/config.ts similarity index 94% rename from config/config.js rename to config/config.ts index e4bb457ce3bd4b6f30a784e4ec7d2dc685fba4b4..47558a5d53d2a00b17098074510779b5bb44cb05 100644 --- a/config/config.js +++ b/config/config.ts @@ -1,4 +1,4 @@ -module.exports = { +export = { development: { app: { name: 'User Account Management', @@ -28,7 +28,7 @@ module.exports = { host: 'mailhost', // hostname secureConnection: false, // TLS requires secureConnection to be false port: 587, // port for secure SMTP - TLS: true, // sets requireTLS + TLS: true, authUser: 'mailuser', authPass: 'mailpass', tlsCiphers: 'SSLv3', @@ -67,7 +67,7 @@ module.exports = { host: 'mailhost', // hostname secureConnection: false, // TLS requires secureConnection to be false port: 587, // port for secure SMTP - TLS: true, // sets requireTLS + TLS: true, authUser: 'mailuser', authPass: 'mailpass', tlsCiphers: 'SSLv3', @@ -77,4 +77,4 @@ module.exports = { token_readWriteProjects: 'token-goes-here' } } -} +} \ No newline at end of file diff --git a/config/const.js b/config/const.ts similarity index 98% rename from config/const.js rename to config/const.ts index dcbb821a4bf191c8a5ddd0dd10e9e0abab0a1c56..302d017701af834d7cf3f73f3b66c2e0a0d2f584 100644 --- a/config/const.js +++ b/config/const.ts @@ -1,4 +1,4 @@ -module.exports = { +export = { mailSignature: 'Mit den besten Grüßen,
das Transferportal-Team der HFT Stuttgart

' + 'Transferportal der Hochschule für Technik Stuttgart
' + diff --git a/config/dbconn.js b/config/dbconn.ts similarity index 91% rename from config/dbconn.js rename to config/dbconn.ts index 670032958e099606b5558a39b94cc6492eb155e4..d918ce7c2e911d3a997c92df8d313f334f8024ca 100644 --- a/config/dbconn.js +++ b/config/dbconn.ts @@ -1,6 +1,6 @@ -const mysql = require('mysql') +import mysql from 'mysql' -var env = process.env.NODE_ENV || 'testing'; +var env = process.env.NODE_ENV || 'testing' const config = require('./config')[env] // ==== USER ACOOUNT DB CONNECTION ==== @@ -14,7 +14,7 @@ var userConnection = mysql.createConnection({ }) userConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) userConnection.query('USE '+config.database.dbUser) @@ -52,7 +52,7 @@ var projectConnection = mysql.createConnection({ }) projectConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) projectConnection.query('USE '+config.database.dbProject) @@ -61,4 +61,4 @@ var connection = { project: projectConnection } -module.exports = connection \ No newline at end of file +export = connection \ No newline at end of file diff --git a/config/dbconn2.js b/config/dbconn2.ts similarity index 91% rename from config/dbconn2.js rename to config/dbconn2.ts index abbd0a012666e0d1f86b03512759cd438cd2d8fe..c13c40f11c2c3994791d75c866c5cfc6d210e1d7 100644 --- a/config/dbconn2.js +++ b/config/dbconn2.ts @@ -1,6 +1,6 @@ -const mysql = require('mysql2') +import mysql from 'mysql2' -var env = process.env.NODE_ENV || 'testing'; +var env = process.env.NODE_ENV || 'testing' const config = require('./config')[env] // ==== USER ACOOUNT DB CONNECTION ==== @@ -14,7 +14,7 @@ var userConnection = mysql.createConnection({ }) userConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) userConnection.query('USE '+config.database.dbUser) @@ -52,7 +52,7 @@ var projectConnection = mysql.createConnection({ }) projectConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) projectConnection.query('USE '+config.database.dbProject) @@ -61,4 +61,4 @@ var connection = { project: projectConnection } -module.exports = connection \ No newline at end of file +export = connection \ No newline at end of file diff --git a/config/mailer.js b/config/mailer.js deleted file mode 100644 index d7c6faf0983428c4e15944cf5d85fa68ee5c158f..0000000000000000000000000000000000000000 --- a/config/mailer.js +++ /dev/null @@ -1,39 +0,0 @@ -const nodemailer = require('nodemailer'); -const nodemailerNTLMAuth = require('nodemailer-ntlm-auth'); - - -var env = process.env.NODE_ENV || 'testing'; -const config = require('./config')[env] - -var smtpTransport = nodemailer.createTransport({ - host: config.mailer.host, - secure: config.mailer.secureConnection, - port: config.mailer.port, - requireTLS: config.mailer.TLS, - auth: { - type: 'custom', - method: 'NTLM', - user: config.mailer.authUser, - pass: config.mailer.authPass, - options: { - domain: 'ad' - } - }, - customAuth:{ - NTLM: nodemailerNTLMAuth - } -}); - -var mailOptions = { - to: "", - from: config.mailer.from, - subject: "", - text: "" -}; - -var mailer = { - transport: smtpTransport, - options: mailOptions -} - -module.exports = mailer diff --git a/config/mailer.ts b/config/mailer.ts new file mode 100644 index 0000000000000000000000000000000000000000..18f25191d2a4abb7e30f6810404e0968a1660a4c --- /dev/null +++ b/config/mailer.ts @@ -0,0 +1,40 @@ +const nodemailer = require('nodemailer') +const nodemailerNTLMAuth = require('nodemailer-ntlm-auth') + +var env = process.env.NODE_ENV || 'testing' +const config = require('./config')[env] + +var smtpTransporter = nodemailer.createTransport({ + host: config.mailer.host, + secure: config.mailer.secureConnection, + port: config.mailer.port, + requireTLS: config.mailer.TLS, + auth: { + type: 'custom', + method: 'NTLM', + user: config.mailer.authUser, + pass: config.mailer.authPass, + options: { + domain: 'ad' + } + }, + customAuth:{ + NTLM: nodemailerNTLMAuth + } +}); + +var mailOptions:any = { + to: "", + cc: "", + from: config.mailer.from, + subject: "", + text: "", + html: "" +} + +var mailer:any = { + transporter: smtpTransporter, + options: mailOptions +} + +export = mailer \ No newline at end of file diff --git a/functions/gitlab.js b/functions/gitlab.ts similarity index 58% rename from functions/gitlab.js rename to functions/gitlab.ts index 62ff36508f80a0747538386a7838e4892c5a1706..5924afd12edb9abca93fef917957dada166078f4 100644 --- a/functions/gitlab.js +++ b/functions/gitlab.ts @@ -1,13 +1,12 @@ +import axios from 'axios' +import fs from 'fs' +import formData from 'form-data' + var env = process.env.NODE_ENV || 'testing' const config = require('../config/config')[env] -const axios = require('axios') -const fs = require('fs') -var formData = require('form-data') var gitlab = { - // todo: GraphQL currentUser - - getUserByEmail: async function(email) { + getUserByEmail: async function(email:string) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/users?search='+email, @@ -15,9 +14,12 @@ var gitlab = { 'Authorization': 'Bearer '+config.gitlab.token_readWriteProjects} }) .then(res => res.data[0]) - .catch(err => console.error(err)) + .catch(function(err){ + console.error(err) + return null + }) }, - createNewPages: async function(newPagesData, newLogoFile, template) { + createNewPages: async function(newPagesData:any, newLogoFile:string, template:any) { let data = new formData() data.append('avatar', fs.createReadStream(newLogoFile)) @@ -32,16 +34,15 @@ var gitlab = { }, data: data }) - .then(res => res = { - error: false, - data: res.data - }) - .catch(err => res = { - error: true, - data: err.response.data + .then(res => res.data) + .catch(function(err) { + console.error("ERR Status: "+err.response.status) + console.error("ERR Name: "+err.response.data.message.name) + console.error("ERR Path: "+err.response.data.message.path) + return err.response }) }, - updateProject: async function(updatedProjectData, newLogoFile){ + updateProject: async function(updatedProjectData:any, newLogoFile:string){ let data = new formData() if (newLogoFile) { data.append('avatar', fs.createReadStream(newLogoFile)) @@ -57,16 +58,16 @@ var gitlab = { }, data : data }) - .then(res => res = { - error: false, - data: res.data - }) - .catch(err => res = { - error: true, - data: err.response.data + //.then(res => res.data[0]) + .then(res => res.data) + .catch(function(err){ + console.error("ERR Status: "+err.response.status) + console.error("ERR Name: "+err.response.data.message.name) + console.error("ERR Path: "+err.response.data.message.path) + return err.response }) }, - deleteProjectById: function(projectId){ + deleteProjectById: function(projectId:number){ // https://docs.gitlab.com/ee/api/projects.html#delete-project return axios({ method: 'delete', @@ -75,16 +76,15 @@ var gitlab = { 'Authorization': 'Bearer '+config.gitlab.token_readWriteProjects } }) - .then(res => res = { - error: false, - data: res.data - }) - .catch(err => res = { - error: true, - data: err.response.data + .then(res => true) + .catch(function(err) { + console.error("ERR Status: "+err.response.status) + console.error("ERR Name: "+err.response.data.message.name) + console.error("ERR Path: "+err.response.data.message.path) + return false }) }, - getUserProjects: async function(gitlabUserId) { + getUserProjects: async function(gitlabUserId:number) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/users/'+gitlabUserId+'/projects?owned=true&visibility=public', @@ -93,9 +93,12 @@ var gitlab = { } }) .then(res => res.data) - .catch(err => console.error(err)) + .catch(function(err) { + console.error(err) + return null + }) }, - getProjectById: async function(projectId) { + getProjectById: async function(projectId:number) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/projects/'+projectId, @@ -104,45 +107,22 @@ var gitlab = { } }) .then(res => res.data) - .catch(err => console.error(err.response.status)) + .catch(function(err) { + console.error(err) + return null + }) }, - getProjectPipelineLatestStatus: async function(projectId) { + getProjectPipelineLatestStatus: async function(projectId:number) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/projects/'+projectId+'/pipelines' }) .then(res => res.data[0].status) - .catch(err => console.error(err)) - }, - // - // test GraphQL - getGraphqlTest: function(callback) { - axios({ - url: 'https://gitlab.com/api/graphql', - method: 'get', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer '+config.gitlab.token_readWriteProjects - }, - data: { - query: `{ - currentUser { - id - username - } - }` - /* query: `{ - projects { - nodes { - id - } - } - }` */ - } - }).then((result) => { - console.log(JSON.stringify(result.data)) - }); + .catch(function(err) { + console.error(err) + return null + }) } } -module.exports = gitlab \ No newline at end of file +export = gitlab \ No newline at end of file diff --git a/functions/helpers.js b/functions/helpers.ts similarity index 69% rename from functions/helpers.js rename to functions/helpers.ts index 4645aec75363bf4110ce33f65b8615dc60902f60..1c3451fc00883bf4307ba324dc98f75a7d76b80f 100644 --- a/functions/helpers.js +++ b/functions/helpers.ts @@ -1,5 +1,5 @@ var helpers = { - stringToArray: function (input){ + stringToArray: function (input:string){ if(input != null){ return input.split(','); }else{ @@ -8,4 +8,4 @@ var helpers = { } }; -module.exports = helpers; \ No newline at end of file +export = helpers; \ No newline at end of file diff --git a/functions/methods.js b/functions/methods.ts similarity index 56% rename from functions/methods.js rename to functions/methods.ts index 0037a7e143bab55cee0d9d48b71263818af86b5a..16143b851a743a4f5e023c4b50913dd695cbcc2b 100644 --- a/functions/methods.js +++ b/functions/methods.ts @@ -1,57 +1,55 @@ -const dbconn_OBSOLETE = require('../config/dbconn') // DO NOT USE THIS FOR NEW FUNCTIONS -const dbconn = require('../config/dbconn2') +import dbconn_OBSOLETE = require('../config/dbconn') // DO NOT USE THIS FOR NEW FUNCTIONS +import dbconn = require('../config/dbconn2') var methods = { // ===================== user db ===================== - registerNewUser: function(data, callback) { - dbconn_OBSOLETE.user.beginTransaction(function(err) { // START TRANSACTION - if (err) { - throw err - } + registerNewUser: function(data:any, callback:any) { + dbconn_OBSOLETE.user.beginTransaction(function(err:any) { // START TRANSACTION + if (err) { throw err } // insert profile - dbconn_OBSOLETE.user.query('INSERT INTO user SET ?', data.profile, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO user SET ?', data.profile, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } - var newUserId = results.insertId + let newUserId:number = results.insertId // set password - var credentialData = { + var credentialData:any = { user_id: newUserId, password: data.password } - dbconn_OBSOLETE.user.query('INSERT INTO credential SET ?', credentialData, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO credential SET ?', credentialData, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } // set default user-project-role - var projectRoleData = { + var projectRoleData:any = { project_id: 1, //M4_LAB role_id: 2, // USER user_id: newUserId } - dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', projectRoleData, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', projectRoleData, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } // MLAB-129: INSERT verification token - let verificationData = { + let verificationData:any = { user_id: newUserId, token: data.verificationToken } - dbconn_OBSOLETE.user.query('INSERT INTO verification SET ?', verificationData, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO verification SET ?', verificationData, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } // COMMIT - dbconn_OBSOLETE.user.commit(function(err) { + dbconn_OBSOLETE.user.commit(function(err:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err @@ -65,9 +63,9 @@ var methods = { callback(err) }) }, - getUserByEmail: async function(email) { + getUserByEmail: async function(email:any) { try { - let rows = await dbconn.user.promise().query('SELECT id, verificationStatus, salutation, title, firstname, lastname, industry, organisation, speciality, m4lab_idp FROM user WHERE email = "' +email+'"') + let rows:any = await dbconn.user.promise().query('SELECT id, verificationStatus, salutation, title, firstname, lastname, industry, organisation, speciality, m4lab_idp FROM user WHERE email = "' +email+'"') if (rows[0][0]) { return rows[0][0] } @@ -77,9 +75,9 @@ var methods = { } return null }, - getUserEmailById: async function(userId) { + getUserEmailById: async function(userId:number) { try { - let rows = await dbconn.user.promise().query('SELECT email FROM user WHERE id = ' +userId) + let rows:any = await dbconn.user.promise().query('SELECT email FROM user WHERE id = ' +userId) if (rows[0][0]) { return rows[0][0].email } @@ -89,9 +87,9 @@ var methods = { } return null }, - checkUserEmail: async function(email) { + checkUserEmail: async function(email:any) { try { - let rows = await dbconn.user.promise().query('SELECT id, email FROM user WHERE email = "' +email+'"') + let rows:any = await dbconn.user.promise().query('SELECT id, email FROM user WHERE email = "' +email+'"') if (rows[0][0]) { return rows[0][0] } @@ -101,9 +99,9 @@ var methods = { } return null }, - getUserByToken: async function(token) { + getUserByToken: async function(token:any) { try { - let rows = await dbconn.user.promise().query('SELECT t1.user_id, t2.email FROM userdb.credential AS t1 INNER JOIN userdb.user AS t2 ON t1.user_id = t2.id AND t1.resetPasswordToken = "' + let rows:any = await dbconn.user.promise().query('SELECT t1.user_id, t2.email FROM userdb.credential AS t1 INNER JOIN userdb.user AS t2 ON t1.user_id = t2.id AND t1.resetPasswordToken = "' +token+'" and resetPasswordExpires > '+Date.now()) if (rows[0][0]) { return rows[0][0] @@ -114,47 +112,38 @@ var methods = { } return null }, - updateUserById: function(userData, callback) { - dbconn_OBSOLETE.user.query('UPDATE user SET ? WHERE id = ' +userData.id, userData, function (err, rows, fields) { - if (err) throw err - callback(err) - }) - }, - updateCredential: function(data, callback) { - dbconn_OBSOLETE.user.query('UPDATE credential SET ? WHERE user_id = ' +data.user_id, data, function (err, rows, fields) { - if (err) throw err - callback(err) - }) + updateUserById: async function(userId:number, userData:any) { + try { + let result:any = await dbconn.user.promise().query('UPDATE user SET ? WHERE id = ' +userId, userData) + return result + } catch (err) { + console.error(err) + } + return null }, - getUserIdByEmail_OBSOLETE: function(email, callback) { - let userId - dbconn_OBSOLETE.user.query('SELECT id FROM user WHERE email = "' +email+'"', function (err, rows, fields) { - if (err) { - throw err - } - else { - if ( rows.length > 0) { - userId = rows[0].id - } - } - callback(userId, err) - }); + updateCredential: async function(data:any) { + try { + let result:any = await dbconn.user.promise().query('UPDATE credential SET ? WHERE user_id = ' +data.user_id, data) + return result + } catch (err) { + console.error(err) + } + return null }, - getUserProjectRole_OBSOLETE: function(userId, callback) { - dbconn_OBSOLETE.user.query('SELECT project_id, role_id FROM user_project_role WHERE user_id = "' +userId+'"', function (err, rows, fields) { + addUserProjectRole_OBSOLETE: function(data:any, callback:any) { + /* + dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', data, function (err:any, results:any, fields:any){ if (err) throw err - callback(rows, err) - }); - }, - addUserProjectRole: function(data, callback) { - dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', data, function (err, results, fields){ + callback(err) + }) */ + dbconn.user.query('INSERT INTO user_project_role SET ?', data, function (err:any){ if (err) throw err callback(err) }) }, - getVerificationTokenByUserId: async function(userId) { + getVerificationTokenByUserId: async function(userId:number) { try { - let rows = await dbconn.user.promise().query('SELECT token FROM verification WHERE user_id = "' +userId+'"') + let rows:any = await dbconn.user.promise().query('SELECT token FROM verification WHERE user_id = "' +userId+'"') if (rows[0][0]) { return rows[0][0].token } @@ -164,9 +153,9 @@ var methods = { } return null }, - getUserIdByVerificationToken: async function(token) { + getUserIdByVerificationToken: async function(token:any) { try { - let rows = await dbconn.user.promise().query('SELECT user_id FROM verification WHERE token = "' +token+'"') + let rows:any = await dbconn.user.promise().query('SELECT user_id FROM verification WHERE token = "' +token+'"') if (rows[0][0]) { return rows[0][0].user_id } @@ -178,21 +167,21 @@ var methods = { } return null }, - verifyUserAccount: function(userData, callback) { - dbconn_OBSOLETE.user.beginTransaction(function(err) { // START TRANSACTION + verifyUserAccount: function(userData:any, callback:any) { + dbconn_OBSOLETE.user.beginTransaction(function(err:any) { // START TRANSACTION if (err) { throw err } // update user status - dbconn_OBSOLETE.user.query('UPDATE user SET ? WHERE id =' +userData.id, userData, function (err, rows, fields) { + dbconn_OBSOLETE.user.query('UPDATE user SET ? WHERE id =' +userData.id, userData, function (err:any, rows:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }) } // delete verification token - dbconn_OBSOLETE.user.query('DELETE FROM verification WHERE user_id = '+userData.id, function (err, rows, fields) { + dbconn_OBSOLETE.user.query('DELETE FROM verification WHERE user_id = '+userData.id, function (err:any, rows:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }) } // COMMIT - dbconn_OBSOLETE.user.commit(function(err) { + dbconn_OBSOLETE.user.commit(function(err:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }) } @@ -203,9 +192,9 @@ var methods = { }) }, /* ===== GitLab ===== */ - getGitlabId: async function(userId) { + getGitlabId: async function(userId:number) { try { - let rows = await dbconn.user.promise().query('SELECT gu.gitlab_userId FROM user_gitlab gu, user u WHERE u.id = "' +userId+'" and gu.user_id = u.id') + let rows:any = await dbconn.user.promise().query('SELECT gu.gitlab_userId FROM user_gitlab gu, user u WHERE u.id = "' +userId+'" and gu.user_id = u.id') if (rows[0][0]) { return rows[0][0].gitlab_userId } else { @@ -217,12 +206,12 @@ var methods = { return err } }, - addGitlabUser: function(data, callback){ - dbconn_OBSOLETE.user.query('INSERT INTO user_gitlab SET ?', data, function (err) { + addGitlabUser: function(data:any, callback:any){ + dbconn.user.query('INSERT INTO user_gitlab SET ?', data, function (err:any) { if (err) throw err callback(err) }) } }; -module.exports = methods; \ No newline at end of file +export = methods \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2b7642f6f140ab83974872e34c32167647dc95ae..b2830afb6e961391d6d68837afc5b9caf710bbe0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,28 +15,46 @@ "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "1.4.3", - "crypto": "^1.0.1", - "errorhandler": "1.4.3", + "dotenv": "^9.0.2", "express": "^4.17.1", "express-fileupload": "^1.1.6", "express-flash-2": "^1.0.1", "express-session": "^1.17.0", "form-data": "^3.0.0", "fs": "0.0.1-security", - "helmet": "^3.23.3", - "i18n": "^0.8.5", - "jest": "^26.6.3", + "helmet": "^4.6.0", + "jest": "^26.5.0", "method-override": "^3.0.0", "morgan": "^1.9.1", "mysql": "^2.17.1", "mysql2": "^2.2.5", "nodemailer": "^6.3.1", + "nodemailer-ntlm-auth": "^1.0.1", "passport": "0.3.2", "passport-saml": "^2.1.0", "pug": "^3.0.2" }, "devDependencies": { - "nodemon": "^2.0.1" + "@types/async": "^3.2.6", + "@types/bcryptjs": "^2.4.2", + "@types/compression": "^1.7.0", + "@types/cookie-parser": "^1.4.2", + "@types/express": "^4.17.11", + "@types/express-fileupload": "^1.1.6", + "@types/express-flash-2": "^1.0.6", + "@types/express-session": "^1.17.0", + "@types/jest": "^26.0.23", + "@types/method-override": "^0.0.31", + "@types/morgan": "^1.9.2", + "@types/mysql": "^2.15.18", + "@types/node": "^15.0.2", + "@types/nodemailer": "^6.4.1", + "@types/passport": "^1.0.6", + "@types/passport-strategy": "^0.2.35", + "@types/xml2js": "^0.4.8", + "nodemon": "^2.0.1", + "ts-node": "^9.1.1", + "typescript": "^4.2.4" }, "engines": { "node": ">= 4.0.0" @@ -51,9 +69,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.4.tgz", + "integrity": "sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ==" }, "node_modules/@babel/core": { "version": "7.14.3", @@ -132,13 +150,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz", + "integrity": "sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA==", "dependencies": { - "@babel/compat-data": "^7.13.15", + "@babel/compat-data": "^7.14.4", "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", + "browserslist": "^4.16.6", "semver": "^6.3.0" }, "peerDependencies": { @@ -208,14 +226,14 @@ "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" }, "node_modules/@babel/helper-replace-supers": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", - "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz", + "integrity": "sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ==", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2" + "@babel/types": "^7.14.4" } }, "node_modules/@babel/helper-simple-access": { @@ -321,9 +339,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", - "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.4.tgz", + "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==", "bin": { "parser": "bin/babel-parser.js" }, @@ -510,9 +528,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@babel/types": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", - "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.4.tgz", + "integrity": "sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==", "dependencies": { "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" @@ -830,6 +848,12 @@ "node": ">= 6" } }, + "node_modules/@types/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.6.tgz", + "integrity": "sha512-ZkrXnZLC1mc4b9QLKaSrsxV4oxTRs10OI2kgSApT8G0v1jrmqppSHUVQ15kLorzsFBTjvf7OKF4kAibuuNQ+xA==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.1.14", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", @@ -867,6 +891,100 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/compression": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz", + "integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie-parser": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", + "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz", + "integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-fileupload": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/express-fileupload/-/express-fileupload-1.1.6.tgz", + "integrity": "sha512-8z92PCVgvWvG1TpxucRU9oRz3hZc5cUz+CkeDe4XwVmg2DJDdd/7QASMsJzIo+9Pbfp7LfTEWSeEFUJZBohv9g==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/express-flash-2": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/express-flash-2/-/express-flash-2-1.0.6.tgz", + "integrity": "sha512-8YE6SDUxIIrsAKJL65nfUYtgO356QcT889GaXrwS0em7VYgbz6ShPdLUBOMDu8ihZrqidp5Qz2bNnWCCP4rZMw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz", + "integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/express-session": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.0.tgz", + "integrity": "sha512-OQEHeBFE1UhChVIBhRh9qElHUvTp4BzKKHxMDkGHT7WuYk5eL93hPG7D8YAIkoBSbhNEY0RjreF15zn+U0eLjA==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -889,33 +1007,135 @@ } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dependencies": { "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "26.0.23", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", + "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", + "dev": true, + "dependencies": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "node_modules/@types/method-override": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@types/method-override/-/method-override-0.0.31.tgz", + "integrity": "sha512-aLA4MGzjYjBHGpr5TgAdPRyX97Jd+xlWN2wa6PbsjKYeoUKPsxVDFRSTjI3YG4MvGg3ZJkdMxjAXZfujU9qEPw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/morgan": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz", + "integrity": "sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mysql": { + "version": "2.15.18", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.18.tgz", + "integrity": "sha512-JW74Nh3P/RDAnaP8uXe1qmRpoFBO84SiWvWoSju/F5+2S1kVBi1FbbDoqK/sTZrCCxySaOJnRATvWD+bLcJjAg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.1.tgz", - "integrity": "sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA==" + "version": "15.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==" + }, + "node_modules/@types/nodemailer": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.2.tgz", + "integrity": "sha512-yhsqg5Xbr8aWdwjFS3QjkniW5/tLpWXtOYQcJdo9qE3DolBxsKzgRCQrteaMY0hos8MklJNSEsMqDpZynGzMNg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" }, + "node_modules/@types/passport": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.6.tgz", + "integrity": "sha512-9oKfrJXuAxvyxdrtMCxKkHgmd6DMO8NDOLvMJ1LvIWd6/xP+i81PAkpTaEca7VhJX9S009RciwZL/j6dsLsHrA==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", + "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, "node_modules/@types/prettier": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==" }, + "node_modules/@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==" }, + "node_modules/@types/xml2js": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.8.tgz", + "integrity": "sha512-EyvT83ezOdec7BhDaEcsklWy7RSIdi6CNe95tmOAK0yx/Lm30C9K75snT3fYayK59ApC2oyW+rcHErdG05FHJA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "15.0.13", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", @@ -953,9 +1173,9 @@ } }, "node_modules/acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.3.0.tgz", + "integrity": "sha512-tqPKHZ5CaBJw0Xmy0ZZvLs1qTV+BNFSyvn77ASXkpBNfIRk8ev26fKrD9iLGwGA9zedPao52GSHzq8lyZG0NUw==", "bin": { "acorn": "bin/acorn" }, @@ -1130,6 +1350,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1138,11 +1364,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/argparse/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -1419,11 +1640,6 @@ "node": ">= 0.8" } }, - "node_modules/bowser": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", - "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" - }, "node_modules/boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -1626,15 +1842,10 @@ "node": ">=6" } }, - "node_modules/camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, "node_modules/caniuse-lite": { - "version": "1.0.30001230", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz", - "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==", + "version": "1.0.30001235", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", + "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -1999,14 +2210,6 @@ "node": ">= 0.6" } }, - "node_modules/content-security-policy-builder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", - "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -2061,6 +2264,12 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2074,12 +2283,6 @@ "node": ">= 8" } }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." - }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -2110,11 +2313,6 @@ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, - "node_modules/dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" - }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -2316,14 +2514,6 @@ "node": ">=8" } }, - "node_modules/dont-sniff-mimetype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", - "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -2336,6 +2526,14 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "engines": { + "node": ">=10" + } + }, "node_modules/duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -2348,9 +2546,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.3.741", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.741.tgz", - "integrity": "sha512-4i3T0cwnHo1O4Mnp9JniEco8bZiXoqbm3PhW5hv7uu8YLg35iajYrRnNyKFaN8/8SSTskU2hYqVTeYVPceSpUA==" + "version": "1.3.749", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.749.tgz", + "integrity": "sha512-F+v2zxZgw/fMwPz/VUGIggG4ZndDsYy0vlpthi3tjmDZlcfbhN5mYW0evXUsBr2sUtuDANFtle410A9u/sd/4A==" }, "node_modules/emittery": { "version": "0.7.2", @@ -2392,18 +2590,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/errorhandler": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.4.3.tgz", - "integrity": "sha1-t7cO2PNZ6duICS8tIMD4MUIK2D8=", - "dependencies": { - "accepts": "~1.3.0", - "escape-html": "~1.0.3" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2856,14 +3042,6 @@ "bser": "2.1.1" } }, - "node_modules/feature-policy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", - "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2945,9 +3123,9 @@ } }, "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } @@ -3281,62 +3459,11 @@ } }, "node_modules/helmet": { - "version": "3.23.3", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz", - "integrity": "sha512-U3MeYdzPJQhtvqAVBPntVgAvNSOJyagwZwyKsFdyRa8TV3pOKVFljalPOCxbw5Wwf2kncGhmP0qHjyazIdNdSA==", - "dependencies": { - "depd": "2.0.0", - "dont-sniff-mimetype": "1.1.0", - "feature-policy": "0.3.0", - "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.10.0", - "hide-powered-by": "1.1.0", - "hpkp": "2.0.0", - "hsts": "2.2.0", - "nocache": "2.1.0", - "referrer-policy": "1.2.0", - "x-xss-protection": "1.3.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/helmet-crossdomain": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", - "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/helmet-csp": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz", - "integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==", - "dependencies": { - "bowser": "2.9.0", - "camelize": "1.0.0", - "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/helmet/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/hide-powered-by": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", - "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==", "engines": { - "node": ">=4.0.0" + "node": ">=10.0.0" } }, "node_modules/hosted-git-info": { @@ -3344,30 +3471,6 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, - "node_modules/hpkp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", - "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" - }, - "node_modules/hsts": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", - "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", - "dependencies": { - "depd": "2.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/hsts/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -3439,6 +3542,26 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/httpntlm": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.7.6.tgz", + "integrity": "sha1-aZHoNSg2AH1nEBuD247Q+RX5BtA=", + "dependencies": { + "httpreq": ">=0.4.22", + "underscore": "~1.7.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/httpreq": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz", + "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -3480,22 +3603,6 @@ "node": ">=8.12.0" } }, - "node_modules/i18n": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.8.6.tgz", - "integrity": "sha512-aMsJq8i1XXrb+BBsgmJBwak9mr69zPEIAUPb6c5yw2G/O4k1Q52lBxL+agZdQDN/RGf1ylQzrCswsOOgIiC1FA==", - "dependencies": { - "debug": "*", - "make-plural": "^6.0.1", - "math-interval-parser": "^2.0.1", - "messageformat": "^2.3.0", - "mustache": "*", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4792,10 +4899,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.11", @@ -4824,14 +4932,6 @@ "node": ">=0.10.0" } }, - "node_modules/math-interval-parser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz", - "integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -4850,38 +4950,6 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, - "node_modules/messageformat": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-2.3.0.tgz", - "integrity": "sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==", - "deprecated": "Package renamed as '@messageformat/core', see messageformat.github.io for more details. 'messageformat' will eventually provide a polyfill for Intl.MessageFormat, once it's been defined by Unicode & ECMA.", - "dependencies": { - "make-plural": "^4.3.0", - "messageformat-formatters": "^2.0.1", - "messageformat-parser": "^4.1.2" - } - }, - "node_modules/messageformat-formatters": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz", - "integrity": "sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==" - }, - "node_modules/messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, - "node_modules/messageformat/node_modules/make-plural": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", - "integrity": "sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==", - "bin": { - "make-plural": "bin/make-plural" - }, - "optionalDependencies": { - "minimist": "^1.2.0" - } - }, "node_modules/method-override": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", @@ -4936,19 +5004,19 @@ } }, "node_modules/mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", "dependencies": { - "mime-db": "1.47.0" + "mime-db": "1.48.0" }, "engines": { "node": ">= 0.6" @@ -5018,7 +5086,7 @@ "dependencies": { "browser-stdout": "1.3.0", "commander": "2.9.0", - "debug": "2.6.8", + "debug": "2.6.9", "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.1", @@ -5038,14 +5106,6 @@ "npm": ">= 1.4.x" } }, - "node_modules/mocha/node_modules/debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dependencies": { - "ms": "2.0.0" - } - }, "node_modules/mocha/node_modules/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", @@ -5109,14 +5169,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "bin": { - "mustache": "bin/mustache" - } - }, "node_modules/mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -5232,14 +5284,6 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "node_modules/nocache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", - "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/node-forge": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", @@ -5291,9 +5335,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==" }, "node_modules/nodemailer": { "version": "6.6.1", @@ -5303,6 +5347,14 @@ "node": ">=6.0.0" } }, + "node_modules/nodemailer-ntlm-auth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nodemailer-ntlm-auth/-/nodemailer-ntlm-auth-1.0.1.tgz", + "integrity": "sha512-nQK3NHGpyVU0CcGfpq2rzM3Xg/ZLYOwBGfIAz+oxW3Jhaj3opPC6eLXuei0doK5++Hm0zjl/PH+y7ZCc8sDFtg==", + "dependencies": { + "httpntlm": "1.7.6" + } + }, "node_modules/nodemon": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", @@ -5915,11 +5967,11 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { @@ -6213,14 +6265,6 @@ "node": ">=8.10.0" } }, - "node_modules/referrer-policy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", - "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -7066,9 +7110,9 @@ } }, "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "node_modules/sqlstring": { "version": "2.3.1", @@ -7469,6 +7513,41 @@ "node": ">=8" } }, + "node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "devOptional": true, + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -7519,6 +7598,19 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", + "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -7539,6 +7631,11 @@ "debug": "^2.2.0" } }, + "node_modules/underscore": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -7930,14 +8027,6 @@ } } }, - "node_modules/x-xss-protection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", - "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", @@ -8085,6 +8174,15 @@ "engines": { "node": ">=6" } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "engines": { + "node": ">=6" + } } }, "dependencies": { @@ -8097,9 +8195,9 @@ } }, "@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.4.tgz", + "integrity": "sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ==" }, "@babel/core": { "version": "7.14.3", @@ -8161,13 +8259,13 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz", + "integrity": "sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA==", "requires": { - "@babel/compat-data": "^7.13.15", + "@babel/compat-data": "^7.14.4", "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", + "browserslist": "^4.16.6", "semver": "^6.3.0" } }, @@ -8234,14 +8332,14 @@ "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" }, "@babel/helper-replace-supers": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", - "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz", + "integrity": "sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ==", "requires": { "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2" + "@babel/types": "^7.14.4" } }, "@babel/helper-simple-access": { @@ -8337,9 +8435,9 @@ } }, "@babel/parser": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", - "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==" + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.4.tgz", + "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==" }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", @@ -8478,9 +8576,9 @@ } }, "@babel/types": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", - "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.4.tgz", + "integrity": "sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==", "requires": { "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" @@ -8741,6 +8839,12 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, + "@types/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.6.tgz", + "integrity": "sha512-ZkrXnZLC1mc4b9QLKaSrsxV4oxTRs10OI2kgSApT8G0v1jrmqppSHUVQ15kLorzsFBTjvf7OKF4kAibuuNQ+xA==", + "dev": true + }, "@types/babel__core": { "version": "7.1.14", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", @@ -8778,6 +8882,100 @@ "@babel/types": "^7.3.0" } }, + "@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", + "dev": true + }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/compression": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz", + "integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cookie-parser": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", + "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/express": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz", + "integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-fileupload": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/express-fileupload/-/express-fileupload-1.1.6.tgz", + "integrity": "sha512-8z92PCVgvWvG1TpxucRU9oRz3hZc5cUz+CkeDe4XwVmg2DJDdd/7QASMsJzIo+9Pbfp7LfTEWSeEFUJZBohv9g==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/express-flash-2": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/express-flash-2/-/express-flash-2-1.0.6.tgz", + "integrity": "sha512-8YE6SDUxIIrsAKJL65nfUYtgO356QcT889GaXrwS0em7VYgbz6ShPdLUBOMDu8ihZrqidp5Qz2bNnWCCP4rZMw==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz", + "integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/express-session": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.0.tgz", + "integrity": "sha512-OQEHeBFE1UhChVIBhRh9qElHUvTp4BzKKHxMDkGHT7WuYk5eL93hPG7D8YAIkoBSbhNEY0RjreF15zn+U0eLjA==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -8800,33 +8998,135 @@ } }, "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "requires": { "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "26.0.23", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", + "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "@types/method-override": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@types/method-override/-/method-override-0.0.31.tgz", + "integrity": "sha512-aLA4MGzjYjBHGpr5TgAdPRyX97Jd+xlWN2wa6PbsjKYeoUKPsxVDFRSTjI3YG4MvGg3ZJkdMxjAXZfujU9qEPw==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/morgan": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz", + "integrity": "sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mysql": { + "version": "2.15.18", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.18.tgz", + "integrity": "sha512-JW74Nh3P/RDAnaP8uXe1qmRpoFBO84SiWvWoSju/F5+2S1kVBi1FbbDoqK/sTZrCCxySaOJnRATvWD+bLcJjAg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.1.tgz", - "integrity": "sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA==" + "version": "15.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==" + }, + "@types/nodemailer": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.2.tgz", + "integrity": "sha512-yhsqg5Xbr8aWdwjFS3QjkniW5/tLpWXtOYQcJdo9qE3DolBxsKzgRCQrteaMY0hos8MklJNSEsMqDpZynGzMNg==", + "dev": true, + "requires": { + "@types/node": "*" + } }, "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" }, + "@types/passport": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.6.tgz", + "integrity": "sha512-9oKfrJXuAxvyxdrtMCxKkHgmd6DMO8NDOLvMJ1LvIWd6/xP+i81PAkpTaEca7VhJX9S009RciwZL/j6dsLsHrA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/passport-strategy": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", + "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/passport": "*" + } + }, "@types/prettier": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==" }, + "@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "@types/stack-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==" }, + "@types/xml2js": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.8.tgz", + "integrity": "sha512-EyvT83ezOdec7BhDaEcsklWy7RSIdi6CNe95tmOAK0yx/Lm30C9K75snT3fYayK59ApC2oyW+rcHErdG05FHJA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "15.0.13", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", @@ -8861,9 +9161,9 @@ } }, "acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.3.0.tgz", + "integrity": "sha512-tqPKHZ5CaBJw0Xmy0ZZvLs1qTV+BNFSyvn77ASXkpBNfIRk8ev26fKrD9iLGwGA9zedPao52GSHzq8lyZG0NUw==" }, "acorn-globals": { "version": "6.0.0", @@ -8988,19 +9288,18 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "requires": { "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - } } }, "arr-diff": { @@ -9215,11 +9514,6 @@ "type-is": "~1.6.17" } }, - "bowser": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", - "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" - }, "boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -9377,15 +9671,10 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, - "camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, "caniuse-lite": { - "version": "1.0.30001230", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz", - "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==" + "version": "1.0.30001235", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", + "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==" }, "capture-exit": { "version": "2.0.0", @@ -9673,11 +9962,6 @@ "safe-buffer": "5.1.2" } }, - "content-security-policy-builder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", - "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==" - }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -9720,6 +10004,12 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -9730,11 +10020,6 @@ "which": "^2.0.1" } }, - "crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" - }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -9761,11 +10046,6 @@ } } }, - "dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" - }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -9917,11 +10197,6 @@ } } }, - "dont-sniff-mimetype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", - "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" - }, "dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -9931,6 +10206,11 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -9943,9 +10223,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.741", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.741.tgz", - "integrity": "sha512-4i3T0cwnHo1O4Mnp9JniEco8bZiXoqbm3PhW5hv7uu8YLg35iajYrRnNyKFaN8/8SSTskU2hYqVTeYVPceSpUA==" + "version": "1.3.749", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.749.tgz", + "integrity": "sha512-F+v2zxZgw/fMwPz/VUGIggG4ZndDsYy0vlpthi3tjmDZlcfbhN5mYW0evXUsBr2sUtuDANFtle410A9u/sd/4A==" }, "emittery": { "version": "0.7.2", @@ -9978,15 +10258,6 @@ "is-arrayish": "^0.2.1" } }, - "errorhandler": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.4.3.tgz", - "integrity": "sha1-t7cO2PNZ6duICS8tIMD4MUIK2D8=", - "requires": { - "accepts": "~1.3.0", - "escape-html": "~1.0.3" - } - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -10325,11 +10596,6 @@ "bser": "2.1.1" } }, - "feature-policy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", - "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==" - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -10382,9 +10648,9 @@ } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fragment-cache": { "version": "0.2.1", @@ -10630,76 +10896,15 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" }, "helmet": { - "version": "3.23.3", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz", - "integrity": "sha512-U3MeYdzPJQhtvqAVBPntVgAvNSOJyagwZwyKsFdyRa8TV3pOKVFljalPOCxbw5Wwf2kncGhmP0qHjyazIdNdSA==", - "requires": { - "depd": "2.0.0", - "dont-sniff-mimetype": "1.1.0", - "feature-policy": "0.3.0", - "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.10.0", - "hide-powered-by": "1.1.0", - "hpkp": "2.0.0", - "hsts": "2.2.0", - "nocache": "2.1.0", - "referrer-policy": "1.2.0", - "x-xss-protection": "1.3.0" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - } - } - }, - "helmet-crossdomain": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", - "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" - }, - "helmet-csp": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz", - "integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==", - "requires": { - "bowser": "2.9.0", - "camelize": "1.0.0", - "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0" - } - }, - "hide-powered-by": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", - "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, - "hpkp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", - "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" - }, - "hsts": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", - "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", - "requires": { - "depd": "2.0.0" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - } - } - }, "html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -10756,6 +10961,20 @@ } } }, + "httpntlm": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.7.6.tgz", + "integrity": "sha1-aZHoNSg2AH1nEBuD247Q+RX5BtA=", + "requires": { + "httpreq": ">=0.4.22", + "underscore": "~1.7.0" + } + }, + "httpreq": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz", + "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=" + }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -10785,19 +11004,6 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" }, - "i18n": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.8.6.tgz", - "integrity": "sha512-aMsJq8i1XXrb+BBsgmJBwak9mr69zPEIAUPb6c5yw2G/O4k1Q52lBxL+agZdQDN/RGf1ylQzrCswsOOgIiC1FA==", - "requires": { - "debug": "*", - "make-plural": "^6.0.1", - "math-interval-parser": "^2.0.1", - "messageformat": "^2.3.0", - "mustache": "*", - "sprintf-js": "^1.1.2" - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -11795,10 +12001,11 @@ "semver": "^6.0.0" } }, - "make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true }, "makeerror": { "version": "1.0.11", @@ -11821,11 +12028,6 @@ "object-visit": "^1.0.0" } }, - "math-interval-parser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz", - "integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==" - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -11841,36 +12043,6 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, - "messageformat": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-2.3.0.tgz", - "integrity": "sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==", - "requires": { - "make-plural": "^4.3.0", - "messageformat-formatters": "^2.0.1", - "messageformat-parser": "^4.1.2" - }, - "dependencies": { - "make-plural": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", - "integrity": "sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==", - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "messageformat-formatters": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz", - "integrity": "sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==" - }, - "messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, "method-override": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", @@ -11912,16 +12084,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" }, "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", "requires": { - "mime-db": "1.47.0" + "mime-db": "1.48.0" } }, "mimic-fn": { @@ -11972,7 +12144,7 @@ "requires": { "browser-stdout": "1.3.0", "commander": "2.9.0", - "debug": "2.6.8", + "debug": "2.6.9", "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.1", @@ -11984,14 +12156,6 @@ "supports-color": "3.1.2" }, "dependencies": { - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "requires": { - "ms": "2.0.0" - } - }, "glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", @@ -12044,11 +12208,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==" - }, "mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -12147,11 +12306,6 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "nocache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", - "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" - }, "node-forge": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", @@ -12193,15 +12347,23 @@ } }, "node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==" }, "nodemailer": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.1.tgz", "integrity": "sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg==" }, + "nodemailer-ntlm-auth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nodemailer-ntlm-auth/-/nodemailer-ntlm-auth-1.0.1.tgz", + "integrity": "sha512-nQK3NHGpyVU0CcGfpq2rzM3Xg/ZLYOwBGfIAz+oxW3Jhaj3opPC6eLXuei0doK5++Hm0zjl/PH+y7ZCc8sDFtg==", + "requires": { + "httpntlm": "1.7.6" + } + }, "nodemon": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", @@ -12652,11 +12814,11 @@ } }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, @@ -12912,11 +13074,6 @@ "picomatch": "^2.2.1" } }, - "referrer-policy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", - "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -13594,9 +13751,9 @@ } }, "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sqlstring": { "version": "2.3.1", @@ -13896,6 +14053,28 @@ "punycode": "^2.1.1" } }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "devOptional": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true + } + } + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -13931,6 +14110,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", + "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "devOptional": true + }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -13948,6 +14133,10 @@ "debug": "^2.2.0" } }, + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -14247,11 +14436,6 @@ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "requires": {} }, - "x-xss-protection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", - "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" - }, "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", @@ -14369,6 +14553,12 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true } } } diff --git a/package.json b/package.json index 78d2bb4106093f642e5e0f22d0ce5d86178b5cdf..4604e0514bd1c44f547f8b1850f37b64ef28004f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "url": "https://transfer.hft-stuttgart.de/gitlab/m4lab_tv1/user-account.git" }, "scripts": { - "start": "nodemon app.js", + "start": "nodemon app.ts", + "build": "tsc -build", + "clean": "tsc -build --clean", "test": "jest" }, "dependencies": { @@ -26,17 +28,15 @@ "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "1.4.3", - "crypto": "^1.0.1", - "errorhandler": "1.4.3", + "dotenv": "^9.0.2", "express": "^4.17.1", "express-fileupload": "^1.1.6", "express-flash-2": "^1.0.1", "express-session": "^1.17.0", "form-data": "^3.0.0", "fs": "0.0.1-security", - "helmet": "^3.23.3", - "i18n": "^0.8.5", - "jest": "^26.6.3", + "helmet": "^4.6.0", + "jest": "^26.5.0", "method-override": "^3.0.0", "morgan": "^1.9.1", "mysql": "^2.17.1", @@ -48,7 +48,26 @@ "pug": "^3.0.2" }, "devDependencies": { - "nodemon": "^2.0.1" + "@types/async": "^3.2.6", + "@types/bcryptjs": "^2.4.2", + "@types/compression": "^1.7.0", + "@types/cookie-parser": "^1.4.2", + "@types/express": "^4.17.11", + "@types/express-fileupload": "^1.1.6", + "@types/express-flash-2": "^1.0.6", + "@types/express-session": "^1.17.0", + "@types/jest": "^26.0.23", + "@types/method-override": "^0.0.31", + "@types/morgan": "^1.9.2", + "@types/mysql": "^2.15.18", + "@types/node": "^15.0.2", + "@types/nodemailer": "^6.4.1", + "@types/passport": "^1.0.6", + "@types/passport-strategy": "^0.2.35", + "@types/xml2js": "^0.4.8", + "nodemon": "^2.0.1", + "ts-node": "^9.1.1", + "typescript": "^4.2.4" }, "engines": { "node": ">= 4.0.0" diff --git a/public/js/jquery-ui/i18n/datepicker-de.js b/public/js/jquery-ui/i18n/datepicker-de.js deleted file mode 100644 index e2e61d27b6b4bf33c0fd59e9ebf2d307d0337af3..0000000000000000000000000000000000000000 --- a/public/js/jquery-ui/i18n/datepicker-de.js +++ /dev/null @@ -1,37 +0,0 @@ -/* German initialisation for the jQuery UI date picker plugin. */ -/* Written by Milian Wolff (mail@milianw.de). */ -( function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define( [ "../widgets/datepicker" ], factory ); - } else { - - // Browser globals - factory( jQuery.datepicker ); - } -}( function( datepicker ) { - -datepicker.regional.de = { - closeText: "Schließen", - prevText: "<Zurück", - nextText: "Vor>", - currentText: "Heute", - monthNames: [ "Januar","Februar","März","April","Mai","Juni", - "Juli","August","September","Oktober","November","Dezember" ], - monthNamesShort: [ "Jan","Feb","Mär","Apr","Mai","Jun", - "Jul","Aug","Sep","Okt","Nov","Dez" ], - dayNames: [ "Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag" ], - dayNamesShort: [ "So","Mo","Di","Mi","Do","Fr","Sa" ], - dayNamesMin: [ "So","Mo","Di","Mi","Do","Fr","Sa" ], - weekHeader: "KW", - dateFormat: "dd.mm.yy", - firstDay: 1, - isRTL: false, - showMonthAfterYear: false, - yearSuffix: "" }; -datepicker.setDefaults( datepicker.regional.de ); - -return datepicker.regional.de; - -} ) ); \ No newline at end of file diff --git a/routes/account.js b/routes/account.js deleted file mode 100644 index 0f3b2fc7fb77929caa72ef75797173fcecfa3a4d..0000000000000000000000000000000000000000 --- a/routes/account.js +++ /dev/null @@ -1,568 +0,0 @@ -const fs = require('fs') -const SamlStrategy = require('passport-saml').Strategy -const dbconn = require('../config/dbconn') -const methods = require('../functions/methods') -const gitlab = require('../functions/gitlab') -const constants = require('../config/const') -// pwd encryption -const bcrypt = require('bcryptjs'); -const saltRounds = 10; -const salt = 64; // salt length -// forgot pwd -const async = require('async') -const crypto = require('crypto') -const mailer = require('../config/mailer') -const logoDir = 'public/upload/' -const defaultLogo = 'public/default/logo.png' -const tpGitlabURL = 'https://transfer.hft-stuttgart.de/gitlab/' -const tpGitlabPagesURL = 'https://transfer.hft-stuttgart.de/pages/' - -const portalUser = require('../classes/user') -const projectInformation = require('../classes/website') -const projectRepo = require('../classes/repo') - -module.exports = function (app, config, passport, lang) { - - // =========== PASSPORT ======= - passport.serializeUser(function (user, done) { - done(null, user); - }); - - passport.deserializeUser(function (user, done) { - done(null, user); - }); - - var samlStrategy = new SamlStrategy({ - // URL that goes from the Identity Provider -> Service Provider - callbackUrl: config.passport.saml.path, - // Base address to call logout requests - logoutUrl: config.passport.saml.logoutUrl, - - entryPoint: config.passport.saml.entryPoint, - issuer: config.passport.saml.issuer, - identifierFormat: null, - - // Service Provider private key - decryptionPvk: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), - // Service Provider Certificate - privateCert: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), - // Identity Provider's public key - cert: fs.readFileSync(__dirname + '/cert/cert_idp.pem', 'utf8'), - - validateInResponseTo: false, - disableRequestedAuthnContext: true - }, - function (profile, done) { - return done(null, { - id: profile.nameID, - idFormat: profile.nameIDFormat, - email: profile.email, - firstName: profile.givenName, - lastName: profile.sn - }); - }); - - passport.use(samlStrategy); - - // ============= SAML ============== - app.post(config.passport.saml.path, - passport.authenticate(config.passport.strategy, - { - failureRedirect: '/account/', - failureFlash: true - }), - function (req, res) { - res.redirect('/account/'); - } - ); - - // to generate Service Provider's XML metadata - app.get('/saml/metadata', - function(req, res) { - res.type('application/xml'); - var spMetadata = samlStrategy.generateServiceProviderMetadata(fs.readFileSync(__dirname + '/cert/cert.pem', 'utf8')); - res.status(200).send(spMetadata); - } - ); - - // ======== APP ROUTES - ACCOUNT ==================== - - async function getLoggedInUserData(email) { - let user = await methods.getUserByEmail(email) - if (!user) { - console.log('no user found') - return null - } else { - let loggedInUser = new portalUser( - user.id, email, user.salutation, user.title, user.firstname, user.lastname, user.industry, user.organisation, user.speciality, user.m4lab_idp, null, user.verificationStatus - ) - - let userGitlabId = await methods.getGitlabId(loggedInUser.id) - if (userGitlabId) { - loggedInUser.setGitlabUserId(userGitlabId) - } - return loggedInUser - } - } - - app.get('/', async function (req, res) { - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - res.render(lang+'/account/home', { - user: loggedInUser - }); - } - }); - - app.get('/login', - passport.authenticate(config.passport.strategy, { - successRedirect: '/', - failureRedirect: '/login' - }) - ) - - app.get('/logout', function (req, res) { - if (req.user == null) { - return res.redirect('/'); - } - - req.user.nameID = req.user.id; - req.user.nameIDFormat = req.user.idFormat; - return samlStrategy.logout(req, function(err, uri) { - req.logout(); - - if ( req.session ) { - req.session.destroy((err) => { - if(err) { - return console.log(err); - } - }); - } - - return res.redirect(uri); - }); - }); - - app.get('/profile', async function (req, res) { - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if(loggedInUser.getVerificationStatus() != 1) { - res.redirect('/account/') - } else { - res.render(lang+'/account/profile', { - user: loggedInUser - }) - } - } - }) - - app.get('/services', async function(req, res){ - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if(loggedInUser.getVerificationStatus() != 1) { // unverified users - res.redirect('/account/') - } else { - let gitlabReposArr = [] - let gitlabPagesArr = [] - - if(loggedInUser.getGitlabUserId()) { // for users who have activated their gitlab account - let userProjects = await gitlab.getUserProjects(loggedInUser.getGitlabUserId()) - if (!userProjects) { - console.error("something went wrong") - res.status(500).render(lang+'/500', { error: "something went wrong" }) - } - - for (project in userProjects) { - if (userProjects[project].tag_list.includes('website')) { - let page = { - projectInformation: new projectInformation(loggedInUser.getGitlabUserId(), userProjects[project].id, userProjects[project].name, - userProjects[project].description, userProjects[project].avatar_url, userProjects[project].path_with_namespace), - pipelineStatus: await gitlab.getProjectPipelineLatestStatus(userProjects[project].id) - } - gitlabPagesArr.push(page) - } else { - let repo = new projectRepo(loggedInUser.getGitlabUserId(), userProjects[project].id, userProjects[project].name, - userProjects[project].description, userProjects[project].avatar_url, userProjects[project].path_with_namespace) - gitlabReposArr.push(repo) - } - } - - res.render(lang+'/account/services', { - user: loggedInUser, - gitlabRepos: gitlabReposArr, - gitlabPages: gitlabPagesArr - }) - } else { // for users who have not activated their gitlab account yet - let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) - if (!gitlabUser) { - res.render(lang+'/account/services', { - user: loggedInUser, - gitlabRepos: null, - gitlabPages: null - }) - } else { - let gitlabActivationData = { - user_id: loggedInUser.getId(), - gitlab_userId: gitlabUser.id} - // RS: update to await? - methods.addGitlabUser(gitlabActivationData, function(err){ - if(err) { - res.status(500).render(lang+'/500', { error: err }) - } else { - res.redirect('/account/services') - } - }) - } - } - } - } - }) - - app.get('/security', async function (req, res) { - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if(loggedInUser.getVerificationStatus() == 1 && loggedInUser.getIdpStatus() == 1) { - res.render(lang+'/account/security', { - user: loggedInUser - }) - } else { - res.redirect('/account/') - } - } - }) - - app.post('/updateProfile', async function (req, res) { - var userData = { - salutation: req.body.inputSalutation, - title: req.body.inputTitle, - firstname: req.body.inputFirstname, - lastname: req.body.inputLastname, - email: req.body.inputEmail, - organisation: req.body.inputOrganisation, - industry: req.body.inputIndustry, - speciality: req.body.inputSpeciality, - } - - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if (userData.email) { - dbconn.user.query('UPDATE user SET ? WHERE email = "' +userData.email+'"', userData, function (err, rows, fields) { - if (err) { - res.flash('error', "Failed") - } - else { - loggedInUser.updateProfile(userData.salutation, userData.title, userData.firstname, userData.lastname, userData.email, - userData.organisation, userData.industry, userData.speciality) - res.flash('success', 'Ihr Benutzerprofil wurde aktualisiert!') - } - res.redirect('/account/profile'); - }) - } - } - }); - - app.post('/changePwd', async function (req, res) { - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - var currPwd = req.body.inputCurrPwd - var newPwd = req.body.inputNewPwd - var retypePwd = req.body.inputConfirm - - // update - get userId from loggedInUser - dbconn.user.query('SELECT password FROM credential WHERE user_id='+loggedInUser.getId(), function (err, rows, fields) { - if (err) { - console.error(err) - res.status(500).render(lang+'/500', { error: err }) - } - var userPwd = rows[0].password - - // check if the password is correct - bcrypt.compare(currPwd, userPwd, function(err, isMatch) { - if (err) { - console.error(err) - res.status(500).render(lang+'/500', { error: err }) - } else if (!isMatch) { - res.flash('error', "Das Passwort ist leider falsch. Bitte überprüfen Sie Ihre Eingabe.") - res.redirect('/account/security') - } else { - if ( newPwd != retypePwd ) { - res.flash('error', 'Passwörter stimmen nicht überein. Bitte stellen Sie sicher, dass Sie das Passwort beide Male genau gleich eingeben.') - res.redirect('/account/security') - } else { - // update password - bcrypt.genSalt(saltRounds, function(err, salt) { - bcrypt.hash(newPwd, salt, function(err, hash) { - var credentialData = { - password: hash, - user_id: userId - } - methods.updateCredential(credentialData, function(err){ - if (err) { - res.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") - throw err - } else { - res.flash('success', "Passwort aktualisiert!") - mailer.options.to = req.user.email - mailer.options.subject = constants.updatePasswordMailSubject - mailer.options.html = constants.updatePasswordMailContent+'
'+constants.mailSignature+'
' - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { console.log(err) } - }); - } - res.redirect('/account/security') - }) - }); - }); - } - } - }) - }) - } - }); - - app.get('/resendVerificationEmail', async function(req, res){ - if (!req.isAuthenticated) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if (!loggedInUser) { - res.redirect('/login') - } else { - let token = await methods.getVerificationTokenByUserId(loggedInUser.id) - if (!token) { - res.send(false) - } else { - // send email - var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" - var emailContent = '
Lieber Nutzer,

' + - '

vielen Dank für Ihre Anmeldung am Transferportal der HFT Stuttgart.
' + - 'Um Ihre Anmeldung zu bestätigen, klicken Sie bitte diesen Link: ' + config.app.host + '/verifyAccount?token=' + token + - '

' + - 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.


' + constants.mailSignature + - '
'; - mailer.options.to = loggedInUser.email; - mailer.options.subject = emailSubject; - mailer.options.html = emailContent; - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { - console.log('cannot send email') - throw err - } - }) - res.send(true) - } - } - } - }) - - // ============= NEW GITLAB PAGES =========================== - - app.get('/newInformation', async function(req, res){ - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) - if (!gitlabUser) { // no user found - res.redirect('/account/services') - } else { - res.render(lang+'/account/newInformation', { - user: loggedInUser, - gitlabUsername: gitlabUser.username - }) - } - } - }) - app.post('/newInformation', async function(req, res) { - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - if (!req.body.name && !req.body.description) { - res.flash('error', 'Bitte geben Sie die benötigten Daten ein') - res.redirect('/account/newInformation') - } else { - let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') - let projectDesc = req.body.description - let projectTemplate = req.body.template - let newInformation = new projectInformation(loggedInUser.getGitlabUserId(), null, projectName, projectDesc, null, null) - let newLogoFile = defaultLogo - - if (req.files) { newLogoFile = req.files.logo } - - async.waterfall([ - function(callback){ // upload logo - if (!req.files) { - callback(null, newLogoFile) - } else { - newLogoFile.mv(logoDir + newLogoFile.name, function(err) { - newLogoFile = logoDir+newLogoFile.name - callback(err, newLogoFile) - }) - } - }, - async function(newLogoFile){ // create a new GitLab Page - let newPages = await gitlab.createNewPages(newInformation, newLogoFile, projectTemplate) - if (newPages.error) { - if(newPages.data.message.name == "has already been taken") { - res.flash("error", "Der Projektname '"+newInformation.getName()+"' ist bereits vergeben, bitte wählen Sie einen anderen Namen.") - } else { - res.flash("error", "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. ") - } - res.redirect('/account/newInformation') - } else { - let newPagesData = newPages.data - - res.flash("success", "Ihre Webseite wurde erstellt, aber noch nicht veröffentlicht. Um Ihre Webseite endgültig zu veröffentlichen, "+ - "schließen Sie die Einrichtung gemäß unten stehender Anleitung ab.") - /* res.flash("success", "Your website will be published AFTER you complete your website by following the provided guideline below."+ - "\r\n Your website URL: "+tpGitlabPagesURL+newPagesData.path_with_namespace+"/home/") */ - res.redirect('/account/updateInformation?id='+newPagesData.id) - } - } - ], function (err) { - if(err) console.log(err) - // remove logo - if (req.files) { - fs.unlink(newLogoFile, (err) => { - if(err) console.log(err) - }) - } - }) - } - } - }) - - app.get('/updateInformation', async function(req, res){ - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - if(!req.query.id) { - res.redirect('/account/services') - } else { - let project = await gitlab.getProjectById(req.query.id) - if (!project) { - console.log(" ========= Error or no project found") - res.redirect('/account/services') - } else if (!project.owner) { - console.log(" ========= Project cannot be accessed, since it does not have an owner") - res.redirect('/account/services') - } else if (project.owner.id != loggedInUser.getGitlabUserId()) { - console.log(" ========= Access denied: Not your project") - res.redirect('/account/services') - } else { - let curInformation = new projectInformation(loggedInUser.getGitlabUserId(), req.query.id, project.name, project.description, - project.avatar_url, project.path_with_namespace) - - res.render(lang+'/account/updateInformation', { - user: loggedInUser, - information: curInformation - }) - } - } - } - }) - // update a website - app.post('/updateInformation', async function(req, res){ - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - if (!req.body.name && !req.body.description) { - res.flash('error', 'Bitte geben Sie die benötigten Daten ein') - res.redirect('/account/updateInformation') - } else { - let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') - let projectDesc = req.body.description - let updatedInformation = new projectInformation(loggedInUser.getGitlabUserId(), req.query.id, projectName, projectDesc, null, null) - let newLogoFile - - async.waterfall([ - function(callback){ // upload logo - if(!req.files) { - callback(null, newLogoFile) - } else { - newLogoFile = req.files.logo - newLogoFile.mv(logoDir + newLogoFile.name, function(err) { - newLogoFile = logoDir + newLogoFile.name - callback(err, newLogoFile) - }) - } - }, - async function(newLogoFile, callback){ // update gitlab page - let updatedPages = await gitlab.updateProject(updatedInformation, newLogoFile) - let pagesData = updatedPages.data - if (updatedPages.error) { - if(pagesData.message.name == "has already been taken") { - res.flash("error", "Der Projektname ist bereits vergeben, bitte wählen Sie einen anderen Namen.") - } else { - res.flash("error", "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. ") - } - } else { - updatedInformation.setLogo(pagesData.avatar_url) - updatedInformation.setPath(pagesData.path) - res.flash("success", "Ihre Website wurde aktualisiert") - } - res.redirect('/account/updateInformation?id='+updatedInformation.getId()) - } - ], function (err) { - if(err) console.log(err) - if(newLogoFile){ // remove logo - fs.unlink(newLogoFile, (err) => { - if(err) console.log(err) - }) - } - }) - } - } - }) - - app.delete('/deleteProject', async function(req, res){ - console.log("delete project") - - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - let projectId = req.body.id - - if (projectId) { - // check if the owner is valid - let project = await gitlab.getProjectById(projectId) - if (!project) { - console.log(" ========= Error or no project found") - } else if (!project.owner) { - console.log(" ========= Project cannot be accessed, since it does not have an owner") - } else if (project.owner.id != loggedInUser.getGitlabUserId()) { - console.log(" ========= Access denied: Not your project") - } else { - // delete project - let project = await gitlab.deleteProjectById(projectId) - if (project.error) { - res.flash("error", "Project cannot be deleted. Please try again.") - } - } - } - res.redirect('/account/services') - } - }) - -} \ No newline at end of file diff --git a/routes/account.ts b/routes/account.ts new file mode 100644 index 0000000000000000000000000000000000000000..da1b2fb9e4907cfb424dc8edd6ae085be6beba37 --- /dev/null +++ b/routes/account.ts @@ -0,0 +1,596 @@ +import fs from 'fs' +import async from 'async' +import bcrypt from 'bcryptjs' +import * as passportSaml from 'passport-saml' +import dbconn from '../config/dbconn' +import methods from '../functions/methods' +import gitlab from '../functions/gitlab' +import constants from '../config/const' +import mailer from '../config/mailer' +import portalUser from '../classes/user' +import projectInformation from '../classes/website' +import projectRepo from '../classes/repo' + +const SamlStrategy = passportSaml.Strategy +const saltRounds = 10; +const salt = 64; // salt length +const logoDir = 'public/upload/' +const defaultLogo:any = 'public/default/logo.png' + +export = function (app:any, config:any, passport:any, lang:string) { + + // =========== PASSPORT ======= + passport.serializeUser(function (user:any, done:any) { + done(null, user); + }); + + passport.deserializeUser(function (user:any, done:any) { + done(null, user); + }); + + var samlStrategy = new SamlStrategy({ + // URL that goes from the Identity Provider -> Service Provider + callbackUrl: config.passport.saml.path, + // Base address to call logout requests + logoutUrl: config.passport.saml.logoutUrl, + + entryPoint: config.passport.saml.entryPoint, + issuer: config.passport.saml.issuer, + identifierFormat: undefined, + + // Service Provider private key + decryptionPvk: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), + // Service Provider Certificate + privateCert: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), + // Identity Provider's public key + cert: fs.readFileSync(__dirname + '/cert/cert_idp.pem', 'utf8'), + + validateInResponseTo: false, + disableRequestedAuthnContext: true + }, + function (profile:any, done:any) { + return done(null, { + id: profile.nameID, + idFormat: profile.nameIDFormat, + email: profile.email, + firstName: profile.givenName, + lastName: profile.sn + }); + }); + + passport.use(samlStrategy); + + // ============= SAML ============== + app.post(config.passport.saml.path, + passport.authenticate(config.passport.strategy, + { + failureRedirect: '/account/', + failureFlash: true + }), + function (req:any, res:any) { + res.redirect('/account/'); + } + ); + + // to generate Service Provider's XML metadata + app.get('/saml/metadata', + function(req:any, res:any) { + res.type('application/xml'); + var spMetadata = samlStrategy.generateServiceProviderMetadata(fs.readFileSync(__dirname + '/cert/cert.pem', 'utf8')); + res.status(200).send(spMetadata); + } + ); + + // ======== APP ROUTES - ACCOUNT ==================== + + async function getLoggedInUserData(email:string) { + let user = await methods.getUserByEmail(email) + if (!user) { + console.log('no user found') + return null + } else { + let loggedInUser = new portalUser( + user.id, email, user.salutation, user.title, user.firstname, user.lastname, user.industry, user.organisation, user.speciality, user.m4lab_idp, user.verificationStatus + ) + + let userGitlabId = await methods.getGitlabId(loggedInUser.id) + if (userGitlabId) { + loggedInUser.setGitlabUserId(userGitlabId) + } + return loggedInUser + } + } + + app.get('/', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + res.render(lang+'/account/home', { + user: loggedInUser + }); + } + }); + + app.get('/login', + passport.authenticate(config.passport.strategy, { + successRedirect: '/', + failureRedirect: '/login' + }) + ) + + app.get('/logout', function (req:any, res:any) { + if (req.user == null) { + return res.redirect('/'); + } + + req.user.nameID = req.user.id; + req.user.nameIDFormat = req.user.idFormat; + return samlStrategy.logout(req, function(err:any, uri:any) { + req.logout(); + + if ( req.session ) { + req.session.destroy((err:any) => { + if(err) { + return console.log(err); + } + }); + } + + return res.redirect(uri); + }); + }); + + app.get('/profile', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + if(loggedInUser.getVerificationStatus() != 1) { + res.redirect('/account/') + } else { + res.render(lang+'/account/profile', { + user: loggedInUser + }) + } + } + + } + }) + + app.get('/services', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + if(loggedInUser.getVerificationStatus() != 1) { // unverified users + res.redirect('/account/') + } else { + let gitlabReposArr = [] + let gitlabPagesArr = [] + + if(loggedInUser.getGitlabUserId()) { // for users who have activated their gitlab account + let userProjects = await gitlab.getUserProjects(loggedInUser.getGitlabUserId()!) + if (!userProjects) { + console.error("something went wrong") + res.status(500).render(lang+'/500', { error: "something went wrong" }) + } + + let project:any + for (project in userProjects) { + if (userProjects[project].tag_list.includes('website')) { + let page = { + projectInformation: new projectInformation(loggedInUser.getGitlabUserId()!, userProjects[project].name, userProjects[project].description, + userProjects[project].id, userProjects[project].avatar_url, userProjects[project].path_with_namespace), + pipelineStatus: await gitlab.getProjectPipelineLatestStatus(userProjects[project].id) + } + gitlabPagesArr.push(page) + } else { + let repo = new projectRepo(loggedInUser.getGitlabUserId()!, userProjects[project].name, userProjects[project].description, + userProjects[project].id, userProjects[project].avatar_url, userProjects[project].path_with_namespace) + gitlabReposArr.push(repo) + } + } + + res.render(lang+'/account/services', { + user: loggedInUser, + gitlabRepos: gitlabReposArr, + gitlabPages: gitlabPagesArr + }) + } else { // for users who have not activated their gitlab account yet + let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) + if (!gitlabUser) { + res.render(lang+'/account/services', { + user: loggedInUser, + gitlabRepos: null, + gitlabPages: null + }) + } else { + let gitlabActivationData = { + user_id: loggedInUser.getId(), + gitlab_userId: gitlabUser.id} + + methods.addGitlabUser(gitlabActivationData, function(err:any){ + if(err) { + res.status(500).render(lang+'/500', { error: err }) + } else { + res.redirect('/account/services') + } + }) + } + } + } + } + } + }) + + app.get('/security', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + if(loggedInUser.getVerificationStatus() == 1 && loggedInUser.getIdpStatus() == 1) { + res.render(lang+'/account/security', { + user: loggedInUser + }) + } else { + res.redirect('/account/') + } + } + } + }) + + app.post('/updateProfile', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + let userData = { + salutation: req.body.inputSalutation, + title: req.body.inputTitle, + firstname: req.body.inputFirstname, + lastname: req.body.inputLastname, + email: req.body.inputEmail, + organisation: req.body.inputOrganisation, + industry: req.body.inputIndustry, + speciality: req.body.inputSpeciality, + } + let result = await methods.updateUserById(loggedInUser.getId(), userData) + if (!result) { + res.flash('error', "Failed") + } else { + loggedInUser.updateProfile(userData.salutation, userData.title, userData.firstname, userData.lastname, userData.email, + userData.organisation, userData.industry, userData.speciality) + res.flash('success', 'Ihr Benutzerprofil wurde aktualisiert!') + } + res.redirect('/account/profile') + } + + } + }); + + app.post('/account/changePwd', async function (req:any, res:any) { + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + let currPwd = req.body.inputCurrPwd + let newPwd = req.body.inputNewPwd + let retypePwd = req.body.inputConfirm + + dbconn.user.query('SELECT password FROM credential WHERE user_id='+loggedInUser.getId(), function (err, rows, fields) { + if (err) { + console.error(err) + res.status(500).render(lang+'/500', { error: err }) + } + var userPwd = rows[0].password + + // check if the password is correct + bcrypt.compare(currPwd, userPwd, function(err, isMatch) { + if (err) { + console.error(err) + res.status(500).render(lang+'/500', { error: err }) + } else if (!isMatch) { + res.flash('error', "Das Passwort ist leider falsch. Bitte überprüfen Sie Ihre Eingabe.") + res.redirect('/account/security') + } else { + if ( newPwd != retypePwd ) { + res.flash('error', 'Passwörter stimmen nicht überein. Bitte stellen Sie sicher, dass Sie das Passwort beide Male genau gleich eingeben.') + res.redirect('/account/security') + } else { + // update password + bcrypt.genSalt(saltRounds, function(err, salt) { + bcrypt.hash(newPwd, salt, async function(err, hash) { + var credentialData = { + password: hash, + user_id: loggedInUser!.getId() + } + + let result = await methods.updateCredential(credentialData) + if (!result) { + console.log('Failed to reset password') + res.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") + } else { + res.flash('success', "Passwort aktualisiert!") + // send notifiaction email + mailer.options.to = loggedInUser!.getEmail() + mailer.options.subject = constants.updatePasswordMailSubject + mailer.options.html = constants.updatePasswordMailContent+'
'+constants.mailSignature+'
' + mailer.transporter.sendMail(mailer.options, function(err:any) { + if (err) { console.log(err) } + }) + } + res.redirect('/account/security') + + }); + }); + } + } + }) + }) + } + } + }); + + app.get('/resendVerificationEmail', async function(req:any, res:any){ + if (!req.isAuthenticated) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + let token = await methods.getVerificationTokenByUserId(loggedInUser.id) + if (!token) { + res.send(false) + } else { + // send email + var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" + var emailContent = '
Lieber Nutzer,

' + + '

vielen Dank für Ihre Anmeldung am Transferportal der HFT Stuttgart.
' + + 'Um Ihre Anmeldung zu bestätigen, klicken Sie bitte diesen Link: ' + config.app.host + '/verifyAccount?token=' + token + + '

' + + 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.


' + constants.mailSignature + + '
'; + mailer.options.to = loggedInUser.email; + mailer.options.subject = emailSubject; + mailer.options.html = emailContent; + mailer.transport.sendMail(mailer.options, function(err:any) { + if (err) { + console.log('cannot send email') + throw err + } + }) + res.send(true) + } + } + } + }) + + // ============= NEW GITLAB PAGES =========================== + + app.get('/newInformation', async function(req:any, res:any){ + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) + if (!gitlabUser) { // no user found + res.redirect('/account/services') + } else { + res.render(lang+'/account/newInformation', { + user: loggedInUser, + gitlabUsername: gitlabUser.username + }) + } + } + } + }) + app.post('/newInformation', async function(req:any, res:any) { + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + if (!req.body.name && !req.body.description) { + res.flash('error', 'Bitte geben Sie die benötigten Daten ein') + res.redirect('/account/newInformation') + } else { + let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') + let projectDesc = req.body.description + let projectTemplate = req.body.template + let newInformation = new projectInformation(loggedInUser.getGitlabUserId()!, projectName, projectDesc) + let newLogoFile = defaultLogo + + if (req.files) { newLogoFile = req.files.logo } + + async.waterfall([ + function(callback:any){ // upload logo + if (!req.files) { + callback(null, newLogoFile) + } else { + newLogoFile.mv(logoDir + newLogoFile.name, function(err:any) { + newLogoFile = logoDir+newLogoFile.name + callback(err, newLogoFile) + }) + } + }, + async function(newLogoFile:any){ // create a new GitLab Page + let newPages = await gitlab.createNewPages(newInformation, newLogoFile, projectTemplate) + if (newPages.status) { + if(newPages.data.message.name == "has already been taken") { + res.flash("error", "Der Projektname '"+newInformation.getName()+"' ist bereits vergeben, bitte wählen Sie einen anderen Namen.") + } else { + res.flash("error", "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. ") + } + res.redirect('/account/newInformation') + } else { + res.flash("success", "Ihre Webseite wurde erstellt, aber noch nicht veröffentlicht. Um Ihre Webseite endgültig zu veröffentlichen, "+ + "schließen Sie die Einrichtung gemäß unten stehender Anleitung ab.") + res.redirect('/account/updateInformation?id='+newPages.id) + } + } + ], function (err) { + if(err) console.log(err) + // remove logo + if (req.files) { + fs.unlink(newLogoFile, (err) => { + if(err) console.log(err) + }) + } + }) + } + } + } + }) + + app.get('/updateInformation', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + if (!loggedInUser) { + res.redirect('/login') + } else { + if(!req.query.id) { + res.redirect('/account/services') + } else { + let project = await gitlab.getProjectById(req.query.id) + if (!project) { + console.log(" ========= Error or no project found") + res.redirect('/account/services') + } else if (!project.owner) { + console.log(" ========= Project cannot be accessed, since it does not have an owner") + res.redirect('/account/services') + } else if (project.owner.id != loggedInUser.getGitlabUserId()) { + console.log(" ========= Access denied: Not your project") + res.redirect('/account/services') + } else { + let curInformation = new projectInformation(loggedInUser.getGitlabUserId()!, project.name, project.description, + req.query.id, project.avatar_url, project.path_with_namespace) + + res.render(lang+'/account/updateInformation', { + user: loggedInUser, + information: curInformation + }) + } + } + } + } + }) + // update a website + app.post('/updateInformation', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + if (!loggedInUser) { + res.redirect('/login') + } else { + if (!req.body.name && !req.body.description) { + res.flash('error', 'Bitte geben Sie die benötigten Daten ein') + res.redirect('/account/updateInformation') + } else { + let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') + let projectDesc = req.body.description + let updatedInformation = new projectInformation(loggedInUser.getGitlabUserId()!, projectName, projectDesc, req.query.id) + let newLogoFile:any + + async.waterfall([ + function(callback:any){ // upload logo + if(!req.files) { + callback(null, newLogoFile) + } else { + newLogoFile = req.files.logo + newLogoFile.mv(logoDir + newLogoFile.name, function(err:any) { + newLogoFile = logoDir + newLogoFile.name + callback(err, newLogoFile) + }) + } + }, + async function(newLogoFile:any){ // update gitlab page + let updatedPages = await gitlab.updateProject(updatedInformation, newLogoFile) + + if (updatedPages.status) { + if(updatedPages.data.message.name == "has already been taken") { + res.flash("error", "Der Projektname '"+projectName+"' ist bereits vergeben, bitte wählen Sie einen anderen Namen.") + } else { + res.flash("error", "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. ") + } + } else { + updatedInformation.setLogo(updatedPages.avatar_url) + updatedInformation.setPath(updatedPages.path) + res.flash("success", "Ihre Website wurde aktualisiert") + } + + res.redirect('/account/updateInformation?id='+updatedInformation.getId()) + } + ], function (err) { + if(err) console.log(err) + if(newLogoFile){ // remove logo + fs.unlink(newLogoFile, (err) => { + if(err) console.log(err) + }) + } + }) + } + } + } + }) + + app.delete('/deleteProject', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + let projectId = req.body.id + + if (projectId) { + // check if the owner is valid + let project = await gitlab.getProjectById(projectId) + if (!project) { + console.log(" ========= Error or no project found") + } else if (!project.owner) { + console.log(" ========= Project cannot be accessed, since it does not have an owner") + } else if (project.owner.id != loggedInUser.getGitlabUserId()) { + console.log(" ========= Access denied: Not your project") + } else { + let isDeleted = await gitlab.deleteProjectById(projectId) + if (!isDeleted) { + res.flash("error", "Project cannot be deleted. Please try again.") + } + } + } + res.redirect('/account/services') + } + } + }) + +} \ No newline at end of file diff --git a/routes/public.js b/routes/public.js deleted file mode 100644 index e6cd132aafd32c7f576bf6c5cf43b368cc232aca..0000000000000000000000000000000000000000 --- a/routes/public.js +++ /dev/null @@ -1,296 +0,0 @@ -const methods = require('../functions/methods') -const async = require('async') -const mailer = require('../config/mailer') -const constants = require('../config/const') -// pwd encryption -const crypto = require('crypto') -const bcrypt = require('bcryptjs') -const saltRounds = 10 -const salt = 64 - -module.exports = function (app, config, lang) { - - // ================== NEW USERS REGISTRATION ====================== - - app.get('/registration', function(req, res) { - res.render(lang+'/account/registration') - }) - app.post('/registration', function(req, res) { - // user data - var curDate = new Date() - var userData = { - salutation: req.body.inputSalutation, - title: req.body.inputTitle, - firstname: req.body.inputFirstname, - lastname: req.body.inputLastname, - email: req.body.inputEmail, - organisation: req.body.inputOrganisation, - industry: req.body.inputIndustry, - speciality: req.body.inputSpeciality, - createdDate: curDate.toISOString().slice(0,10) - } - - var userEmail = userData.email - var pos = userEmail.indexOf('@') - var emailLength = userEmail.length - var emailDomain = userEmail.slice(pos, emailLength); - - if ( emailDomain.toLowerCase() == "@hft-stuttgart.de") { - res.flash('error', "Fehlgeschlagen: HFT-Account") - res.redirect('/account/registration') - } else { - let token - async.waterfall([ - function(done) { - crypto.randomBytes(20, function(err, buf) { - token = buf.toString('hex'); - done(err, token); - }); - }, - // encrypt password - function(token, done) { - bcrypt.genSalt(saltRounds, function(err, salt) { - bcrypt.hash(req.body.inputPassword, salt, function(err, hash) { - var newAccount = { - profile: userData, - password: hash, - verificationToken: token - } - done(err, newAccount) - }); - }); - }, - // save data - function(newAccount, err) { - methods.registerNewUser(newAccount, function(err){ - if (err) { - res.flash('error', "Fehlgeschlagen") - } - else { - // send email - var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" - var emailContent = '
Lieber Nutzer,

' + - '

vielen Dank für Ihre Anmeldung am Transferportal der HFT Stuttgart.
' + - 'Um Ihre Anmeldung zu bestätigen, klicken Sie bitte diesen Link ' + - '

' + - 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.


' + constants.mailSignature + - '
'; - mailer.options.to = req.body.inputEmail; - mailer.options.subject = emailSubject; - mailer.options.html = emailContent; - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { - console.error('cannot send email') - throw err - } - }) - // user feedback - res.flash('success', 'Vielen Dank für Ihre Registrierung!'+'\r\n\r\n'+ - 'Wir haben Ihnen eine E-Mail an Ihre verwendete Adresse gesendet. Diese enthält einen Link zur Bestätigung Ihres Accounts.'+'\r\n'+ - 'Wenn Sie die Mail nicht in ihrem Postfach vorfinden, prüfen Sie bitte auch Ihren Spam-Ordner.') - } - res.redirect('/account/registration') - }) - } - ]) - } - }) - - // =================== USERS VERIFICATION ========================= - - app.get("/verifyAccount", async function(req, res){ - let userId = await methods.getUserIdByVerificationToken(req.query.token) - if (!userId) { - // no user found - res.render(lang+'/account/verification', { - status: null - }) - } else { - // a user found, verify the account - let userData = { - id: userId, - verificationStatus: 1 - } - methods.verifyUserAccount(userData, async function(err){ - if (err) { - console.log("Error: "+err) - res.render(lang+'/account/verification', { - status: false - }); - } else { - // send welcome email after successful account verification - let userEmail = await methods.getUserEmailById(userId) - if (!userEmail) { - res.render(lang+'/account/verification', { - status: false - }) - } else { - // send email - var emailSubject = "Herzlich willkommen" - var emailContent = '
Lieber Nutzer,

' + - '

herzlich willkommen beim Transferportal der HFT Stuttgart!
' + - 'Sie können nun alle Dienste des Portals nutzen.


' + constants.mailSignature; - mailer.options.to = userEmail - mailer.options.subject = emailSubject - mailer.options.html = emailContent - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { - console.log('cannot send email') - throw err - } - }) - - res.render(lang+'/account/verification', { - status: true - }) - } - } - }) - } - }) - - // ==================== FORGOT PASSWORD =========================== - - app.get('/forgotPwd', function (req, res) { - res.render(lang+'/account/forgotPwd', { - user: req.user - }) - }) - app.post('/forgotPwd', function(req, res) { - let emailAddress = req.body.inputEmail - async.waterfall([ - function(done) { - crypto.randomBytes(20, function(err, buf) { - var token = buf.toString('hex') - done(err, token) - }) - }, - async function(token) { - let user = await methods.checkUserEmail(emailAddress) - if (!user) { - console.log('no user found') - } else { - var emailSubject = "Ihre Passwort-Anfrage an das Transferportal der HFT Stuttgart"; - var emailContent = '

Lieber Nutzer,

' + - '

wir haben Ihre Anfrage zur Erneuerung Ihres Passwortes erhalten. Falls Sie diese Anfrage nicht gesendet haben, ignorieren Sie bitte diese E-Mail.

' + - 'Sie können Ihr Passwort mit dem Klick auf diesen Link ändern: '+config.app.host+'/reset/' + token + '
' + - 'Dieser Link ist aus Sicherheitsgründen nur für 1 Stunde gültig.

' + constants.mailSignature + '
' - - var credentialData = { - user_id: user.id, - resetPasswordToken: token, - resetPasswordExpires: Date.now() + 3600000 // 1 hour - } - methods.updateCredential(credentialData, function(err) { - if (err) { console.error(err) } - }) - - // send email - mailer.options.to = emailAddress - mailer.options.subject = emailSubject - mailer.options.html = emailContent - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { console.error(err) } - }) - } - } - ], function(err) { - if (err) { - res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.') - } - else { - res.flash('success', 'Wenn Ihre E-Mail-Adresse registriert ist, wurde eine E-Mail mit dem weiteren Vorgehen an ' + emailAddress + ' versendet.') - } - res.redirect('/account/forgotPwd') - }) - }) - - // reset - app.get('/reset/:token', async function(req, res) { - let user = await methods.getUserByToken(req.params.token) - if (!user) { - res.flash('error', 'Der Schlüssel zum zurücksetzen des Passworts ist ungültig oder abgelaufen.') - res.redirect('/account/forgotPwd') - } else { - res.render(lang+'/account/reset') - } - }) - app.post('/reset/:token', async function(req, res) { - var newPwd = req.body.inputNewPwd - - let user = await methods.getUserByToken(req.params.token) - if (!user) { - res.flash('error', "User not found.") - res.redirect('/login') - } else { - // encrypt password - bcrypt.genSalt(saltRounds, function(err, salt) { - bcrypt.hash(newPwd, salt, function(err, hash) { - var credentialData = { - password: hash, - user_id: user.user_id - } - // update password - methods.updateCredential(credentialData, function(err){ - if (err) { - res.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") - throw err - } else { - res.flash('success', "Passwort aktualisiert!") - - // send notifiaction email - mailer.options.to = user.email - mailer.options.subject = constants.updatePasswordMailSubject - mailer.options.html = constants.updatePasswordMailContent+'
'+constants.mailSignature+'
' - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { console.log(err) } - }) - - res.redirect('/login') - } - }) - }); - }); - } - - }) - - // ======================= CONTACT FORM =========================== - - app.get('/contact', function (req, res) { - res.render(lang+'/account/contact', { - user: req.user - }) - }) - app.post('/contact', function(req, res, next) { - //methods.currentDate(); - let emailAddress = req.body.inputEmail; - let supportAddress = "support-transfer@hft-stuttgart.de"; - let inputName = req.body.name; - let inputContent = req.body.message; - let emailContent = "Es wurde eine Anfrage an das Transferportal gestellt: \n\n NAME: " + inputName + "\n NACHRICHT:\n "+ inputContent; - let emailSubject = "Ihre Anfrage an das Transferportal"; - async.waterfall([ - function(done) { - // send email - mailer.options.to = supportAddress; - mailer.options.cc = emailAddress; - mailer.options.subject = emailSubject; - mailer.options.text = emailContent; - mailer.transport.sendMail(mailer.options, function(err) { - done(err, 'done'); - }); - } - ], function(err) { - if (err) { - console.error(err) - res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'); - } - else { - res.flash('success', 'Vielen Dank für Ihre Anfrage. Wir melden uns baldmöglichst bei Ihnen. Eine Kopie Ihrer Anfrage wurde an ' + emailAddress + ' versandt.'); - } - res.redirect('/account/contact') - }) - }) - -} \ No newline at end of file diff --git a/routes/public.ts b/routes/public.ts new file mode 100644 index 0000000000000000000000000000000000000000..43b28e8a756df5fa8b942167dff51714e89d476d --- /dev/null +++ b/routes/public.ts @@ -0,0 +1,303 @@ +import async from 'async' +import bcrypt from 'bcryptjs' +import methods from '../functions/methods' +import mailer from '../config/mailer' +import constants from '../config/const' + +const saltRounds:number = 10 +const salt:number = 64 + +export = function (app:any, config:any, lang:string) { + + // ================== NEW USERS REGISTRATION ====================== + app.get('/registration', function(req:any, res:any) { + res.render(lang+'/account/registration') + }) + app.post('/registration', function(req:any, res:any) { + // user data + var curDate:Date = new Date() + var userData:any = { + salutation: req.body.inputSalutation, + title: req.body.inputTitle, + firstname: req.body.inputFirstname, + lastname: req.body.inputLastname, + email: req.body.inputEmail, + organisation: req.body.inputOrganisation, + industry: req.body.inputIndustry, + speciality: req.body.inputSpeciality, + createdDate: curDate.toISOString().slice(0,10) + } + + var userEmail:any = userData.email + var pos:number = userEmail.indexOf('@') + var emailLength:number = userEmail.length + var emailDomain:any = userEmail.slice(pos, emailLength); + + if ( emailDomain.toLowerCase() == "@hft-stuttgart.de") { + res.flash('error', "Fehlgeschlagen: HFT-Account") + res.redirect('/account/registration') + } else { + async.waterfall([ + function(done:any) { + // generate token + let token:string = ''; + let randomChars:string = 'abcdefghijklmnopqrstuvwxyz0123456789'; + for ( let i = 0; i<40; i++ ) { + token += randomChars.charAt(Math.floor(Math.random() * randomChars.length)); + } + // encrypt password + bcrypt.genSalt(saltRounds, function(err, salt) { + bcrypt.hash(req.body.inputPassword, salt, function(err:any, hash:any) { + var newAccount:any = { + profile: userData, + password: hash, + verificationToken: token + } + done(err, newAccount) + }); + }); + }, + // save data + function(newAccount:any, err:any) { + methods.registerNewUser(newAccount, function(err:any){ + if (err) { + res.flash('error', "Fehlgeschlagen") + } + else { + // send email + var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" + var emailContent = '
Lieber Nutzer,

' + + '

vielen Dank für Ihre Anmeldung am Transferportal der HFT Stuttgart.
' + + 'Um Ihre Anmeldung zu bestätigen, klicken Sie bitte diesen Link ' + + '

' + + 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.


' + constants.mailSignature + + '
'; + mailer.options.to = req.body.inputEmail; + mailer.options.subject = emailSubject; + mailer.options.html = emailContent; + mailer.transporter.sendMail(mailer.options, function(err:any) { + if (err) { + console.error('Cannot send email. [Error] '+err) + throw err + } + }) + // user feedback + res.flash('success', 'Vielen Dank für Ihre Registrierung!'+'\r\n\r\n'+ + 'Wir haben Ihnen eine E-Mail an Ihre verwendete Adresse gesendet. Diese enthält einen Link zur Bestätigung Ihres Accounts.'+'\r\n'+ + 'Wenn Sie die Mail nicht in ihrem Postfach vorfinden, prüfen Sie bitte auch Ihren Spam-Ordner.') + } + res.redirect('/account/registration') + }) + } + ]) + } + }) + // to check whether or not an account is already exist + app.get('/email/:email', async function(req:any, res:any) { + let user = await methods.checkUserEmail(req.params.email) + if (!user) { + console.log('No user found: '+req.params.email) + res.send(true) + } else { + console.log('User found: '+req.params.email) + res.send(false) + } + }) + + // =================== USERS VERIFICATION ========================= + + app.get("/verifyAccount", async function(req:any, res:any){ + let userId:number = await methods.getUserIdByVerificationToken(req.query.token) + if (!userId) { + // no user found + res.render(lang+'/account/verification', { + status: null + }) + } else { + // a user found, verify the account + let userData:any = { + id: userId, + verificationStatus: 1 + } + methods.verifyUserAccount(userData, async function(err:any){ + if (err) { + console.log("Error: "+err) + res.render(lang+'/account/verification', { + status: false + }); + } else { + // send welcome email after successful account verification + let userEmail:string = await methods.getUserEmailById(userId) + if (!userEmail) { + res.render(lang+'/account/verification', { + status: false + }) + } else { + // send email + var emailSubject = "Herzlich willkommen" + var emailContent = '
Lieber Nutzer,

' + + '

herzlich willkommen beim Transferportal der HFT Stuttgart!
' + + 'Sie können nun alle Dienste des Portals nutzen.


' + constants.mailSignature; + mailer.options.to = userEmail + mailer.options.subject = emailSubject + mailer.options.html = emailContent + mailer.transporter.sendMail(mailer.options, function(err:any) { + if (err) { + console.log('cannot send email') + throw err + } + }) + + res.render(lang+'/account/verification', { + status: true + }) + } + } + }) + } + }) + + // ==================== FORGOT PASSWORD =========================== + + app.get('/forgotPwd', function (req:any, res:any) { + res.render(lang+'/account/forgotPwd', { + user: req.user + }) + }) + app.post('/forgotPwd', function(req:any, res:any) { + let emailAddress = req.body.inputEmail + async.waterfall([ + async function(done:any) { + let user = await methods.checkUserEmail(emailAddress) + if (!user) { + console.log('No user found: '+emailAddress) + } else { + // generate token + let token:string = ''; + let randomChars:string = 'abcdefghijklmnopqrstuvwxyz0123456789'; + for ( let i = 0; i<40; i++ ) { + token += randomChars.charAt(Math.floor(Math.random() * randomChars.length)); + } + + var emailSubject = "Ihre Passwort-Anfrage an das Transferportal der HFT Stuttgart"; + var emailContent = '

Lieber Nutzer,

' + + '

wir haben Ihre Anfrage zur Erneuerung Ihres Passwortes erhalten. Falls Sie diese Anfrage nicht gesendet haben, ignorieren Sie bitte diese E-Mail.

' + + 'Sie können Ihr Passwort mit dem Klick auf diesen Link ändern: '+config.app.host+'/reset/' + token + '
' + + 'Dieser Link ist aus Sicherheitsgründen nur für 1 Stunde gültig.

' + constants.mailSignature + '
' + + var credentialData = { + user_id: user.id, + resetPasswordToken: token, + resetPasswordExpires: Date.now() + 3600000 // 1 hour + } + let result = await methods.updateCredential(credentialData) + if (!result) { + console.log('failed to update credential') + } else { + // send email + mailer.options.to = emailAddress + mailer.options.subject = emailSubject + mailer.options.html = emailContent + mailer.transporter.sendMail(mailer.options, function(err:any) { + if (err) { console.error(err) } + }) + } + } + done(null) + } + ], function(err:any) { + if (err) { + res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.') + } + else { + res.flash('success', 'Wenn Ihre E-Mail-Adresse registriert ist, wurde eine E-Mail mit dem weiteren Vorgehen an ' + emailAddress + ' versendet.') + } + res.redirect('/account/forgotPwd') + }) + }) + + // reset + app.get('/reset/:token', async function(req:any, res:any) { + let user = await methods.getUserByToken(req.params.token) + if (!user) { + res.flash('error', 'Der Schlüssel zum zurücksetzen des Passworts ist ungültig oder abgelaufen.') + res.redirect('/account/forgotPwd') + } else { + res.render(lang+'/account/reset') + } + }) + app.post('/reset/:token', async function(req:any, res:any) { + var newPwd = req.body.inputNewPwd + + var user = await methods.getUserByToken(req.params.token) + if (!user) { + res.flash('error', "User not found.") + res.redirect('/login') + } else { + // encrypt password + bcrypt.genSalt(saltRounds, function(err, salt) { + bcrypt.hash(newPwd, salt, async function(err:any, hash) { + var credentialData = { + password: hash, + user_id: user.user_id + } + // update password + let result = await methods.updateCredential(credentialData) + if (!result) { + console.log('Failed to reset password') + res.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") + } else { + res.flash('success', "Passwort aktualisiert!") + // send notifiaction email + mailer.options.to = user.email + mailer.options.subject = constants.updatePasswordMailSubject + mailer.options.html = constants.updatePasswordMailContent+'
'+constants.mailSignature+'
' + mailer.transporter.sendMail(mailer.options, function(err:any) { + if (err) { console.log(err) } + }) + } + res.redirect('/login') + }); + }); + } + + }) + + // ======================= CONTACT FORM =========================== + app.get('/contact', function (req:any, res:any) { + res.render(lang+'/account/contact', { + user: req.user + }) + }) + app.post('/contact', function(req:any, res:any, next:any) { + //methods.currentDate(); + let emailAddress = req.body.inputEmail; + let supportAddress = "support-transfer@hft-stuttgart.de"; + let inputName = req.body.name; + let inputContent = req.body.message; + let emailContent = "Es wurde eine Anfrage an das Transferportal gestellt: \n\n NAME: " + inputName + "\n NACHRICHT:\n "+ inputContent; + let emailSubject = "Ihre Anfrage an das Transferportal"; + async.waterfall([ + function(done:any) { + // send email + mailer.options.to = supportAddress; + mailer.options.cc = emailAddress; + mailer.options.subject = emailSubject; + mailer.options.text = emailContent; + mailer.transporter.sendMail(mailer.options, function(err:any) { + done(err, 'done'); + }); + } + ], function(err:any) { + if (err) { + console.error(err) + res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'); + } + else { + res.flash('success', 'Vielen Dank für Ihre Anfrage. Wir melden uns baldmöglichst bei Ihnen. Eine Kopie Ihrer Anfrage wurde an ' + emailAddress + ' versandt.'); + } + res.redirect('/account/contact') + }) + }) + +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..1519fee76e91ea94764d0ac22eec6f766f63686e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "rootDir": "./", + "outDir": "./built", + "esModuleInterop": true, + "strict": true, + "allowJs": true + } +} \ No newline at end of file diff --git a/views/DE/404.pug b/views/DE/404.pug index 5a16f0863287e4c4728d22cb6b594203d1fd9edc..29a3cd86e08627a0059a1bc7abc132fe3fe3221f 100644 --- a/views/DE/404.pug +++ b/views/DE/404.pug @@ -21,8 +21,8 @@ html(lang="de") body div(class="container") div(class="center", align="center") - a(href="https://m4lab.hft-stuttgart.de") - img(src="https://transfer.hft-stuttgart.de/images/demo/m4lab_logo.jpg", class="img-responsive center-block", width="185", height="192") + a(href="/") + img(src="/images/demo/m4lab_logo.jpg", class="img-responsive center-block", width="185", height="192") br br p(class="h5") 404. The requested URL was not found. diff --git a/views/DE/500.pug b/views/DE/500.pug index aea0e76c327decde492f063feeac5a7a364a9ac4..20dbfa9d1098bf6ba18d64c37253b3a94f83788b 100644 --- a/views/DE/500.pug +++ b/views/DE/500.pug @@ -21,8 +21,8 @@ html(lang="de") body div(class="container") div(class="center", align="center") - a(href="https://m4lab.hft-stuttgart.de") - img(src="https://transfer.hft-stuttgart.de/images/demo/m4lab_logo.jpg", class="img-responsive center-block", width="185", height="192") + a(href="/") + img(src="/images/demo/m4lab_logo.jpg", class="img-responsive center-block", width="185", height="192") br br p(class="h5") 500. Unexpected Error :( diff --git a/views/DE/account/newInformation.pug b/views/DE/account/newInformation.pug index 543d456d3f078664bc8e843d1823cfc5bef33d53..21788f6a514d7acad69f8115ab327cf0d3c16a07 100644 --- a/views/DE/account/newInformation.pug +++ b/views/DE/account/newInformation.pug @@ -76,7 +76,7 @@ html(lang="de") label(for="logo", class="col-sm-2") Projektlogo div(class="col-sm-8") div(class="form-group row px-4") - - let defaultLogo = "https://m4lab.hft-stuttgart.de/img/footer/M4_LAB_LOGO_Graustufen.png" + - let defaultLogo = "/img/footer/M4_LAB_LOGO_Graustufen.png" img(src=defaultLogo, width="100" height="100") div(class="form-group row px-3") input#logo(name="logo", class="form-control-file", type="file") diff --git a/views/DE/account/updateInformation.pug b/views/DE/account/updateInformation.pug index f8318ee0076fb6e6b76d9cc3ff92e221583e9b3b..58cd8a42bbbda6f773864ea1cbab81e87abed816 100644 --- a/views/DE/account/updateInformation.pug +++ b/views/DE/account/updateInformation.pug @@ -85,7 +85,7 @@ html(lang="de") li Klicken Sie hier, um Ihre index.html in GitLab zu öffnen. li Bearbeiten Sie ihre Datei. li Um die Änderungen zu speichern und auf ihrer Seite sofort zu übernehmen, klicken Sie auf Commit changes - img(src="https://transfer.hft-stuttgart.de/img/help/save_file.png", class="img-fluid", style="border: 1px solid gray;", alt="index.html") + img(src="/img/help/save_file.png", class="img-fluid", style="border: 1px solid gray;", alt="index.html") li Sobald Sie Änderungen an Ihrer index.html vornehmen, wird Ihre Website veröffentlicht. div(class="card-header") div(class="card-title") @@ -95,21 +95,21 @@ html(lang="de") li Klicken Sie settings.js, um Ihre settings.js in GitLab zu öffnen. li Bearbeiten Sie ihre Datei. li Hier sehen Sie die Standardwerde für Soziale Netzwerke und persönliche Webseiten eines Teilnehmers sowie den Standardavatar. Es wird empfohlen, diese Werte nicht zu ändern, aber Sie können weitere Soziale Netzwerke hinzufügen. - img(src="https://transfer.hft-stuttgart.de/img/help/default_settings.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/default_settings.png", class="img-fluid", style="border: 1px solid gray;") li Diese Schalter kontrollieren, welche Teile der gitlab-Seite angezeigt werden sollen. Wenn Sie also beispielsweise nur eine einzige Seite haben, benötigen Sie kein Menü und können den Wert für 'menu' auf OFF stellen. - img(src="https://transfer.hft-stuttgart.de/img/help/switches.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/switches.png", class="img-fluid", style="border: 1px solid gray;") li Hier ändern Sie das Projektlogo. Das Logo wird am oberen Rand der Seite mittig angezeigt. Wenn der Schalter für 'project logo' auf OFF steht, wird es nicht angezeigt. - img(src="https://transfer.hft-stuttgart.de/img/help/pr_logo.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/pr_logo.png", class="img-fluid", style="border: 1px solid gray;") li Hier ändern Sie das Menü Ihrer gitlab-Seite. Ein Menü kann entweder auf einen Unterordner/ template verweisen oder aber auf einen externen Link, z.B. eine Demo. Sie können Menüeinträge hinzufügen oder entfernen. Das Menü wird mit dem Schalter 'OFF' verborgen. Vergessen Sie nicht den Schrägstrich am Ende eines Menülinks, wenn dieser auf einen Ordner zeigt. - img(src="https://transfer.hft-stuttgart.de/img/help/menu.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/menu.png", class="img-fluid", style="border: 1px solid gray;") li Hier ändern Sie die Teilnehmenden. Sie können die Standardwerte für Soziale Netzwerke (diese beinhalten die HFT-Kanäle) oder Ihr eigenen Profile verwenden. Sie können auf Ihre persönliche Webseite verlinken. Sie können soziale Netzwerke hinzufügen oder entfernen. Sie können auch einen persönlichen Avatar oder den Standard-Avatar (DEFAULT.avatar) verwenden. - img(src="https://transfer.hft-stuttgart.de/img/help/partic.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/partic.png", class="img-fluid", style="border: 1px solid gray;") li Hier ist ein Beispiel mit zwei Teilnehmenden: - img(src="https://transfer.hft-stuttgart.de/img/help/partic2.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/partic2.png", class="img-fluid", style="border: 1px solid gray;") li Hier ändern Sie die Fußzeilenlogos z.B. zu denen von Projektpartnern. Wenn Sie das Logo nicht mit einer externen Webseite verlinken wollen, verwenden Sie EMPTY_LINK als Wert für href. Der Titel title wird bei Mouse-Hover über dem Logo erscheinen. - img(src="https://transfer.hft-stuttgart.de/img/help/f_logos.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/f_logos.png", class="img-fluid", style="border: 1px solid gray;") li Klicken Sie anschließend auf Commit changes, um die Änderungen zu speichern. - img(src="https://transfer.hft-stuttgart.de/img/help/edit_settings_generic.png", class="img-fluid", style="border: 1px solid gray;") + img(src="/img/help/edit_settings_generic.png", class="img-fluid", style="border: 1px solid gray;") hr div(class="mx-4") diff --git a/views/DE/layout.pug b/views/DE/layout.pug deleted file mode 100644 index 32d27e01d25d57838d7c44edf1de5f54b7568fdf..0000000000000000000000000000000000000000 --- a/views/DE/layout.pug +++ /dev/null @@ -1,12 +0,0 @@ -doctype html -html - head - title PassportJS SAML example - block links - link(rel='stylesheet', href='bower_components/bootstrap/dist/css/bootstrap.css') - body - div.container - block content - script(src='bower_components/jquery/dist/jquery.min.js') - script(src='bower_components/bootstrap/dist/js/bootstrap.min.js') - block scripts