-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
228 lines (204 loc) · 7.91 KB
/
index.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
'use strict'
const express = require('express')
const session = require('express-session')
const next = require('next')
const smtpTransport = require('nodemailer-smtp-transport')
const directTransport = require('nodemailer-direct-transport')
const MongoClient = require('mongodb').MongoClient
const MongoStore = require('connect-mongo')(session)
const NeDB = require('nedb') // Use MongoDB work-a-like if no user db configured
const cookieParser = require('cookie-parser')
const routes = {
admin: require('./routes/admin'),
auth: require('./routes/auth')
}
// Load environment variables from .env file if present
//require('dotenv').load()
require('dotenv').config()
// now-logs allows remote debugging if deploying to now.sh
if (process.env.LOGS_SECRET) {
require('now-logs')(process.env.LOGS_SECRET)
}
process.on('uncaughtException', function(err) {
console.error('Uncaught Exception: ', err)
})
process.on('unhandledRejection', (reason, p) => {
console.error('Unhandled Rejection: Promise:', p, 'Reason:', reason)
})
// Default when run with `npm start` is 'production' and default port is '80'
// `npm run dev` defaults mode to 'development' & port to '3000'
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
process.env.PORT = process.env.PORT || 80
// Define the session secret (should be unique to your site)
process.env.SESSION_SECRET = process.env.SESSION_SECRET || 'change-me'
// If EMAIL_USERNAME and EMAIL_PASSWORD are configured use them to send email.
// If you don't specify an email server then email will be sent from localhost
// which is less reliable than using a configured mail server.
let mailserver = directTransport()
if (process.env.EMAIL_SERVER && process.env.EMAIL_USERNAME && process.env.EMAIL_PASSWORD) {
mailserver = smtpTransport({
host: process.env.EMAIL_SERVER,
port: process.env.EMAIL_PORT || 25,
secure: (process.env.EMAIL_SECURE && process.env.EMAIL_SECURE.match(/true/i)) ? true : false,
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD
}
})
}
const expressApp = express()
const nextApp = next({
dir: '.',
dev: (process.env.NODE_ENV === 'development')
})
// We use cookie-parser to parse cookies and populate req.cookies in express
// (this makes cookies easier to work with in pages when rendering server side)
expressApp.use(cookieParser())
let userdb, sessionStore
nextApp.prepare()
.then(() => {
// Connect to the user database
return new Promise((resolve, reject) => {
if (process.env.USER_DB_CONNECTION_STRING) {
// Example connection string: mongodb://localhost:27017/my-user-db
MongoClient.connect(process.env.USER_DB_CONNECTION_STRING, (err, db) => {
userdb = db.collection('users')
resolve(true)
})
} else {
// If no user db connection string, use in-memory MongoDB work-a-like
console.warn("Warning: No user database connection string configured (using in-memory database, user data will not be persisted)")
userdb = new NeDB({ autoload: true })
userdb.loadDatabase((err) => {
if (err) return reject(err)
resolve(true)
})
}
})
})
.then(() => {
// Configure a session store and connect it to the session database
return new Promise((resolve, reject) => {
if (process.env.SESSION_DB_CONNECTION_STRING) {
sessionStore = new MongoStore({
url: process.env.SESSION_DB_CONNECTION_STRING,
autoRemove: 'interval',
autoRemoveInterval: 10, // Removes expired sessions every 10 minutes
collection: 'sessions',
stringify: false
})
resolve(true)
} else {
// If no session db connection string, use in-memory MongoDB work-a-like
console.warn("Warning: No session database connection string configured (using in-memory session store, session data will not be persisted)")
sessionStore = new session.MemoryStore()
resolve(true)
}
})
})
.then(() => {
// Once DB connections are available, can configure authentication routes
routes.auth.configure({
nextApp: nextApp,
expressApp: expressApp,
userdb: userdb,
session: session,
store: sessionStore,
secret: process.env.SESSION_SECRET,
mailserver: mailserver,
fromEmail: process.env.EMAIL_ADDRESS || null,
serverUrl: process.env.SERVER_URL || null
})
// Add admin routes
routes.admin.configure({
expressApp: expressApp,
userdb: userdb
})
// Serve fonts from ionicon npm module
expressApp.use('/fonts/ionicons', express.static('./node_modules/ionicons/dist/fonts'))
// A simple example of custom routing
//
// Send requests for '/custom-route/{anything}' to 'pages/examples/routing.js'
expressApp.get('/custom-route/:id', (req, res) => {
return nextApp.render(req, res, '/examples/routing')
})
//
// Requests to just '/custom-route' will redirect to '/custom-route/example'
// (which will trigger the route handling above)
expressApp.get('/custom-route', (req, res) => {
return res.redirect('/custom-route/example')
})
// Expose a route to return user profile if logged in with a session
expressApp.get('/account/user', (req, res) => {
if (req.user) {
userdb.findOne({'_id': req.user.id}, (err, user) => {
if (err || !user)
return res.status(500).json({error: 'Unable to fetch profile'})
res.json({
name: user.name,
email: user.email,
emailVerified: (user.emailVerified && user.emailVerified === true) ? true : false,
linkedWithFacebook: (user.facebook && user.facebook.id) ? true : false,
linkedWithGoogle: (user.google && user.google.id) ? true : false,
linkedWithTwitter: (user.twitter && user.twitter.id) ? true : false
})
})
} else {
return res.status(403).json({error: 'Must be signed in to get profile'})
}
})
// Expose a route to allow users to update their profiles (name, email)
expressApp.post('/account/user', (req, res) => {
if (req.user) {
userdb.findOne({'_id': req.user.id}, (err, user) => {
if (err || !user)
return res.status(500).json({error: 'Unable to fetch profile'})
if (req.body.name)
user.name = req.body.name
if (req.body.email) {
// Reset email verification field if email address has changed
if (req.body.email && req.body.email !== user.email)
user.emailVerified = false
user.email = req.body.email
}
userdb.update({'_id': user._id}, user, {}, (err) => {
if (err)
return res.status(500).json({error: 'Unable save changes to profile'})
return res.status(204).redirect('/account')
})
})
} else {
return res.status(403).json({error: 'Must be signed in to update profile'})
}
})
// Expose a route to allow users to delete their profile.
expressApp.post('/account/delete', (req, res) => {
if (req.user) {
userdb.remove({'_id': req.user.id}, (err, user) => {
if (err || !user)
return res.status(500).json({error: 'Unable to delete profile'})
// When the account has been deleted, redirect client to /auth/callback
// to ensure the client has it's local session state updated to reflect
// that the user is no longer logged in.
return res.status(204).redirect('/auth/callback')
})
} else {
return res.status(403).json({error: 'Must be signed in to delete profile'})
}
})
// Default catch-all handler to allow Next.js to handle all other routes
expressApp.all('*', (req, res) => {
let nextRequestHandler = nextApp.getRequestHandler()
return nextRequestHandler(req, res)
})
expressApp.listen(process.env.PORT, err => {
if (err) {
throw err
}
console.log('> Ready on http://localhost:' + process.env.PORT + ' [' + process.env.NODE_ENV + ']')
})
})
.catch(err => {
console.log('An error occurred, unable to start the server')
console.log(err)
})