Promise = require "bluebird" router = require("express-promise-router")() errors = require "errors" busboy = require "connect-busboy" path = require "path" fs = require "fs" rfr = require "rfr" config = rfr "config.json" randomString = rfr "lib/random-string" streamContains = rfr "lib/stream-contains" tapError = rfr "lib/tap-error" disableInMaintenanceMode = rfr "lib/disable-in-maintenance-mode" router.post "/", disableInMaintenanceMode, busboy(limits: {fileSize: (config.upload_size_limit ? (150 * 1024 * 1024))}), (req, res) -> Promise.try -> Promise.all [ randomString 16 randomString 16 ] .spread (slugID, fileID) -> storagePath = path.join config.storage_path, fileID Promise.try -> handleUpload req, res, storagePath: storagePath, fieldName: "file" .then -> headStream = fs.createReadStream storagePath, end: 1023 streamContains headStream, "%PDF" .then (isPDF) -> if isPDF Promise.resolve() else Promise.reject new errors.InvalidFiletype "The file you uploaded is not a valid PDF file." .then -> objData = SlugId: slugID Public: (req.body.visibility == "public") Views: 0 OriginalFilename: req.files.file.filename Filename: fileID Mirrored: 0 CDN: 0 Thumbnailed: 0 Uploaded: new Date() DeleteKey: "" req.model("Document").forge objData .save() .then (model) -> if config.storage.ia and model.get("Public") == true req.taskRunner.do "mirror", id: slugID req.taskRunner.do "thumbnail", id: slugID res.json {redirect: "/d/#{slugID}"} .catch tapError (err) -> # This is like a .tap, but for errors - it removes the file that was just uploaded. fs.unlink storagePath, (err) -> if err req.reportError err # TODO (CDN) SPEC: Then: (tasks abstracted to task file) # * Create task: Tahoe-LAFS upload, update DB entry # * Check if all tasks completed; if yes, remove file (but only if config says local storage is disabled). handleUpload = (req, res, options) -> if not req.busboy? return Promise.resolve() else return new Promise (resolve, reject) -> processFields req, res, options processFiles req, res, options req.busboy.on "finish", -> resolve() req.pipe req.busboy processFields = (req, res, options) -> req.body ?= {} req.busboy.on "field", (name, value, keyTruncated, valueTruncated) -> req.body[name] = value processFiles = (req, res, options) -> req.busboy.on "file", (name, file, filename, encoding, mimetype) -> # TODO: Is the encoding taken care of automatically...? if name != options.fieldName # This is not the correct form field. Ignore it. return file .on "limit", -> # The maximum file size was exceeded. file.unpipe() reject new errors.UploadTooLarge "The file you attempted to upload is too large." .pipe fs.createWriteStream(options.storagePath) req.files ?= {} req.files[name] = filename: filename encoding: encoding mimetype: mimetype storagePath: options.storagePath module.exports = router