ed458fb619
* feat(server): add server authz pipeline rework first sketch * feat(server authz): add new server authz middleware poc implementation * test(server authz): add unittests for the new server authz workflow * feat(wip rework of fileuploads vs blob storage): add basim impl of separate blob storage service * feat(fileimport service): refactored file import service to utilize the new asssetstorage service * refactor(server errors): refactor server errors to use the shared module definitions Now all the errors inherit from BaseError * refactor(fileimport service): cleanup after refactor * feat(frontend fileimports): use the new blob storage for downloading the original file * refactor(server fileimports): clean up the remnants of S3 storage from file imports * refactor(server authz): centralize generic authz pipeline configs * refactor(server blob storage): refactor / rename everything to use the `blob-storage` name * ci(circleci): add s3 objectstorage environment variables * ci(circleci): fix missing env variables * ci(circleci): add minio test container * ci(circleci): fix minio app startup * ci(circleci): enable circleci remote docker * ci(circleci): fix minio startup * ci(cirleci): detach and wait properly for minio to start * ci(circleci): revert to additional minio img config, it only fails when the container is stopped ?! * ci(circleci): disable file uploads * fix(fileimports): update with blob storage refactor leftovers * feat(server blob storage): add blob storage graphql api * refactor(server errors): merge new errors to shared module * fix(server comments rte): fix import for RTE error * chore(fileimports): remove node-fetch from dependency * chore(server): remove body parser dependency * fix(server blob storage): fix gql api * fix(frontend): fix fileupload item not loading the new upload status, cause of premature event fire * feat(server blob storage): fix file size limit and allow for public streams * Update packages/server/modules/blobstorage/graph/schemas/blobstorage.graphql Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com> * chore(blobstorage): fix PR review issues * fix(server): fix import bugs Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
108 lines
3.6 KiB
JavaScript
108 lines
3.6 KiB
JavaScript
const knex = require('@/db/knex')
|
|
const { NotFoundError, ResourceMismatch } = require('@/modules/shared/errors')
|
|
const BlobStorage = () => knex('blob_storage')
|
|
|
|
const blobLookup = ({ blobId }) => BlobStorage().where({ id: blobId })
|
|
|
|
const uploadFileStream = async (
|
|
storeFileStream,
|
|
{ streamId, userId },
|
|
{ blobId, fileName, fileType, fileStream }
|
|
) => {
|
|
const objectKey = `assets/${streamId}/${blobId}`
|
|
const dbFile = {
|
|
id: blobId,
|
|
streamId,
|
|
userId,
|
|
objectKey,
|
|
fileName,
|
|
fileType
|
|
}
|
|
// need to insert the upload data before starting otherwise the upload finished
|
|
// even might fire faster, than the db insert, causing missing asset data in the db
|
|
await BlobStorage().insert(dbFile)
|
|
const { fileHash } = await storeFileStream({ objectKey, fileStream })
|
|
return { blobId, fileName, fileHash }
|
|
}
|
|
|
|
const getBlobMetadata = async ({ streamId, blobId }) => {
|
|
const obj = (await blobLookup({ blobId }).first()) || null
|
|
if (!obj) throw new NotFoundError(`The requested asset: ${blobId} doesn't exist`)
|
|
if (!streamId) throw new ResourceMismatch('No steamId provided')
|
|
if (obj.streamId !== streamId)
|
|
throw new ResourceMismatch("The stream doesn't have the given resource")
|
|
return obj
|
|
}
|
|
|
|
const blobQuery = ({ streamId, query }) => {
|
|
let blobs = BlobStorage().where({ streamId })
|
|
if (query) blobs = blobs.andWhereLike('fileName', `%${query}%`)
|
|
return blobs
|
|
}
|
|
|
|
const getBlobMetadataCollection = async ({ streamId, query, limit, cursor }) => {
|
|
const cursorTarget = 'createdAt'
|
|
const limitMax = 25
|
|
const queryLimit = limit && limit < limitMax ? limit : limitMax
|
|
const blobs = blobQuery({ streamId, query })
|
|
.orderBy(cursorTarget, 'desc')
|
|
.limit(queryLimit)
|
|
if (cursor) query.andWhere(cursorTarget, '<', cursor)
|
|
|
|
const rows = await blobs
|
|
return {
|
|
blobs: rows,
|
|
cursor: rows.length > 0 ? rows[rows.length - 1][cursorTarget].toISOString() : null
|
|
}
|
|
}
|
|
|
|
const blobCollectionSummary = async ({ streamId, query }) => {
|
|
const [summary] = await blobQuery({ streamId, query }).sum('fileSize').count('id')
|
|
return { totalSize: summary.sum ?? 0, totalCount: summary.count }
|
|
}
|
|
|
|
const getFileStream = async ({ getObjectStream, streamId, blobId }) => {
|
|
const { objectKey } = await getBlobMetadata({ streamId, blobId })
|
|
return await getObjectStream({ objectKey })
|
|
}
|
|
|
|
const markUploadSuccess = async (getObjectAttributes, streamId, blobId) =>
|
|
await updateBlobMetadata(streamId, blobId, async ({ objectKey }) => {
|
|
const { fileSize } = await getObjectAttributes({ objectKey })
|
|
return { uploadStatus: 1, fileSize }
|
|
})
|
|
|
|
const markUploadOverFileSizeLimit = async (deleteObject, streamId, blobId) =>
|
|
await markUploadError(deleteObject, streamId, blobId, 'File size limit reached')
|
|
|
|
const markUploadError = async (deleteObject, streamId, blobId, error) =>
|
|
await updateBlobMetadata(streamId, blobId, async ({ objectKey }) => {
|
|
await deleteObject({ objectKey })
|
|
return { uploadStatus: 2, uploadError: error }
|
|
})
|
|
|
|
const deleteBlob = async ({ streamId, blobId, deleteObject }) => {
|
|
const { objectKey } = await getBlobMetadata({ streamId, blobId })
|
|
await deleteObject({ objectKey })
|
|
await blobLookup({ blobId }).del()
|
|
}
|
|
|
|
const updateBlobMetadata = async (streamId, blobId, updateCallback) => {
|
|
const { objectKey, fileName } = await getBlobMetadata({ streamId, blobId })
|
|
const updateData = await updateCallback({ objectKey })
|
|
await blobLookup({ blobId }).update(updateData)
|
|
return { blobId, fileName, ...updateData }
|
|
}
|
|
|
|
module.exports = {
|
|
getBlobMetadata,
|
|
uploadFileStream,
|
|
markUploadSuccess,
|
|
markUploadOverFileSizeLimit,
|
|
markUploadError,
|
|
getFileStream,
|
|
deleteBlob,
|
|
getBlobMetadataCollection,
|
|
blobCollectionSummary
|
|
}
|