aboutsummaryrefslogtreecommitdiff
path: root/src/modules/webserver.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/webserver.js')
-rw-r--r--src/modules/webserver.js92
1 files changed, 92 insertions, 0 deletions
diff --git a/src/modules/webserver.js b/src/modules/webserver.js
new file mode 100644
index 0000000..5d8b2c9
--- /dev/null
+++ b/src/modules/webserver.js
@@ -0,0 +1,92 @@
+const http = require('http');
+const mime = require('mime');
+const url = require('url');
+const fs = require('fs');
+const moment = require('moment');
+const config = require('../config');
+const threads = require('../data/threads');
+const attachments = require('../data/attachments');
+
+const {THREAD_MESSAGE_TYPE} = require('../data/constants');
+
+function notfound(res) {
+ res.statusCode = 404;
+ res.end('Page Not Found');
+}
+
+async function serveLogs(res, pathParts) {
+ const threadId = pathParts[pathParts.length - 1];
+ if (threadId.match(/^[0-9a-f\-]+$/) === null) return notfound(res);
+
+ const thread = await threads.findById(threadId);
+ if (! thread) return notfound(res);
+
+ const threadMessages = await thread.getThreadMessages();
+ const lines = threadMessages.map(message => {
+ // Legacy messages are the entire log in one message, so just serve them as they are
+ if (message.message_type === THREAD_MESSAGE_TYPE.LEGACY) {
+ return message.body;
+ }
+
+ let line = `[${moment.utc(message.created_at).format('YYYY-MM-DD HH:mm:ss')}] `;
+
+ if (message.message_type === THREAD_MESSAGE_TYPE.SYSTEM) {
+ // System messages don't need the username
+ line += message.body;
+ } else if (message.message_type === THREAD_MESSAGE_TYPE.FROM_USER) {
+ line += `[FROM USER] ${message.user_name}: ${message.body}`;
+ } else if (message.message_type === THREAD_MESSAGE_TYPE.TO_USER) {
+ line += `[TO USER] ${message.user_name}: ${message.body}`;
+ } else {
+ line += `${message.user_name}: ${message.body}`;
+ }
+
+ return line;
+ });
+
+ res.setHeader('Content-Type', 'text/plain; charset=UTF-8');
+ res.end(lines.join('\n'));
+}
+
+function serveAttachments(res, pathParts) {
+ const desiredFilename = pathParts[pathParts.length - 1];
+ const id = pathParts[pathParts.length - 2];
+
+ if (id.match(/^[0-9]+$/) === null) return notfound(res);
+ if (desiredFilename.match(/^[0-9a-z._-]+$/i) === null) return notfound(res);
+
+ const attachmentPath = attachments.getLocalAttachmentPath(id);
+ fs.access(attachmentPath, (err) => {
+ if (err) return notfound(res);
+
+ const filenameParts = desiredFilename.split('.');
+ const ext = (filenameParts.length > 1 ? filenameParts[filenameParts.length - 1] : 'bin');
+ const fileMime = mime.getType(ext);
+
+ res.setHeader('Content-Type', fileMime);
+
+ const read = fs.createReadStream(attachmentPath);
+ read.pipe(res);
+ })
+}
+
+module.exports = () => {
+ const server = http.createServer((req, res) => {
+ const parsedUrl = url.parse(`http://${req.url}`);
+ const pathParts = parsedUrl.path.split('/').filter(v => v !== '');
+
+ if (parsedUrl.path.startsWith('/logs/')) {
+ serveLogs(res, pathParts);
+ } else if (parsedUrl.path.startsWith('/attachments/')) {
+ serveAttachments(res, pathParts);
+ } else {
+ notfound(res);
+ }
+ });
+
+ server.on('error', err => {
+ console.log('[WARN] Web server error:', err.message);
+ });
+
+ server.listen(config.port);
+};