-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathapp.js
executable file
·151 lines (138 loc) · 4.35 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env node
// https://stackoverflow.com/questions/7697038/more-than-10-lines-in-a-node-js-stack-error
Error.stackTraceLimit = Infinity
const bodyParser = require('body-parser')
const cors = require('cors')
const express = require('express')
const morgan = require('morgan')
const next = require('next')
const passport = require('passport')
const passport_local = require('passport-local')
const session = require('express-session')
const api = require('./api')
const lib = require('./lib')
const models = require('./models')
const config = require('./front/config')
/**
* - port: 0 means find random empty port
* - startNext: if false, don't start Next.js, run just the API
**/
async function start(port, startNext, cb) {
const app = express()
let nextApp
let nextHandle
if (startNext) {
nextApp = next({ dev: !config.isProductionNext })
nextHandle = nextApp.getRequestHandler()
}
const sequelize = models.getSequelize(__dirname)
app.set('sequelize', sequelize)
passport.use(
new passport_local.Strategy(
{
usernameField: 'user[email]',
passwordField: 'user[password]',
},
function (email, password, done) {
sequelize.models.User.findOne({ where: { email: email } })
.then(function (user) {
if (!user || !sequelize.models.User.validPassword(user, password)) {
return done(null, false, {
errors: { 'email or password': 'is invalid' },
})
}
return done(null, user)
})
.catch(done)
}
)
)
app.use(cors())
// Normal express config defaults
if (config.verbose) {
// https://stackoverflow.com/questions/42099925/logging-all-requests-in-node-js-express/64668730#64668730
app.use(morgan('combined'))
}
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(require('method-override')())
// Next handles anything outside of /api.
app.get(new RegExp('^(?!' + config.apiPath + '(/|$))'), function (req, res) {
// We pass the sequelize that we have already created and connected to the database
// so that the Next.js backend can just use that connection. This is in particular mandatory
// if we wish to use SQLite in-memory database, because there is no way to make two separate
// connections to the same in-memory database. In memory databases are used by the test system.
req.sequelize = sequelize
return nextHandle(req, res)
})
app.use(
session({
secret: config.secret,
cookie: { maxAge: 60000 },
resave: false,
saveUninitialized: false,
})
)
// Handle API routes.
{
const router = express.Router()
router.use(config.apiPath, api)
app.use(router)
}
// 404 handler.
app.use(function (req, res) {
res.status(404).json('error: 404 Not Found')
})
// Error handlers
app.use(function (err, req, res, next) {
// Automatiaclly handle Sequelize validation errors.
if (err instanceof sequelize.Sequelize.ValidationError) {
if (!config.isProduction) {
// The fuller errors can be helpful during development.
console.error(err)
}
const errors = {}
for (let errItem of err.errors) {
let errorsForColumn = errors[errItem.path]
if (errorsForColumn === undefined) {
errorsForColumn = []
errors[errItem.path] = errorsForColumn
}
errorsForColumn.push(errItem.message)
}
return res.status(422).json({ errors })
} else if (err instanceof lib.ValidationError) {
return res.status(err.status).json({
errors: err.errors,
})
}
return next(err)
})
if (startNext) {
await nextApp.prepare()
}
await sequelize.authenticate()
// Just a convenience DB create so we don't have to force new users to do it manually.
await models.sync(sequelize)
return new Promise((resolve, reject) => {
const server = app.listen(port, async function () {
try {
cb && (await cb(server))
} catch (e) {
reject(e)
this.close()
throw e
}
})
server.on('close', async function () {
await sequelize.close()
resolve()
})
})
}
if (require.main === module) {
start(config.port, true, (server) => {
console.log('Listening on: http://localhost:' + server.address().port)
})
}
module.exports = { start }