From 646fe6ac141dfd88f1f9559f175047172536dad4 Mon Sep 17 00:00:00 2001 From: Robbie Antenesse Date: Thu, 10 Jan 2019 15:20:48 -0700 Subject: [PATCH] Move routing to separate files --- routes/get_about.js | 12 ++ routes/get_give.js | 10 ++ routes/get_history.js | 59 +++++++ routes/get_home.js | 66 +++++++ routes/get_tools.js | 56 ++++++ routes/middleware.js | 43 +++++ routes/post_give.js | 61 +++++++ routes/post_tools.js | 59 +++++++ routes/socketio.js | 29 ++++ server.js | 394 +++--------------------------------------- 10 files changed, 416 insertions(+), 373 deletions(-) create mode 100644 routes/get_about.js create mode 100644 routes/get_give.js create mode 100644 routes/get_history.js create mode 100644 routes/get_home.js create mode 100644 routes/get_tools.js create mode 100644 routes/middleware.js create mode 100644 routes/post_give.js create mode 100644 routes/post_tools.js create mode 100644 routes/socketio.js diff --git a/routes/get_about.js b/routes/get_about.js new file mode 100644 index 0000000..130eda4 --- /dev/null +++ b/routes/get_about.js @@ -0,0 +1,12 @@ +module.exports = function (app) { + app.server.get('/about', (req, res) => { + const resourcePath = (req.url.substr(-1) === '/' ? '../' : './'); + const body = app.fillTemplate('./templates/pages/about.html', { resourcePath }); + const html = app.fillTemplate('./templates/htmlContainer.html', { title: 'About', body }); + if (html) { + res.send(html); + } else { + res.send('Something went wrong!'); + } + }); +} \ No newline at end of file diff --git a/routes/get_give.js b/routes/get_give.js new file mode 100644 index 0000000..ad93bc1 --- /dev/null +++ b/routes/get_give.js @@ -0,0 +1,10 @@ +module.exports = function (app) { + app.server.get('/give', (req, res) => { + const resourcePath = (req.url.substr(-1) === '/' ? '../' : './'); + let body = app.fillTemplate('./templates/pages/uploadForm.html', { resourcePath }); + body = app.replaceBodyWithTooManyBooksWarning(body); + + const html = app.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body }); + res.send(html); + }); +} \ No newline at end of file diff --git a/routes/get_history.js b/routes/get_history.js new file mode 100644 index 0000000..a743183 --- /dev/null +++ b/routes/get_history.js @@ -0,0 +1,59 @@ +const path = require('path'); +const fs = require('fs'); +const snarkdown = require('snarkdown'); +const fecha = require('fecha'); + +module.exports = function (app) { + app.server.get('/history', (req, res) => { + const files = fs.readdirSync(app.historyLocation).filter(fileName => fileName.includes('.json')) + .map(fileName => { // Cache the file data so sorting doesn't need to re-check each file + return { name: fileName, time: fs.statSync(path.resolve(app.historyLocation, fileName)).mtime.getTime() }; + }).sort((a, b) => b.time - a.time).map(v => v.name); // Sort from newest to oldest. + + let history = files.map(fileName => { + const bookData = JSON.parse(fs.readFileSync(path.resolve(app.historyLocation, fileName), 'utf8')); + bookData.author = bookData.author ? bookData.author : 'author not provided'; + bookData.contributor = bookData.contributor ? bookData.contributor : 'Anonymous'; + const id = fileName.replace('.json', ''); + const added = fecha.format(new Date(bookData.added), 'hh:mm:ssA on dddd MMMM Do, YYYY'); + const removed = fecha.format(new Date(parseInt(id)), 'hh:mm:ssA on dddd MMMM Do, YYYY'); + const removedTag = '
Taken' + removed + '
'; + const modal = app.fillTemplate('./templates/elements/modalCard.html', { + id, + header: '

' + bookData.title + '

' + bookData.author + '

', + content: app.fillTemplate('./templates/elements/bookInfo.html', { + contributor: bookData.contributor, + fileFormat: bookData.fileType, + added, + removedTag, + summary: snarkdown(bookData.summary), + }), + footer: 'Close', + }); + return app.fillTemplate('./templates/elements/book.html', { + id, + title: bookData.title, + author: bookData.author, + fileType: bookData.fileType, + modal, + }); + }).join(''); + + if (history == '') { + history = '
No books have been taken yet. Would you like to take a book?
'; + } + + const body = '

History

' + history + '
'; + const html = app.fillTemplate('./templates/htmlContainer.html', { + title: 'History', + resourcePath: (req.url.substr(-1) === '/' ? '../' : './'), + body + }); + + if (html) { + res.send(html); + } else { + res.send('Something went wrong!'); + } + }); +} \ No newline at end of file diff --git a/routes/get_home.js b/routes/get_home.js new file mode 100644 index 0000000..43bada1 --- /dev/null +++ b/routes/get_home.js @@ -0,0 +1,66 @@ +const path = require('path'); +const fs = require('fs'); +const snarkdown = require('snarkdown'); +const fecha = require('fecha'); + +module.exports = function (app) { + app.server.get('/', (req, res) => { + const files = fs.readdirSync(app.fileLocation).filter(fileName => fileName.includes('.json')) + .map(fileName => { // Cache the file data so sorting doesn't need to re-check each file + return { name: fileName, time: fs.statSync(path.resolve(app.fileLocation, fileName)).mtime.getTime() }; + }).sort((a, b) => a.time - b.time).map(v => v.name); // Sort from oldest to newest. + + let books = files.map(fileName => { + const bookData = JSON.parse(fs.readFileSync(path.resolve(app.fileLocation, fileName), 'utf8')); + if (bookData.hasOwnProperty('fileName')) return ''; + bookData.author = bookData.author ? bookData.author : 'author not provided'; + bookData.contributor = bookData.contributor ? bookData.contributor : 'Anonymous'; + + const id = fileName.replace('.json', ''); + const confirmId = 'confirm_' + id; + const added = fecha.format(new Date(bookData.added), 'hh:mm:ssA on dddd MMMM Do, YYYY'); + const modal = app.fillTemplate('./templates/elements/modalCard.html', { + id, + header: '

' + bookData.title + '

' + bookData.author + '

', + content: app.fillTemplate('./templates/elements/bookInfo.html', { + contributor: bookData.contributor, + fileFormat: bookData.fileType, + added, + summary: snarkdown(bookData.summary), + }) + + app.fillTemplate('./templates/elements/modal.html', { + id: confirmId, + content: app.fillTemplate('./templates/elements/messageBox.html', { + header: 'Download Your Book', + message: app.fillTemplate('./templates/elements/takeConfirm.html', { id }), + }), + }), + footer: 'Close Take Book', + }); + return app.fillTemplate('./templates/elements/book.html', { + id, + title: bookData.title, + author: bookData.author, + fileType: bookData.fileType, + modal, + }); + }).join(''); + + if (books == '') { + books = '
The shelf is empty. Would you like to add a book?
'; + } + + const body = '

Available Books

' + books + '
'; + const html = app.fillTemplate('./templates/htmlContainer.html', { + title: 'View', + resourcePath: (req.url.substr(-1) === '/' ? '../' : './'), + body + }); + + if (html) { + res.send(html); + } else { + res.send('Something went wrong!'); + } + }); +} \ No newline at end of file diff --git a/routes/get_tools.js b/routes/get_tools.js new file mode 100644 index 0000000..62eaa80 --- /dev/null +++ b/routes/get_tools.js @@ -0,0 +1,56 @@ +const path = require('path'); +const fs = require('fs'); + +module.exports = function (app) { + app.server.get('/tools', (req, res) => { + if (req.query.pass === settings.toolsPassword) { + const templateValues = {}; + let html = app.fillTemplate('./templates/pages/tools.html', templateValues); + + if (req.query.do && ['resetVisitors'].includes(req.query.do)) { + app.connections = 0; + templateValues.resetVisitors = 'Done!'; + html = app.fillTemplate('./templates/pages/tools.html', templateValues); + res.send(html); + } else if (req.query.dl && ['files', 'history'].includes(req.query.dl)) { + const onezip = require('onezip'); + const { dl } = req.query; + const saveLocation = path.resolve(app.fileLocation, dl + 'Backup.zip'); + const backupLocation = dl === 'history' ? app.historyLocation : app.fileLocation; + const files = fs.readdirSync(backupLocation).filter(fileName => !fileName.includes('.zip')); + onezip.pack(backupLocation, saveLocation, files) + .on('start', () => { + console.info('Starting a backup zip of ' + dl) + }) + .on('error', (error) => { + console.error(error); + templateValues[dl + 'Download'] = 'Something went wrong: ' + JSON.stringify(error); + html = app.fillTemplate('./templates/pages/tools.html', templateValues); + res.send(html); + }) + .on('end', () => { + console.log('Backup complete. Saved to ' + saveLocation); + let backupLocation = saveLocation.replace(/\\/g, '/'); + backupLocation = backupLocation.substr(backupLocation.lastIndexOf('/')); + templateValues[dl + 'Download'] = 'Download (This will be removed from the server in 1 hour)'; + html = app.fillTemplate('./templates/pages/tools.html', templateValues); + res.send(html); + console.log('Will delete ' + saveLocation + ' in 1 hour'); + setTimeout(() => { + fs.unlink(saveLocation, (err) => { + if (err) { + console.error(err); + } else { + console.log('Deleted backup file ' + saveLocation); + } + }) + }, 60 * 60 * 1000); + }); + } else { + res.send(html); + } + } else { + res.status(400).send(); + } + }); +} \ No newline at end of file diff --git a/routes/middleware.js b/routes/middleware.js new file mode 100644 index 0000000..68a2d46 --- /dev/null +++ b/routes/middleware.js @@ -0,0 +1,43 @@ +const path = require('path'); +const fs = require('fs'); +const express = require('express'); +const helmet = require('helmet'); +const bodyParser = require('body-parser'); +const fileUpload = require('express-fileupload'); + +const settings = require('../settings.json'); + +module.exports = function (app) { + app.server.use(helmet()); + + app.server.use(bodyParser.json()); // support json encoded bodies + app.server.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies + + app.server.use('/give', fileUpload({ // support file uploads + limits: { + fileSize: (settings.maxFileSize > 0 ? settings.maxFileSize * 1024 * 1024 : Infinity), // filesize in bytes (settings accepts MB) + }, + })); + app.server.use('/tools', fileUpload()); // Allow file upload on backup with no limits. + + app.server.use('/files', express.static(path.resolve('./public/files/'))); + app.server.use('/css', express.static(path.resolve('./node_modules/bulma/css/'))); + app.server.use('/css', express.static(path.resolve('./public/css/'))); + app.server.use('/js', express.static(path.resolve('./public/js/'))); + app.server.use('/js', express.static(path.resolve('./node_modules/jquery/dist/'))); + app.server.use('/js', express.static(path.resolve('./node_modules/socket.io-client/dist/'))); + + // If a `.well-known` directory exists, allow it to be used for things like Let's Encrypt challenges + if (fs.existsSync(path.resolve('./.well-known'))) { + app.server.use('/.well-known', express.static(path.resolve('./.well-known'))); + } + + if (app.https && settings.forceHTTPS) { + app.server.use(function (req, res, next) { + if (!req.secure) { + return res.redirect(['https://', req.get('Host'), req.baseUrl].join('')); + } + next(); 0 + }); + } +} \ No newline at end of file diff --git a/routes/post_give.js b/routes/post_give.js new file mode 100644 index 0000000..6563663 --- /dev/null +++ b/routes/post_give.js @@ -0,0 +1,61 @@ +module.exports = function (app) { + app.server.post('/give', (req, res) => { + const resourcePath = (req.url.substr(-1) === '/' ? '../' : './'); + const { title, author, summary, contributor } = req.body; + if (Object.keys(req.files).length > 0 + && req.body.hasOwnProperty('title') && title.trim() !== '' + && req.body.hasOwnProperty('summary') && summary.trim() !== '') { + const { book } = req.files; + const fileType = book.name.substr(book.name.lastIndexOf('.')); + app.addBook({ book, title, author, summary, contributor, fileType }, () => { + const messageBox = app.fillTemplate('./templates/elements/messageBox.html', { + style: 'is-success', + header: 'Upload Successful', + message: 'Thank you for your contribution!' + }); + const modal = app.fillTemplate('./templates/elements/modal.html', { + isActive: 'is-active', + content: messageBox, + }); + let body = app.fillTemplate('./templates/pages/uploadForm.html', { resourcePath }); + body = app.replaceBodyWithTooManyBooksWarning(body); + const html = app.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body, modal }); + res.send(html); + }, (err) => { + const messageBox = app.fillTemplate('./templates/elements/messageBox.html', { + style: 'is-danger', + header: 'Upload Failed', + message: err, + }); + const modal = app.fillTemplate('./templates/elements/modal.html', { + isActive: 'is-active', + content: messageBox, + }); + let body = app.fillTemplate('./templates/pages/uploadForm.html', { resourcePath, title, author, summary, contributor }); + body = app.replaceBodyWithTooManyBooksWarning(body); + const html = app.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body, modal }); + res.send(html); + }); + } else { + let errorMessage = ''; + if (Object.keys(req.files).length <= 0) { + errorMessage += 'You have not selected a file.'; + } + if (!req.body.hasOwnProperty('title') || req.body.title.trim() === '') { + errorMessage += (errorMessage.length > 0 ? '
' : '') + 'You have not written a title.'; + } + if (!req.body.hasOwnProperty('summary') || req.body.summary.trim() === '') { + errorMessage += (errorMessage.length > 0 ? '
' : '') + 'You have not written a summary.'; + } + const message = app.fillTemplate('./templates/elements/messageBox.html', { + style: 'is-danger', + header: 'Missing Required Fields', + message: errorMessage, + }); + let body = app.fillTemplate('./templates/pages/uploadForm.html', { resourcePath, title, author, summary, contributor }); + body = app.replaceBodyWithTooManyBooksWarning(body); + const html = app.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body, message }); + res.send(html); + } + }); +} \ No newline at end of file diff --git a/routes/post_tools.js b/routes/post_tools.js new file mode 100644 index 0000000..6df2802 --- /dev/null +++ b/routes/post_tools.js @@ -0,0 +1,59 @@ +const path = require('path'); +const fs = require('fs'); + +module.exports = function (app) { + app.server.post('/tools', (req, res) => { + if (req.query.pass === settings.toolsPassword) { + const templateValues = {}; + let html = app.fillTemplate('./templates/pages/tools.html', templateValues); + + const { files } = req; + if (Object.keys(files).length > 0) { + const backupType = Object.keys(files)[0]; + if (['files', 'history'].includes(backupType)) { + const onezip = require('onezip'); + const uploadPath = path.resolve('./', backupType + 'UploadedBackup.zip'); + files[backupType].mv(uploadPath, (err) => { + if (err) { + console.error(error); + templateValues[backupType + 'UploadSuccess'] = 'Could not upload the file.'; + html = app.fillTemplate('./templates/pages/tools.html', templateValues); + res.send(html); + } else { + onezip.extract(uploadPath, path.resolve('./public', backupType)) + .on('start', () => { + console.info('Extracting file ' + uploadPath) + }) + .on('error', (error) => { + console.error(error); + templateValues[backupType + 'UploadSuccess'] = 'Something went wrong: ' + JSON.stringify(error); + html = app.fillTemplate('./templates/pages/tools.html', templateValues); + res.send(html); + }) + .on('end', () => { + templateValues[backupType + 'UploadSuccess'] = 'Uploaded Successfully!'; + html = app.fillTemplate('./templates/pages/tools.html', templateValues); + res.send(html); + fs.unlink(uploadPath, (err) => { + if (err) { + console.error(err); + } else { + console.log('Deleted backup file ' + uploadPath); + } + }) + }); + } + }); + } else { + templateValues['generalError'] = '

' + backupType + ' is not a valid backup type.

'; + html = app.fillTemplate('./templates/pages/tools.html', templateValues); + res.send(html); + } + } else { + res.send(html); + } + } else { + res.status(400).send(); + } + }); +} \ No newline at end of file diff --git a/routes/socketio.js b/routes/socketio.js new file mode 100644 index 0000000..b8e8967 --- /dev/null +++ b/routes/socketio.js @@ -0,0 +1,29 @@ +const settings = require('../settings.json'); + +module.exports = function (app) { + app.io.on('connection', socket => { + if (!settings.hideVisitors) { + app.connections++; + app.io.emit('update visitors', app.connections); + } + + socket.on('take book', bookId => { + const fileLocation = app.takeBook(bookId, socket.id); + if (fileLocation) { + console.log(socket.id + ' removed ' + bookId); + const downloadLocation = fileLocation.substr(fileLocation.lastIndexOf('/')); + socket.emit('get book', encodeURI('./files' + downloadLocation)); + socket.broadcast.emit('remove book', bookId); + } + }); + + socket.on('disconnect', () => { + if (!settings.hideVisitors) { + app.connections--; + if (app.connections < 0) app.connections = 0; + app.io.emit('update visitors', app.connections); + } + app.deleteBooks(socket.id); + }); + }); +} \ No newline at end of file diff --git a/server.js b/server.js index 07e3da4..19b7375 100644 --- a/server.js +++ b/server.js @@ -4,14 +4,9 @@ const express = require('express'); const http = require('http'); const https = require('https'); const SocketIoServer = require('socket.io'); -const helmet = require('helmet'); -const bodyParser = require('body-parser'); -const fileUpload = require('express-fileupload'); const filenamify = require('filenamify'); const unusedFilename = require('unused-filename'); const striptags = require('striptags'); -const snarkdown = require('snarkdown'); -const fecha = require('fecha'); const settings = require('./settings.json'); const privateKey = settings.sslPrivateKey ? fs.readFileSync(settings.sslPrivateKey, 'utf8') : null; @@ -38,267 +33,21 @@ function Server () { this.connections = 0; this.takenBooks = []; - this.server.use(helmet()); + require('./routes/middleware')(this); - this.server.use(bodyParser.json()); // support json encoded bodies - this.server.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies + require('./routes/get_home')(this); - this.server.use('/give', fileUpload({ // support file uploads - limits: { - fileSize: (settings.maxFileSize > 0 ? settings.maxFileSize * 1024 * 1024 : Infinity), // filesize in bytes (settings accepts MB) - }, - })); - this.server.use('/tools', fileUpload()); // Allow file upload on backup with no limits. + require('./routes/get_give')(this); + require('./routes/post_give')(this); - this.server.use('/files', express.static(path.join(__dirname, './public/files/'))); - this.server.use('/css', express.static(path.resolve('./node_modules/bulma/css/'))); - this.server.use('/css', express.static(path.join(__dirname, './public/css/'))); - this.server.use('/js', express.static(path.join(__dirname, './public/js/'))); - this.server.use('/js', express.static(path.resolve('./node_modules/jquery/dist/'))); - this.server.use('/js', express.static(path.resolve('./node_modules/socket.io-client/dist/'))); + require('./routes/get_history')(this); - // If a `.well-known` directory exists, allow it to be used for things like Let's Encrypt challenges - if (fs.existsSync(path.resolve('./.well-known'))) { - this.server.use('/.well-known', express.static(path.resolve('./.well-known'))); - } + require('./routes/get_about')(this); - if (this.https && settings.forceHTTPS) { - this.server.use(function (req, res, next) { - if (!req.secure) { - return res.redirect(['https://', req.get('Host'), req.baseUrl].join('')); - } - next();0 - }); - } - - this.server.get('/', (req, res) => { - const html = this.generateHomePage(req); - if (html) { - res.send(html); - } else { - res.send('Something went wrong!'); - } - }); + require('./routes/get_tools')(this); + require('./routes/post_tools')(this); - this.server.get('/give', (req, res) => { - const resourcePath = (req.url.substr(-1) === '/' ? '../' : './'); - let body = this.fillTemplate('./templates/pages/uploadForm.html', { resourcePath }); - body = this.replaceBodyWithTooManyBooksWarning(body); - - const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body }); - res.send(html); - }); - this.server.post('/give', (req, res) => { - const resourcePath = (req.url.substr(-1) === '/' ? '../' : './'); - const { title, author, summary, contributor } = req.body; - if (Object.keys(req.files).length > 0 - && req.body.hasOwnProperty('title') && title.trim() !== '' - && req.body.hasOwnProperty('summary') && summary.trim() !== '') { - const { book } = req.files; - const fileType = book.name.substr(book.name.lastIndexOf('.')); - this.addBook({ book, title, author, summary, contributor, fileType }, () => { - const messageBox = this.fillTemplate('./templates/elements/messageBox.html', { - style: 'is-success', - header: 'Upload Successful', - message: 'Thank you for your contribution!' - }); - const modal = this.fillTemplate('./templates/elements/modal.html', { - isActive: 'is-active', - content: messageBox, - }); - let body = this.fillTemplate('./templates/pages/uploadForm.html', { resourcePath }); - body = this.replaceBodyWithTooManyBooksWarning(body); - const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body, modal }); - res.send(html); - }, (err) => { - const messageBox = this.fillTemplate('./templates/elements/messageBox.html', { - style: 'is-danger', - header: 'Upload Failed', - message: err, - }); - const modal = this.fillTemplate('./templates/elements/modal.html', { - isActive: 'is-active', - content: messageBox, - }); - let body = this.fillTemplate('./templates/pages/uploadForm.html', { resourcePath, title, author, summary, contributor }); - body = this.replaceBodyWithTooManyBooksWarning(body); - const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body, modal }); - res.send(html); - }); - } else { - let errorMessage = ''; - if (Object.keys(req.files).length <= 0) { - errorMessage += 'You have not selected a file.'; - } - if (!req.body.hasOwnProperty('title') || req.body.title.trim() === '') { - errorMessage += (errorMessage.length > 0 ? '
' : '') + 'You have not written a title.'; - } - if (!req.body.hasOwnProperty('summary') || req.body.summary.trim() === '') { - errorMessage += (errorMessage.length > 0 ? '
' : '') + 'You have not written a summary.'; - } - const message = this.fillTemplate('./templates/elements/messageBox.html', { - style: 'is-danger', - header: 'Missing Required Fields', - message: errorMessage, - }); - let body = this.fillTemplate('./templates/pages/uploadForm.html', { resourcePath, title, author, summary, contributor }); - body = this.replaceBodyWithTooManyBooksWarning(body); - const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', resourcePath, body, message }); - res.send(html); - } - }); - - this.server.get('/history', (req, res) => { - const html = this.generateHistoryPage(req); - if (html) { - res.send(html); - } else { - res.send('Something went wrong!'); - } - }); - - this.server.get('/about', (req, res) => { - const resourcePath = (req.url.substr(-1) === '/' ? '../' : './'); - const body = this.fillTemplate('./templates/pages/about.html', { resourcePath }); - const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'About', body }); - if (html) { - res.send(html); - } else { - res.send('Something went wrong!'); - } - }); - - this.server.get('/tools', (req, res) => { - if (req.query.pass === settings.toolsPassword) { - const templateValues = {}; - let html = this.fillTemplate('./templates/pages/tools.html', templateValues); - - if (req.query.do && ['resetVisitors'].includes(req.query.do)) { - this.connections = 0; - templateValues.resetVisitors = 'Done!'; - html = this.fillTemplate('./templates/pages/tools.html', templateValues); - res.send(html); - } else if (req.query.dl && ['files', 'history'].includes(req.query.dl)) { - const onezip = require('onezip'); - const { dl } = req.query; - const saveLocation = path.resolve(this.fileLocation, dl + 'Backup.zip'); - const backupLocation = dl === 'history' ? this.historyLocation : this.fileLocation; - const files = fs.readdirSync(backupLocation).filter(fileName => !fileName.includes('.zip')); - onezip.pack(backupLocation, saveLocation, files) - .on('start', () => { - console.info('Starting a backup zip of ' + dl) - }) - .on('error', (error) => { - console.error(error); - templateValues[dl + 'Download'] = 'Something went wrong: ' + JSON.stringify(error); - html = this.fillTemplate('./templates/pages/tools.html', templateValues); - res.send(html); - }) - .on('end', () => { - console.log('Backup complete. Saved to ' + saveLocation); - let backupLocation = saveLocation.replace(/\\/g, '/'); - backupLocation = backupLocation.substr(backupLocation.lastIndexOf('/')); - templateValues[dl + 'Download'] = 'Download (This will be removed from the server in 1 hour)'; - html = this.fillTemplate('./templates/pages/tools.html', templateValues); - res.send(html); - console.log('Will delete ' + saveLocation + ' in 1 hour'); - setTimeout(() => { - fs.unlink(saveLocation, (err) => { - if (err) { - console.error(err); - } else { - console.log('Deleted backup file ' + saveLocation); - } - }) - }, 60 * 60 * 1000); - }); - } else { - res.send(html); - } - } else { - res.status(400).send(); - } - }); - this.server.post('/tools', (req, res) => { - if (req.query.pass === settings.toolsPassword) { - const templateValues = {}; - let html = this.fillTemplate('./templates/pages/tools.html', templateValues); - - const { files } = req; - if (Object.keys(files).length > 0) { - const backupType = Object.keys(files)[0]; - if (['files', 'history'].includes(backupType)) { - const onezip = require('onezip'); - const uploadPath = path.resolve('./', backupType + 'UploadedBackup.zip'); - files[backupType].mv(uploadPath, (err) => { - if (err) { - console.error(error); - templateValues[backupType + 'UploadSuccess'] = 'Could not upload the file.'; - html = this.fillTemplate('./templates/pages/tools.html', templateValues); - res.send(html); - } else { - onezip.extract(uploadPath, path.resolve('./public', backupType)) - .on('start', () => { - console.info('Extracting file ' + uploadPath) - }) - .on('error', (error) => { - console.error(error); - templateValues[backupType + 'UploadSuccess'] = 'Something went wrong: ' + JSON.stringify(error); - html = this.fillTemplate('./templates/pages/tools.html', templateValues); - res.send(html); - }) - .on('end', () => { - templateValues[backupType + 'UploadSuccess'] = 'Uploaded Successfully!'; - html = this.fillTemplate('./templates/pages/tools.html', templateValues); - res.send(html); - fs.unlink(uploadPath, (err) => { - if (err) { - console.error(err); - } else { - console.log('Deleted backup file ' + uploadPath); - } - }) - }); - } - }); - } else { - templateValues['generalError'] = '

' + backupType + ' is not a valid backup type.

'; - html = this.fillTemplate('./templates/pages/tools.html', templateValues); - res.send(html); - } - } else { - res.send(html); - } - } else { - res.status(400).send(); - } - }); - - this.io.on('connection', socket => { - if (!settings.hideVisitors) { - this.connections++; - this.io.emit('update visitors', this.connections); - } - - socket.on('take book', bookId => { - const fileLocation = this.takeBook(bookId, socket.id); - if (fileLocation) { - console.log(socket.id + ' removed ' + bookId); - const downloadLocation = fileLocation.substr(fileLocation.lastIndexOf('/')); - socket.emit('get book', encodeURI('./files' + downloadLocation)); - socket.broadcast.emit('remove book', bookId); - } - }); - - socket.on('disconnect', () => { - if (!settings.hideVisitors) { - this.connections--; - if (this.connections < 0) this.connections = 0; - this.io.emit('update visitors', this.connections); - } - this.deleteBooks(socket.id); - }); - }); + require('./routes/socketio')(this); } Server.prototype.fillTemplate = function (file, templateVars = {}) { @@ -306,7 +55,7 @@ Server.prototype.fillTemplate = function (file, templateVars = {}) { if (this.templateCache.hasOwnProperty(file)) { data = this.templateCache[file]; } else { - data = fs.readFileSync(path.join(__dirname, file), 'utf8'); + data = fs.readFileSync(path.resolve(file), 'utf8'); } if (data) { if (!this.templateCache.hasOwnProperty(file)) { @@ -347,118 +96,6 @@ Server.prototype.replaceBodyWithTooManyBooksWarning = function (body) { return body; } -Server.prototype.generateHomePage = function (req) { - const files = fs.readdirSync(this.fileLocation).filter(fileName => fileName.includes('.json')) - .map(fileName => { // Cache the file data so sorting doesn't need to re-check each file - return { name: fileName, time: fs.statSync(path.resolve(this.fileLocation, fileName)).mtime.getTime() }; - }).sort((a, b) => a.time - b.time).map(v => v.name); // Sort from oldest to newest. - - let books = files.map(fileName => { - const bookData = JSON.parse(fs.readFileSync(path.resolve(this.fileLocation, fileName), 'utf8')); - if (bookData.hasOwnProperty('fileName')) return ''; - bookData.author = bookData.author ? bookData.author : 'author not provided'; - bookData.contributor = bookData.contributor ? bookData.contributor : 'Anonymous'; - - const id = fileName.replace('.json', ''); - const confirmId = 'confirm_' + id; - const added = fecha.format(new Date(bookData.added), 'hh:mm:ssA on dddd MMMM Do, YYYY'); - const modal = this.fillTemplate('./templates/elements/modalCard.html', { - id, - header: '

' + bookData.title + '

' + bookData.author + '

', - content: this.fillTemplate('./templates/elements/bookInfo.html', { - contributor: bookData.contributor, - fileFormat: bookData.fileType, - added, - summary: snarkdown(bookData.summary), - }) - + this.fillTemplate('./templates/elements/modal.html', { - id: confirmId, - content: this.fillTemplate('./templates/elements/messageBox.html', { - header: 'Download Your Book', - message: this.fillTemplate('./templates/elements/takeConfirm.html', { id }), - }), - }), - footer: 'Close Take Book', - }); - return this.fillTemplate('./templates/elements/book.html', { - id, - title: bookData.title, - author: bookData.author, - fileType: bookData.fileType, - modal, - }); - }).join(''); - - if (books == '') { - books = '
The shelf is empty. Would you like to add a book?
'; - } - - const body = '

Available Books

' + books + '
'; - return this.fillTemplate('./templates/htmlContainer.html', { - title: 'View', - resourcePath: (req.url.substr(-1) === '/' ? '../' : './'), - body - }); -} - -Server.prototype.generateHistoryPage = function (req) { - const files = fs.readdirSync(this.historyLocation).filter(fileName => fileName.includes('.json')) - .map(fileName => { // Cache the file data so sorting doesn't need to re-check each file - return { name: fileName, time: fs.statSync(path.resolve(this.historyLocation, fileName)).mtime.getTime() }; - }).sort((a, b) => b.time - a.time).map(v => v.name); // Sort from newest to oldest. - - let history = files.map(fileName => { - const bookData = JSON.parse(fs.readFileSync(path.resolve(this.historyLocation, fileName), 'utf8')); - bookData.author = bookData.author ? bookData.author : 'author not provided'; - bookData.contributor = bookData.contributor ? bookData.contributor : 'Anonymous'; - const id = fileName.replace('.json', ''); - const added = fecha.format(new Date(bookData.added), 'hh:mm:ssA on dddd MMMM Do, YYYY'); - const removed = fecha.format(new Date(parseInt(id)), 'hh:mm:ssA on dddd MMMM Do, YYYY'); - const removedTag = '
Taken' + removed + '
'; - const modal = this.fillTemplate('./templates/elements/modalCard.html', { - id, - header: '

' + bookData.title + '

' + bookData.author + '

', - content: this.fillTemplate('./templates/elements/bookInfo.html', { - contributor: bookData.contributor, - fileFormat: bookData.fileType, - added, - removedTag, - summary: snarkdown(bookData.summary), - }), - footer: 'Close', - }); - return this.fillTemplate('./templates/elements/book.html', { - id, - title: bookData.title, - author: bookData.author, - fileType: bookData.fileType, - modal, - }); - }).join(''); - - if (history == '') { - history = '
No books have been taken yet. Would you like to take a book?
'; - } - - const body = '

History

' + history + '
'; - return this.fillTemplate('./templates/htmlContainer.html', { - title: 'History', - resourcePath: (req.url.substr(-1) === '/' ? '../' : './'), - body - }); -} - -Server.prototype.start = function () { - this.http.listen((process.env.PORT || settings.port), () => { - console.log('Started server on port ' + (process.env.PORT || settings.port)); - }); - if (this.https) { - this.https.listen(443, () => { - console.log('Started SSL server on port 443'); - }); - } -} - Server.prototype.addBook = function (uploadData = {}, success = () => {}, error = () => {}) { const { book } = uploadData; @@ -568,5 +205,16 @@ Server.prototype.uuid4 = function () { }); } +Server.prototype.start = function () { + this.http.listen((process.env.PORT || settings.port), () => { + console.log('Started server on port ' + (process.env.PORT || settings.port)); + }); + if (this.https) { + this.https.listen(443, () => { + console.log('Started SSL server on port 443'); + }); + } +} + const server = new Server(); server.start();