mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 13:14:16 +08:00
Add endpoint for file downloads
This commit is contained in:
parent
fd7570f6c8
commit
b3b5a5106c
4 changed files with 91 additions and 4 deletions
|
@ -73,4 +73,40 @@ module.exports = (server) => {
|
|||
})
|
||||
},
|
||||
})
|
||||
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: '/files/{id}/download',
|
||||
config: {
|
||||
description: 'Returns binary data for file with specified id.',
|
||||
notes: 'Notes go here',
|
||||
tags: ['files'],
|
||||
validate: {
|
||||
params: {
|
||||
id: Joi.string(),
|
||||
},
|
||||
},
|
||||
},
|
||||
handler: (request, reply) => {
|
||||
request.getAccountDatabase()
|
||||
.then((db) => {
|
||||
const {headers: {accept}} = request
|
||||
const {params: {id}} = request
|
||||
const account = request.auth.credentials
|
||||
|
||||
db.File.findOne({where: {id}})
|
||||
.then((file) => {
|
||||
if (!file) {
|
||||
return reply.notFound(`File ${id} not found`)
|
||||
}
|
||||
return file.fetch({account, db})
|
||||
.then((stream) => reply(stream))
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('Error fetching file: ', error)
|
||||
reply(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
};
|
||||
|
|
|
@ -73,6 +73,24 @@ class IMAPBox {
|
|||
})
|
||||
}
|
||||
|
||||
fetchStream({messageId, options}) {
|
||||
if (!messageId) {
|
||||
throw new Error("IMAPConnection.fetchStream requires a message identifier.")
|
||||
}
|
||||
if (!options) {
|
||||
throw new Error("IMAPConnection.fetchStream requires an options object.")
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const f = this._imap.fetch(messageId, options);
|
||||
f.on('message', (imapMessage) => {
|
||||
imapMessage.on('body', (stream) => {
|
||||
resolve(stream)
|
||||
})
|
||||
})
|
||||
f.once('error', (error) => reject)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Promise} that resolves to requested message
|
||||
*/
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
const IMAPConnection = require('../../imap-connection')
|
||||
const NylasError = require('../../nylas-error')
|
||||
|
||||
module.exports = (sequelize, Sequelize) => {
|
||||
const File = sequelize.define('file', {
|
||||
accountId: { type: Sequelize.STRING, allowNull: false },
|
||||
version: Sequelize.INTEGER,
|
||||
filename: Sequelize.STRING,
|
||||
contentId: Sequelize.STRING,
|
||||
partId: Sequelize.STRING,
|
||||
contentType: Sequelize.STRING,
|
||||
size: Sequelize.INTEGER,
|
||||
}, {
|
||||
|
@ -13,6 +16,31 @@ module.exports = (sequelize, Sequelize) => {
|
|||
},
|
||||
},
|
||||
instanceMethods: {
|
||||
fetch: function fetch({account, db}) {
|
||||
const settings = Object.assign({}, account.connectionSettings, account.decryptedCredentials())
|
||||
return Promise.props({
|
||||
message: this.getMessage(),
|
||||
connection: IMAPConnection.connect(db, settings),
|
||||
})
|
||||
.then(({message, connection}) => {
|
||||
return message.getCategory()
|
||||
.then((category) => connection.openBox(category.name))
|
||||
.then((imapBox) => imapBox.fetchStream({
|
||||
messageId: message.categoryUID,
|
||||
options: {
|
||||
bodies: [this.partId],
|
||||
struct: true,
|
||||
}
|
||||
}))
|
||||
.then((stream) => {
|
||||
if (stream) {
|
||||
return Promise.resolve(stream)
|
||||
}
|
||||
return Promise.reject(new NylasError(`Unable to fetch binary data for File ${this.id}`))
|
||||
})
|
||||
.finally(() => connection.end())
|
||||
})
|
||||
},
|
||||
toJSON: function toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
|
@ -20,7 +48,7 @@ module.exports = (sequelize, Sequelize) => {
|
|||
account_id: this.accountId,
|
||||
message_id: this.messageId,
|
||||
filename: this.filename,
|
||||
content_id: this.contentId,
|
||||
part_id: this.partId,
|
||||
content_type: this.contentType,
|
||||
size: this.size,
|
||||
};
|
||||
|
|
|
@ -181,14 +181,19 @@ class FetchMessagesInCategory {
|
|||
for (const part of struct) {
|
||||
if (part.constructor === Array) {
|
||||
this._createFilesFromStruct({message, struct: part})
|
||||
} else if (part.disposition) {
|
||||
} else if (part.type !== 'text' && part.disposition) {
|
||||
let filename = null
|
||||
if (part.disposition.params) {
|
||||
filename = part.disposition.params.filename
|
||||
}
|
||||
// Only exposes partId for inline attachments
|
||||
let partId = null
|
||||
if (part.disposition.type === 'inline') {
|
||||
partId = part.partID
|
||||
}
|
||||
File.create({
|
||||
filename: filename,
|
||||
contentId: part.partID,
|
||||
partId: partId,
|
||||
contentType: `${part.type}/${part.subtype}`,
|
||||
accountId: this._db.accountId,
|
||||
size: part.size,
|
||||
|
|
Loading…
Reference in a new issue