up1/server/server.js

221 lines
6.5 KiB
JavaScript
Raw Normal View History

2016-01-08 15:32:04 +01:00
var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var Busboy = require('busboy');
var express = require('express');
var http = require('http');
var https = require('https');
var request = require('request');
var tmp = require('tmp');
// Different headers can be pushed depending on data format
// to allow for changes with backwards compatibility
var UP1_HEADERS = {
v1: new Buffer("UP1\0", 'binary')
}
function handle_upload(req, res) {
var config = req.app.locals.config
var busboy = new Busboy({
headers: req.headers,
limits: {
fileSize: config.maximum_file_size,
files: 1,
parts: 3
}
});
var fields = {};
var tmpfname = null;
busboy.on('field', function(fieldname, value) {
fields[fieldname] = value;
});
busboy.on('file', function(fieldname, file, filename) {
try {
var ftmp = tmp.fileSync({ postfix: '.tmp', dir: req.app.locals.config.path.i, keep: true });
2016-01-08 15:32:04 +01:00
tmpfname = ftmp.name;
var fstream = fs.createWriteStream('', {fd: ftmp.fd, defaultEncoding: 'binary'});
fstream.write(UP1_HEADERS.v1);
file.pipe(fstream);
} catch (err) {
console.error("Error on file:", err);
res.send("Internal Server Error");
req.unpipe(busboy);
res.close();
}
});
busboy.on('finish', function() {
try {
if (!tmpfname) {
res.send("Internal Server Error");
} else if (fields.api_key !== config['api_key']) {
res.send('{"error": "API key doesn\'t match", "code": 2}');
} else if (!fields.ident) {
res.send('{"error": "Ident not provided", "code": 11}');
} else if (fields.ident.length !== 22) {
res.send('{"error": "Ident length is incorrect", "code": 3}');
} else if (ident_exists(fields.ident)) {
res.send('{"error": "Ident is already taken.", "code": 4}');
} else {
var delhmac = crypto.createHmac('sha256', config.delete_key)
.update(fields.ident)
.digest('hex');
fs.rename(tmpfname, ident_path(fields.ident), function() {
res.json({delkey: delhmac});
});
}
} catch (err) {
console.error("Error on finish:", err);
res.send("Internal Server Error");
}
});
return req.pipe(busboy);
};
function handle_delete(req, res) {
var config = req.app.locals.config
if (!req.query.ident) {
res.send('{"error": "Ident not provided", "code": 11}');
return;
}
if (!req.query.delkey) {
res.send('{"error": "Delete key not provided", "code": 12}');
return;
}
var delhmac = crypto.createHmac('sha256', config.delete_key)
.update(req.query.ident)
.digest('hex');
if (req.query.ident.length !== 22) {
res.send('{"error": "Ident length is incorrect", "code": 3}');
} else if (delhmac !== req.query.delkey) {
res.send('{"error": "Incorrect delete key", "code": 10}');
} else if (!ident_exists(req.query.ident)) {
res.send('{"error": "Ident does not exist", "code": 9}');
} else {
fs.unlink(ident_path(req.query.ident), function() {
cf_invalidate(req.query.ident, config);
res.redirect('/');
});
}
};
function ident_path(ident) {
2016-01-08 16:01:50 +01:00
return '../i/' + path.basename(ident);
2016-01-08 15:32:04 +01:00
}
function ident_exists(ident) {
try {
fs.lstatSync(ident_path(ident));
return true;
} catch (err) {
return false;
}
}
function cf_do_invalidate(ident, mode, cfconfig) {
2016-01-11 04:57:34 +01:00
var inv_url = mode + '://' + cfconfig.url + '/i/' + ident;
2016-01-08 15:32:04 +01:00
request.post({
url: 'https://www.cloudflare.com/api_json.html',
form: {
a: 'zone_file_purge',
tkn: cfconfig.token,
email: cfconfig.email,
z: cfconfig.domain,
url: inv_url
}
}, function(err, response, body) {
if (err) {
console.error("Cache invalidate failed for", ident);
console.error("Body:", body);
return;
}
try {
var result = JSON.parse(body)
if (result.result === 'error') {
console.error("Cache invalidate failed for", ident);
console.error("Message:", msg);
}
} catch(err) {}
});
}
function cf_invalidate(ident, config) {
var cfconfig = config['cloudflare-cache-invalidate']
if (!cfconfig.enabled) {
return;
}
if (config.http.enabled)
cf_do_invalidate(ident, 'http', cfconfig);
if (config.https.enabled)
cf_do_invalidate(ident, 'https', cfconfig);
}
function create_app(config) {
var app = express();
app.locals.config = config
app.use('', express.static(config.path.client));
app.use('/i', express.static(config.path.i));
2016-01-08 15:32:04 +01:00
app.post('/up', handle_upload);
app.get('/del', handle_delete);
return app
}
/* Convert an IP:port string to a split IP and port */
function get_addr_port(s) {
var spl = s.split(":");
if (spl.length === 1)
return { host: spl[0], port: 80 };
else if (spl[0] === '')
return { port: parseInt(spl[1]) };
else
return { host: spl[0], port: parseInt(spl[1]) };
}
function serv(server, serverconfig, callback) {
var ap = get_addr_port(serverconfig.listen);
return server.listen(ap.port, ap.host, callback);
}
2016-02-02 00:44:29 +01:00
function init_defaults(config) {
config.path = config.path ? config.path : {};
config.path.i = config.path.i ? config.path.i : "../i";
config.path.client = config.path.client ? config.path.client : "../client";
}
2016-01-08 15:32:04 +01:00
function init(config) {
2016-02-02 00:44:29 +01:00
init_defaults(config)
2016-01-08 15:32:04 +01:00
var app = create_app(config);
if (config.http.enabled) {
serv(http.createServer(app), config.http, function() {
console.info('Started server at http://%s:%s', this.address().address, this.address().port);
});
}
if (config.https.enabled) {
var sec_creds = {
key: fs.readFileSync(config.https.key),
cert: fs.readFileSync(config.https.cert)
};
serv(https.createServer(sec_creds, app), config.https, function() {
console.info('Started server at https://%s:%s', this.address().address, this.address().port);
});
}
}
function main(configpath) {
init(JSON.parse(fs.readFileSync(configpath)));
}
main('./server.conf')