/src/methods/post.js |
@@ -0,0 +1,413 @@ |
#!/usr/bin/env node |
|
/*************************************************************************/ |
/* Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3 */ |
/*************************************************************************/ |
|
const url = require('url'); |
const path = require('path'); |
const fs = require('fs'); |
const mime = require('mime'); |
const auth = require("http-auth"); |
const stream = require('stream'); |
const util = require('util'); |
var formidable = require('formidable'); |
const EventEmitter = require('events').EventEmitter; |
|
// Local imports. |
const Cache = require( |
path |
.resolve( |
path.dirname(require.main.filename), |
'src/server', |
'cache' |
) |
); |
const was = require( |
path |
.resolve( |
path.dirname(require.main.filename), |
'src/server', |
'was' |
) |
); |
|
// Constructor. |
function POST() { |
// Create events emitters for logging and data. |
EventEmitter.call(this); |
}; |
|
// Process a request. |
POST.prototype.process = function(config, request, response, root) { |
EventEmitter.call(this); |
var self = this; |
|
// Get client details. |
const address = request.socket.address(); |
// Get requested URL. |
const requestURL = url.parse( |
request.url, true |
); |
|
// Perform URL re-writes. |
Object.keys(config.site.rewrite).forEach((key, index) => { |
if (config.site.rewrite[key].test(requestURL.path)) { |
const originalPath = requestURL.path; |
requestURL.path = requestURL |
.path |
.replace( |
config.site.rewrite[key], key |
); |
requestURL.pathname = url.parse( |
requestURL |
.pathname |
.replace( |
config.site.rewrite[key], key |
), |
true |
) |
.pathname; |
self.emit('log', { |
message: 'Rewrite path: ' + |
originalPath + |
' to: ' + |
requestURL.path, |
severity: 'info' |
}); |
} |
}); |
|
const trimmedPath = requestURL |
.pathname |
.split('/') |
.filter(Boolean) |
.join('/'); |
const requestPath = trimmedPath === '/' ? |
path.join(root, trimmedPath) : |
path.resolve(root, trimmedPath); |
|
var form = new formidable.IncomingForm(), |
properfields = {}; |
|
// Workaround for formidable not parsing multiple filed options. |
form.on('field', function(name, value) { |
if (!properfields[name]) { |
properfields[name] = value; |
} else { |
if (properfields[name].constructor.toString().indexOf("Array") > -1) { // is array |
properfields[name].push(value); |
} else { // not array |
var tmp = properfields[name]; |
properfields[name] = []; |
properfields[name].push(tmp); |
properfields[name].push(value); |
} |
} |
}); |
|
form.parse(request, function(error, fields, files) { |
// If the form data could not be parsed. |
if (error) { |
self.emit('log', { |
message: 'Could not parse form data from: ' + |
address.address + ':' + |
address.port + |
' requesting: ' + |
requestURL.pathname, |
severity: 'warning' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
|
switch(trimmedPath) { |
case 'add': |
// Write the icon file. |
var iconPath = path.join(root, 'data/' + fields['service-name'] + '.png'); |
var formFile = fs.createReadStream(files['service-icon'].path) |
.on('open', () => { |
formFile.pipe( |
fs.createWriteStream(iconPath) |
.on('error', (error) => { |
if (error) { |
self.emit('log', { |
message: 'Unable to create file at: ' + iconPath, |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
}) |
); |
}) |
.on('error', (error) => { |
if (error) { |
self.emit('log', { |
message: 'Unable to create file at: ' + iconPath, |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
}); |
|
// Update the data file. |
fs.realpath(path.join(root, 'data/data.json'), (error, dataPath) => { |
// If the path does not exist, then return early. |
if (error) { |
self.emit('log', { |
message: 'Unable to access data path: ' + dataPath, |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
|
fs.readFile(dataPath, 'utf8', function(error, data) { |
// Could not read data file. |
if (error) { |
self.emit('log', { |
message: 'Unable to read data file.', |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
|
// Remove previous service if it exists. |
var services = JSON.parse(data).filter((service) => { |
return service.tooltip != fields['service-name']; |
}); |
|
services.push({ |
image: '/data/' + fields['service-name'] + '.png', |
width: '50', |
height: '50', |
url: fields['service-url'], |
target: '_top', |
tooltip: fields['service-name'] |
}); |
|
fs.writeFile(dataPath, JSON.stringify(services, null, 4), (error) => { |
// Could not write data file. |
if (error) { |
self.emit('log', { |
message: 'Unable to write data file.', |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
|
// Send the main site index. |
self.emit('log', { |
message: 'Added new service.', |
severity: 'info' |
}); |
self.emit('data', { |
status: 200, |
data: fs.createReadStream(path.join(root, config.site.index)), |
type: 'text/html' |
}); |
|
}); |
|
}); |
|
}); |
break; |
case 'remove': |
fs.realpath(path.join(root, 'data/data.json'), (error, dataPath) => { |
// If the path does not exist, then return early. |
if (error) { |
self.emit('log', { |
message: 'Unable to access data path: ' + dataPath, |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
|
fs.readFile(dataPath, 'utf8', function(error, data) { |
// Could not read data file. |
if (error) { |
self.emit('log', { |
message: 'Unable to read data file.', |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
|
// Remove all specified services and their corresponding icon files. |
var currentServices = JSON.parse(data); |
var services = []; |
currentServices.forEach(currentService => { |
if([ properfields['remove-services'] ].some((service) => service === currentService.tooltip)) { |
var imagePath = path.join(root, currentService.image); |
fs.stat(imagePath, (error, stats) => { |
// Image file does not exist. |
if (error) { |
self.emit('log', { |
message: 'Image file does not exist.', |
severity: 'warning' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
fs.unlink(imagePath, (error) => { |
if(error) { |
self.emit('log', { |
message: 'Could not remove image file.', |
severity: 'warning' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
} |
}); |
}); |
return; |
} |
services.push(currentService); |
}); |
|
// Write the data file back. |
fs.writeFile(dataPath, JSON.stringify(services, null, 4), (error) => { |
// Could not write data file. |
if (error) { |
self.emit('log', { |
message: 'Unable to write data file.', |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
|
// Send the main site index. |
self.emit('log', { |
message: 'Removed services.', |
severity: 'info' |
}); |
self.emit('data', { |
status: 200, |
data: fs.createReadStream(path.join(root, config.site.index)), |
type: 'text/html' |
}); |
|
}); |
|
}); |
}); |
break; |
default: |
self.emit('log', { |
message: 'No such path.', |
severity: 'error' |
}); |
self.emit('data', { |
status: 404, |
data: new stream.Readable({ |
read(size) { |
this.push(null); |
} |
}), |
type: 'text/plain' |
}); |
return; |
} |
}); |
|
return this; |
}; |
|
util.inherits(POST, EventEmitter); |
util.inherits(Cache, EventEmitter); |
module.exports = POST; |