mirror of
https://github.com/chatmail/core.git
synced 2026-04-21 15:36:30 +03:00
Move file upload into SMTP send job.
- This adds params for the upload URL and local file path, so that the actual upload can happen in the send job. - This also moves the URL generation to the client side so that we can generate a valid URL before the upload (because the MIME rendering of the mail message happens earlier and we want to include the URL there)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
const p = require('path')
|
||||
const crypto = require('crypto')
|
||||
const express = require('express')
|
||||
const fs = require('fs')
|
||||
const { pipeline } = require('stream')
|
||||
@@ -10,44 +9,65 @@ 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
|
||||
baseurl: process.env.BASE_URL
|
||||
}
|
||||
|
||||
if (!config.baseurl) config.baseurl = `http://${config.hostname}:${config.port}/`
|
||||
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.use('/:filename', checkFilenameMiddleware)
|
||||
app.put('/:filename', (req, res) => {
|
||||
const uploadpath = req.uploadpath
|
||||
const filename = req.params.filename
|
||||
fs.stat(uploadpath, (err, stat) => {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
console.error('error', err.message)
|
||||
return res.code(500).send('internal server error')
|
||||
}
|
||||
if (stat) return res.status(500).send('filename in use')
|
||||
|
||||
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)
|
||||
const ws = fs.createWriteStream(uploadpath)
|
||||
pipeline(req, ws, err => {
|
||||
if (err) {
|
||||
console.error('error', err.message)
|
||||
return res.status(500).send('internal server error')
|
||||
}
|
||||
console.log('file uploaded: ' + uploadpath)
|
||||
const url = config.baseurl + filename
|
||||
res.end(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)
|
||||
const uploadpath = req.uploadpath
|
||||
const rs = fs.createReadStream(uploadpath)
|
||||
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()
|
||||
if (err) console.error('error', err.message)
|
||||
if (err) return res.status(500).send(err.message)
|
||||
})
|
||||
})
|
||||
|
||||
function checkFilenameMiddleware (req, res, next) {
|
||||
const filename = req.params.filename
|
||||
if (!filename) return res.status(500).send('missing filename')
|
||||
if (!filename.match(/^[a-zA-Z0-9]{26,32}$/)) {
|
||||
return res.status(500).send('illegal filename')
|
||||
}
|
||||
const uploadpath = p.normalize(p.join(config.path, req.params.filename))
|
||||
if (!uploadpath.startsWith(config.path)) {
|
||||
return res.code(500).send('bad request')
|
||||
}
|
||||
req.uploadpath = uploadpath
|
||||
next()
|
||||
}
|
||||
|
||||
app.listen(config.port, err => {
|
||||
if (err) console.error(err)
|
||||
else console.log('Listening on ' + baseUrl)
|
||||
else console.log(`Listening on ${config.baseurl}`)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user