-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.js
331 lines (300 loc) · 8.75 KB
/
server.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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/*
server.js
Owen Gallagher
25 july 2019
*/
try {
if (require('dotenv').config().error) {
throw 'environment variables not loaded from .env'
}
else {
console.log('environment variables loaded from .env')
}
// logging
const temp_logger = require('temp_js_logger')
temp_logger.config({
level: 'debug',
with_timestamp: true,
name: 'server',
with_lineno: true,
parse_level_prefix: true,
with_level: true,
with_always_level_name: false,
with_cli_colors: true,
log_to_file: true
})
.then(temp_logger.imports_promise)
.then(() => {
main(temp_logger)
})
}
catch (err) {
console.log(
`error make sure you run the (npm install) command to get needed node modules first\n${err.stack}`
)
process.exit(1)
}
function main(temp_logger) {
//web server
const express = require('express')
const app = express()
//compression
const brotli_compress = require('brotli/compress') //use brotli to compress large cacheserver entries
app.use(require('compression')({
level: 5 //from 0 (fastest) to 9 (smallest)
})) //use gzip/deflate to compress HTTP request responses
//handle POST request data with bodyparser
const bodyparser = require('body-parser')
app.use(bodyparser.json())
app.use(bodyparser.urlencoded({
extended: false,
limit: '50mb'
}))
//local libraries
const enums = require('./enums')
const dbserver = require('./db/dbserver')
const sessionserver = require('./sessionserver')
const emailserver = require('./email/emailserver')
const SITE = enums.site.TEXTILES; //select database to connect to
app.set('port', process.env.PORT)
//enable cross-origin requests for same origin html imports
const cors = require('cors')
const origins = [
'https://localhost', //local testing (same device)
'http://localhost', //local testing (same device; http)
'https://textilesjournal.org', //english site domain
'http://textilesjournal.org', //english site domain (http)
'https://www.textilesjournal.org', //english site subdomain (www)
'http://www.textilesjournal.org' //english site subdomain (www; http)
]
app.use(cors({
origin: function(origin,callback) {
if (origin != null && origins.indexOf(origin) == -1) {
return callback(new Error('CORS for origin ' + origin + ' is not allowed access.'), false)
}
else {
return callback(null,true)
}
}
}))
//serve the website from public/
app.use(express.static('public'))
function on_start() {
let site_name = 'textiles'
if (SITE == enums.site.TEJOS) {
site_name = 'tejos'
}
console.log('info ' + site_name + ' server is running at <host>:' + app.get('port'))
console.log('info connecting to database')
dbserver.init(SITE)
console.log('info enabling sessions')
sessionserver.init()
console.log('info enabling email notifications')
emailserver
.init()
.then(function() {
console.log('info email server initialized')
})
.catch(function() {
console.log('error email server failed')
})
}
if (app.get('port') == 443) {
//https server
const fs = require('fs')
const PATH_HTTPS = process.env.PATH_HTTPS
try {
require('https').createServer({
key: fs.readFileSync(PATH_HTTPS + 'privkey.pem'),
cert: fs.readFileSync(PATH_HTTPS + 'cert.pem'),
ca: fs.readFileSync(PATH_HTTPS + 'fullchain.pem')
}, app)
.listen(app.get('port'), on_start)
}
catch (err) {
console.log(`error https server needs root permissions to run\n${err.stack}`)
}
}
else {
//http server
app.listen(app.get('port'), on_start)
}
function handle_db(endpoint,args,res) {
dbserver
.get_query(endpoint, args, true)
.then(function(action) {
if (action.cached) {
res.json(action.cached)
}
else if (action.sql) {
dbserver.send_query(action.sql, function(err,data) {
if (err) {
console.log('error error in db data fetch: ' + err)
res.json({error: 'fetch error'})
}
else {
res.json(data)
}
})
}
else {
res.json({error: action})
}
})
.catch(function(problem) {
console.log('error error in conversion from endpoint to sql: ' + problem)
res.json({error: 'endpoint error'})
})
}
//expose database
app
.route('/db')
.get(function (req,res) {
let endpoint = req.query.endpoint //db api endpoint
let args = req.query.args //inputs for compiled sql string
handle_db(endpoint,args,res)
})
.post(function (req,res) {
let endpoint = req.body.endpoint //db api endpoint
let args = req.body.args //inputs for compiled sql string
handle_db(endpoint,args,res)
})
//expose sessions
app
.route('/sessions')
.post(function (req,res) {
let endpoint = req.body.endpoint
let args = req.body['args[]']
sessionserver.handle_request(endpoint, args, dbserver)
.then(function(data) {
if ((endpoint == sessionserver.ENDPOINT_CREATE && data.register) ||
endpoint == sessionserver.ENDPOINT_REQUEST_ACTIVATE) {
console.log('info requesting activation')
//args = [username, password, session_id, email, subscribed]
//create activation code
sessionserver
.request_activate(data.username)
.then(function(activation_code) {
res.json({success: data})
//send registration/activation email
emailserver.email(data.email, emailserver.EMAIL_REGISTER, {
username: data.username,
subscribed: data.subscribed,
activation_code: activation_code
})
})
.catch(function() {
res.json({error: 'activation'})
})
}
else if (endpoint == sessionserver.ENDPOINT_RESET_PASSWORD) {
if (!args[3]) {
//new password not provided; requesting reset, not completing reset
let reset_code = data
//send password reset email
emailserver.email(args[2], emailserver.EMAIL_PASSWORD_RESET, {
username: args[1],
reset_code: reset_code
})
}
res.json({success: true})
}
else {
res.json({success: data})
}
})
.catch(function(err) {
/*
Session server status codes are used internally, but converted to strings when passed to
the client.
*/
if (err == sessionserver.STATUS_CREATE_ERR) {
res.json({error: 'create'})
}
else if (err == sessionserver.STATUS_NO_SESSION) {
res.json({error: 'null'})
}
else if (err == sessionserver.STATUS_EXPIRE || err == sessionserver.STATUS_NO_ACTIVATION) {
if (endpoint == sessionserver.ENDPOINT_ACTIVATE) {
//args = [session_id, username, activation_code]
//get dest email
let username = args[1]
dbserver.get_query('fetch_user',[username])
.then(function(action) {
dbserver.send_query(action.sql, function(err, data) {
if (err) {
console.log('error user ' + username + ' not found in db')
}
else {
let account_info = data[0]
let dest_email = account_info.email
let subscribed = (account_info.subscription[0] == 1)
//send new activation code
sessionserver
.request_activate(username)
.then(function(activation_code) {
//send new activation email
emailserver.email(dest_email, emailserver.EMAIL_REGISTER, {
username: username,
subscribed: subscribed,
activation_code: activation_code
})
})
.catch(function() {
console.log('error unable to create new activation code for ' + username)
})
}
})
})
.catch(function(err) {
console.log('error unable to find db --> fetch_user')
})
}
res.json({error: 'expired'})
}
else if (err == sessionserver.STATUS_LOGIN_WRONG) {
res.json({error: 'login'})
}
else if (err == sessionserver.STATUS_DB_ERR) {
res.json({error: 'db'})
}
else if (err == sessionserver.STATUS_DELETE_ERR) {
res.json({error: 'delete'})
}
else if (err == sessionserver.STATUS_ACTIVATION) {
res.json({error: 'activation'})
}
else if (err == sessionserver.STATUS_XSS_ERR) {
res.json({error: 'xss'})
}
else if (err == sessionserver.STATUS_NO_PLAY) {
res.json({error: 'no_play'})
}
else if (err == sessionserver.STATUS_RESET) {
res.json({error: 'reset'})
}
else {
res.json({error: 'endpoint'})
console.log(err)
}
})
})
app
.route('/email')
.post(function(req,res) {
emailserver
.email(emailserver.TJ_EMAIL, emailserver.EMAIL_CUSTOM, req.body)
.then(function(err) {
if (err) {
res.json({error: 'email'})
}
else {
res.json({success: 'email'})
}
})
})
//for enabling https by getting ssl cert from certbot
app.get('/.well-known/acme-challenge/:content', function(req,res) {
res.send(process.env.CERTBOT_DOMAIN_AUTH)
})
}