From 1b250435bb958a1acab9c1d92f1ac5ac90cd001c Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Mon, 13 May 2019 14:42:55 +0300 Subject: [PATCH] Simple file uploading, no automatic cleanup yet --- applications/distrib-ipfs/index.js | 2 + applications/tempfiles/.gitignore | 1 + applications/tempfiles/index.js | 115 ++++++++++++++++++ .../tempfiles/migrations/001-initial.sql | 10 ++ package.json | 2 + server.js | 9 +- 6 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 applications/tempfiles/.gitignore create mode 100644 applications/tempfiles/index.js create mode 100644 applications/tempfiles/migrations/001-initial.sql diff --git a/applications/distrib-ipfs/index.js b/applications/distrib-ipfs/index.js index 41c04dc..031db3e 100644 --- a/applications/distrib-ipfs/index.js +++ b/applications/distrib-ipfs/index.js @@ -18,6 +18,8 @@ async function init () { .then(db => db.migrate({ migrationsPath: path.join(__dirname, 'migrations') })) router.get('/:hash', async (req, res, next) => { + if (!req.params.hash) return res.status(400).send('Invalid request') + let db = await dbPromise let translation = await db.get('SELECT * FROM Translation WHERE translation = ?', req.params.hash) if (!translation) return res.status(404).end() diff --git a/applications/tempfiles/.gitignore b/applications/tempfiles/.gitignore new file mode 100644 index 0000000..98e6ef6 --- /dev/null +++ b/applications/tempfiles/.gitignore @@ -0,0 +1 @@ +*.db diff --git a/applications/tempfiles/index.js b/applications/tempfiles/index.js new file mode 100644 index 0000000..be5d690 --- /dev/null +++ b/applications/tempfiles/index.js @@ -0,0 +1,115 @@ +const express = require('express') +const multiparty = require('multiparty') +const path = require('path') +const fsa = require('fs') +const fs = fsa.promises +const sqlite = require('sqlite') + +const router = express.Router() +const cfgLoader = require(path.join('..', '..', 'config-loader'))(path.join(__dirname, 'config.json'), { + database: 'index.db', + root: './files', + expiry: 2246400, + baseurl: 'https://i.lunasqu.ee/', + tokens: {} +}) + +function asyncForm (req, form) { + return new Promise(function (resolve, reject) { + form.parse(req, function(err, fields, files) { + if (err) return reject(err) + resolve({ fields, files }) + }) + }) +} + +async function init () { + let config = await cfgLoader + let root = path.resolve(config.root) + let baseurl = config.baseurl + + await fs.access(root, fsa.constants.F_OK) + + const dbPromise = Promise.resolve() + .then(() => sqlite.open(path.join(__dirname, config.database), { Promise, cache: true })) + .then(db => db.migrate({ migrationsPath: path.join(__dirname, 'migrations') })) + + router.get('/:hash', async (req, res, next) => { + if (!req.params.hash) return res.status(400).send('Invalid request') + + let db = await dbPromise + let file = await db.get('SELECT * FROM File WHERE path = ?', req.params.hash) + if (!file) return res.status(404).end() + + res.header('Cache-Control', 'max-age=' + 7 * 24 * 60 * 60 * 1000) + res.sendFile(path.join(root, file.path)) + }) + + router.post('/publish', async (req, res, next) => { + let token = req.header('token') || req.body.token + if (!token || !config.tokens[token]) return res.status(402).send('Forbidden') + + // Handle multipart data + let form = new multiparty.Form() + let { fields, files } = await asyncForm(req, form) + + // Detect all files + let allFiles = [] + for (let i in files) { + for (let j in files[i]) { + allFiles.push(files[i][j]) + } + } + + if (!allFiles.length) return res.status(400).send('Invalid request') + + // Determine real IP address + let ip + if (req.header('x-forwarded-for')) { + ip = req.header('x-forwarded-for') + } else { + ip = req.connection.remoteAddress + } + + console.log('[%s] from %s request to upload %d file(s)', new Date(), ip, allFiles.length) + + // Handle all files provided + let db = await dbPromise + let uploadedFiles = [] + for (let i in allFiles) { + let file = allFiles[i] + let fname = file.originalFilename + let target = path.join(root, fname) + + // Handle already exists case (overwrite) + try { + await fs.access(target, fsa.constants.F_OK) + await fs.unlink(target) + await fs.copyFile(file.path, target) + await fs.unlink(file.path) + + await db.run('UPDATE File SET ip = ?, upload = ? WHERE path = ?', ip, new Date(), fname) + + uploadedFiles.push(baseurl + fname) + continue + } catch (e) { + if (e.code !== 'ENOENT') throw e + } + + // Copy to target and unlink temporary file + await fs.copyFile(file.path, target) + await fs.unlink(file.path) + + await db.run('INSERT INTO File (path,ip,upload) VALUES (?,?,?)', fname, ip, new Date()) + uploadedFiles.push(baseurl + fname) + } + + if (uploadedFiles.length === 0) return res.status(400).send('No files were uploaded') + + res.send(uploadedFiles.join('\n')) + }) + + return router +} + +module.exports = init diff --git a/applications/tempfiles/migrations/001-initial.sql b/applications/tempfiles/migrations/001-initial.sql new file mode 100644 index 0000000..bc3ce61 --- /dev/null +++ b/applications/tempfiles/migrations/001-initial.sql @@ -0,0 +1,10 @@ +-- Up + +CREATE TABLE File ( + path TEXT, + ip TEXT, + upload TEXT +); + +-- Down +DROP TABLE File; diff --git a/package.json b/package.json index ad86bb6..62dce8b 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "dependencies": { "body-parser": "^1.18.3", "express": "^4.16.4", + "express-async-errors": "^3.1.1", + "multiparty": "^4.2.1", "sqlite": "^3.0.1" } } diff --git a/server.js b/server.js index 2e6630d..0e93d03 100644 --- a/server.js +++ b/server.js @@ -6,7 +6,9 @@ const fsPromises = fs.promises const app = express() -let apps = process.env.APPS ? JSON.parse(process.env.APPS) : ['highlight-termbin', 'dyndns', 'distrib-ipfs'] +require('express-async-errors') + +let apps = process.env.APPS ? JSON.parse(process.env.APPS) : ['highlight-termbin', 'dyndns', 'tempfiles'] let sock = process.env.SOCKET || '/tmp/serve-lunasqu.ee.t.sock' app.enable('trust proxy', 1) @@ -52,6 +54,11 @@ async function init () { try { await fsPromises.unlink(sock) } catch (e) {} + app.use(function (err, req, res, next) { + console.error(err) + res.status(500).end('Server error') + }) + app.listen(sock, function () { console.log('Started server on', sock) })