123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- 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 });
- 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) {
- return '../i/' + path.basename(ident);
- }
- function ident_exists(ident) {
- try {
- fs.lstatSync(ident_path(ident));
- return true;
- } catch (err) {
- return false;
- }
- }
- function cf_do_invalidate(ident, mode, cfconfig) {
- var inv_url = mode + '://' + cfconfig.url + '/i/' + ident;
- 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));
- 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);
- }
- 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";
- }
- function init(config) {
- init_defaults(config)
- 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')
|