diff --git a/upload-server/.gitignore b/upload-server/.gitignore new file mode 100644 index 000000000..af66d66fc --- /dev/null +++ b/upload-server/.gitignore @@ -0,0 +1,4 @@ +uploads +node_modules +yarn* +.gitfoo diff --git a/upload-server/README.md b/upload-server/README.md new file mode 100644 index 000000000..daf2f6d51 --- /dev/null +++ b/upload-server/README.md @@ -0,0 +1,17 @@ +# deltachat-upload-server + +Demo server for the HTTP file upload feature. + +### Usage + +``` +npm install +node server.js +``` + +Configure with environment variables: +* `UPLOAD_PATH`: Path to upload files to (default: `./uploads`) +* `PORT`: Port to listen on (default: `8080`) +* `HOSTNAME`: Hostname to listen on (default: `0.0.0.0`) +* `BASEURL`: Base URL for generated links (default: `http://[hostname]:[port]/`) + diff --git a/upload-server/package.json b/upload-server/package.json new file mode 100644 index 000000000..f5f0b3c33 --- /dev/null +++ b/upload-server/package.json @@ -0,0 +1,12 @@ +{ + "name": "deltachat-upload-server", + "version": "1.0.0", + "main": "server.js", + "license": "MIT", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.17.1" + } +} diff --git a/upload-server/server.js b/upload-server/server.js new file mode 100644 index 000000000..d7d8259e9 --- /dev/null +++ b/upload-server/server.js @@ -0,0 +1,53 @@ +const p = require('path') +const crypto = require('crypto') +const express = require('express') +const fs = require('fs') +const { pipeline } = require('stream') + +const app = express() + +const config = { + path: process.env.UPLOAD_PATH || p.resolve('./uploads'), + port: process.env.PORT || 8080, + hostname: process.env.HOSTNAME || '0.0.0.0', + baseurl: process.env.BASE_URL || null +} + +if (!config.baseurl.endsWith('/')) config.baseurl = config.baseurl + '/' + +if (!fs.existsSync(config.path)) { + fs.mkdirSync(config.path, { recursive: true }) +} + +const baseUrl = config.baseurl || `http://${config.hostname}:${config.port}/` + +app.post('*', (req, res) => { + const filename = crypto.randomBytes(12).toString('hex') + const ws = fs.createWriteStream(p.join(config.path, filename)) + pipeline(req, ws, err => { + if (err) console.error(err) + if (err) res.status(500).send(err.message) + const url = baseUrl + filename + console.log('file uploaded: ' + filename) + res.send(url) + }) +}) + +app.get('/:filename', (req, res) => { + const filepath = p.normalize(p.join(config.path, req.params.filename)) + if (!filepath.startsWith(config.path)) { + return res.code(500).send('bad request') + } + const rs = fs.createReadStream(filepath) + res.setHeader('content-type', 'application/octet-stream') + pipeline(rs, res, err => { + if (err) console.error(err) + if (err) res.status(500).send(err.message) + res.end() + }) +}) + +app.listen(config.port, err => { + if (err) console.error(err) + else console.log('Listening on ' + baseUrl) +})