134 lines
4.2 KiB
JavaScript
134 lines
4.2 KiB
JavaScript
/* istanbul ignore file */
|
|
'use strict'
|
|
|
|
const debug = require( 'debug' )
|
|
const express = require( 'express' )
|
|
const appRoot = require( 'app-root-path' )
|
|
const Busboy = require( 'busboy' )
|
|
|
|
const cors = require( 'cors' )
|
|
const { matomoMiddleware } = require( `${appRoot}/logging/matomoHelper` )
|
|
const { contextMiddleware, validateScopes, authorizeResolver } = require( `${appRoot}/modules/shared` )
|
|
|
|
const { checkBucket, uploadFile, getFileInfo, getFileStream } = require( './services/fileuploads' )
|
|
const { getStream } = require ( '../core/services/streams' )
|
|
|
|
exports.init = async ( app, options ) => {
|
|
if ( process.env.DISABLE_FILE_UPLOADS ) {
|
|
debug( 'speckle:modules' )( '📄 FileUploads module is DISABLED' )
|
|
return
|
|
} else {
|
|
debug( 'speckle:modules' )( '📄 Init FileUploads module' )
|
|
}
|
|
|
|
if ( !process.env.S3_BUCKET ) {
|
|
debug( 'speckle:modules' )( 'ERROR: S3_BUCKET env variable was not specified. File uploads will be DISABLED.' )
|
|
return
|
|
}
|
|
|
|
await checkBucket()
|
|
|
|
|
|
let checkStreamPermissions = async ( req ) => {
|
|
if ( !req.context || !req.context.auth ) {
|
|
return { hasPermissions: false, httpErrorCode: 401 }
|
|
}
|
|
|
|
try {
|
|
await validateScopes( req.context.scopes, 'streams:write' )
|
|
} catch ( err ) {
|
|
return { hasPermissions: false, httpErrorCode: 401 }
|
|
}
|
|
|
|
try {
|
|
await authorizeResolver( req.context.userId, req.params.streamId, 'stream:contributor' )
|
|
} catch ( err ) {
|
|
return { hasPermissions: false, httpErrorCode: 401 }
|
|
}
|
|
|
|
return { hasPermissions: true, httpErrorCode: 200 }
|
|
}
|
|
|
|
app.get( '/api/file/:fileId', contextMiddleware, matomoMiddleware, async ( req, res ) => {
|
|
if ( process.env.DISABLE_FILE_UPLOADS ) {
|
|
return res.status( 503 ).send( 'File uploads are disabled on this server' )
|
|
}
|
|
|
|
let fileInfo = await getFileInfo( { fileId: req.params.fileId } )
|
|
|
|
if ( !fileInfo )
|
|
return res.status( 404 ).send( 'File not found' )
|
|
|
|
// Check stream read access
|
|
let streamId = fileInfo.streamId
|
|
const stream = await getStream( { streamId: streamId, userId: req.context.userId } )
|
|
|
|
if ( !stream ) {
|
|
return res.status( 404 ).send( 'File stream not found' )
|
|
}
|
|
|
|
if ( !stream.isPublic && req.context.auth === false ) {
|
|
return res.status( 401 ).send( 'You must be logged in to access private streams' )
|
|
}
|
|
|
|
if ( !stream.isPublic ) {
|
|
try {
|
|
await validateScopes( req.context.scopes, 'streams:read' )
|
|
} catch ( err ) {
|
|
return res.status( 401 ).send( 'The provided auth token can\'t read streams' )
|
|
}
|
|
|
|
try {
|
|
await authorizeResolver( req.context.userId, streamId, 'stream:reviewer' )
|
|
} catch ( err ) {
|
|
return res.status( 401 ).send( 'You don\'t have access to this private stream' )
|
|
}
|
|
}
|
|
|
|
let fileStream = await getFileStream( { fileId: req.params.fileId } )
|
|
|
|
res.writeHead( 200, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': `attachment; filename="${fileInfo.fileName}"`, } )
|
|
|
|
fileStream.pipe( res )
|
|
} ),
|
|
|
|
app.post( '/api/file/:fileType/:streamId/:branchName?', contextMiddleware, matomoMiddleware, async ( req, res ) => {
|
|
if ( process.env.DISABLE_FILE_UPLOADS ) {
|
|
return res.status( 503 ).send( 'File uploads are disabled on this server' )
|
|
}
|
|
let { hasPermissions, httpErrorCode } = await checkStreamPermissions( req )
|
|
if ( !hasPermissions ) {
|
|
return res.status( httpErrorCode ).end()
|
|
}
|
|
|
|
let fileUploadPromises = []
|
|
let busboy = new Busboy( { headers: req.headers } )
|
|
|
|
busboy.on( 'file', function( fieldname, file, filename, encoding, mimetype ) {
|
|
let promise = uploadFile( {
|
|
streamId: req.params.streamId,
|
|
branchName: req.params.branchName || '',
|
|
userId: req.context.userId,
|
|
fileName: filename,
|
|
fileType: req.params.fileType,
|
|
fileStream: file
|
|
} )
|
|
fileUploadPromises.push( promise )
|
|
} )
|
|
|
|
busboy.on( 'finish', async function() {
|
|
let fileIds = []
|
|
|
|
for ( let promise of fileUploadPromises )
|
|
{
|
|
let fileId = await promise
|
|
fileIds.push( fileId )
|
|
}
|
|
res.send( fileIds )
|
|
} )
|
|
req.pipe( busboy )
|
|
} )
|
|
}
|
|
|
|
exports.finalize = () => {}
|