Files
speckle-server/packages/server/modules/fileuploads/index.js
T
2022-02-07 14:04:26 +02:00

135 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 = Busboy( { headers: req.headers } )
busboy.on( 'file', ( name, file, info ) => {
const { filename, encoding, mimeType } = info
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 = () => {}