bde148f286
* wip * some extra fixes * stuff kinda works? * need to figure out mocks * need to figure out mocks * fix db listener * gqlgen fix * minor gqlgen watch adjustment * lint fixes * delete old codegen file * converting migrations to ESM * getModuleDIrectory * vitest sort of works * added back ts-vitest * resolve gql double load * fixing test timeout configs * TSC lint fix * fix automate tests * moar debugging * debugging * more debugging * codegen update * server works * yargs migrated * chore(server): getting rid of global mocks for Server ESM (#5046) * got rid of email mock * got rid of comment mocks * got rid of multi region mocks * got rid of stripe mock * admin override mock updated * removed final mock * fixing import.meta.resolve calls * another import.meta.resolve fix * added requested test * nyc ESM fix * removed unneeded deps + linting * yarn lock forgot to commit * tryna fix flakyness * email capture util fix * sendEmail fix * fix TSX check * sender transporter fix + CR comments * merge main fix * test fixx * circleci fix * gqlgen bigint fix * error formatter fix * more error formatting improvements * esmloader added to Dockerfile * more dockerfile fixes * bg jobs fix
217 lines
6.9 KiB
TypeScript
217 lines
6.9 KiB
TypeScript
import {
|
|
allowForAllRegisteredUsersOnPublicStreamsWithPublicComments,
|
|
allowForRegisteredUsersOnPublicStreamsEvenWithoutRole,
|
|
allowAnonymousUsersOnPublicStreams,
|
|
streamCommentsWritePermissionsPipelineFactory,
|
|
streamReadPermissionsPipelineFactory
|
|
} from '@/modules/shared/authz'
|
|
import { authMiddlewareCreator } from '@/modules/shared/middleware'
|
|
import { isArray } from 'lodash-es'
|
|
import { UnauthorizedError } from '@/modules/shared/errors'
|
|
import {
|
|
getAllStreamBlobIdsFactory,
|
|
getBlobMetadataFactory,
|
|
getBlobMetadataCollectionFactory,
|
|
deleteBlobFactory
|
|
} from '@/modules/blobstorage/repositories'
|
|
import { db } from '@/db/knex'
|
|
import {
|
|
getFileStreamFactory,
|
|
fullyDeleteBlobFactory
|
|
} from '@/modules/blobstorage/services/management'
|
|
import { Router } from 'express'
|
|
import { getProjectObjectStorage } from '@/modules/multiregion/utils/blobStorageSelector'
|
|
import {
|
|
deleteObjectFactory,
|
|
getObjectStreamFactory
|
|
} from '@/modules/blobstorage/repositories/blobs'
|
|
import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
|
|
import { getStreamFactory } from '@/modules/core/repositories/streams'
|
|
import { processNewFileStreamFactory } from '@/modules/blobstorage/services/streams'
|
|
import { UserInputError } from '@/modules/core/errors/userinput'
|
|
import { createBusboy } from '@/modules/blobstorage/rest/busboy'
|
|
import contentDisposition from 'content-disposition'
|
|
|
|
export const blobStorageRouterFactory = (): Router => {
|
|
const processNewFileStream = processNewFileStreamFactory()
|
|
|
|
const app = Router()
|
|
|
|
app.post(
|
|
'/api/stream/:streamId/blob',
|
|
async (req, res, next) => {
|
|
await authMiddlewareCreator(
|
|
streamCommentsWritePermissionsPipelineFactory({
|
|
getStream: getStreamFactory({ db })
|
|
})
|
|
)(req, res, next)
|
|
},
|
|
async (req, res) => {
|
|
const streamId = req.params.streamId
|
|
const userId = req.context.userId
|
|
if (!userId) throw new UnauthorizedError()
|
|
req.log = req.log.child({ streamId, userId })
|
|
req.log.debug('Uploading blob.')
|
|
|
|
const busboy = createBusboy(req)
|
|
const newFileStreamProcessor = await processNewFileStream({
|
|
busboy,
|
|
streamId,
|
|
userId,
|
|
logger: req.log,
|
|
onFinishAllFileUploads: async (uploadResults) => {
|
|
res.status(201).send({ uploadResults })
|
|
},
|
|
onError: () => {
|
|
res.contentType('application/json')
|
|
res
|
|
.status(400)
|
|
.end(
|
|
'{ "error": "Upload request error. The server logs may have more details." }'
|
|
)
|
|
}
|
|
})
|
|
req.pipe(newFileStreamProcessor)
|
|
}
|
|
)
|
|
|
|
app.post(
|
|
'/api/stream/:streamId/blob/diff',
|
|
async (req, res, next) => {
|
|
await authMiddlewareCreator([
|
|
...streamReadPermissionsPipelineFactory({
|
|
getStream: getStreamFactory({ db })
|
|
}),
|
|
allowForAllRegisteredUsersOnPublicStreamsWithPublicComments,
|
|
allowForRegisteredUsersOnPublicStreamsEvenWithoutRole,
|
|
allowAnonymousUsersOnPublicStreams
|
|
])(req, res, next)
|
|
},
|
|
async (req, res) => {
|
|
if (!isArray(req.body)) {
|
|
throw new UserInputError('An array of blob IDs expected in the body.')
|
|
}
|
|
|
|
const projectDb = await getProjectDbClient({ projectId: req.params.streamId })
|
|
|
|
const getAllStreamBlobIds = getAllStreamBlobIdsFactory({ db: projectDb })
|
|
const bq = await getAllStreamBlobIds({ streamId: req.params.streamId })
|
|
const unknownBlobIds = [...req.body].filter(
|
|
(id) => bq.findIndex((bInfo) => bInfo.id === id) === -1
|
|
)
|
|
res.send(unknownBlobIds)
|
|
}
|
|
)
|
|
|
|
app.get(
|
|
'/api/stream/:streamId/blob/:blobId',
|
|
async (req, res, next) => {
|
|
await authMiddlewareCreator([
|
|
...streamReadPermissionsPipelineFactory({
|
|
getStream: getStreamFactory({ db })
|
|
}),
|
|
allowForAllRegisteredUsersOnPublicStreamsWithPublicComments,
|
|
allowForRegisteredUsersOnPublicStreamsEvenWithoutRole,
|
|
allowAnonymousUsersOnPublicStreams
|
|
])(req, res, next)
|
|
},
|
|
async (req, res) => {
|
|
const streamId = req.params.streamId
|
|
const [projectDb, projectStorage] = await Promise.all([
|
|
getProjectDbClient({ projectId: streamId }),
|
|
getProjectObjectStorage({ projectId: streamId })
|
|
])
|
|
|
|
const getBlobMetadata = getBlobMetadataFactory({ db: projectDb })
|
|
const getFileStream = getFileStreamFactory({ getBlobMetadata })
|
|
const getObjectStream = getObjectStreamFactory({
|
|
storage: projectStorage.private
|
|
})
|
|
|
|
const { fileName } = await getBlobMetadata({
|
|
streamId: req.params.streamId,
|
|
blobId: req.params.blobId
|
|
})
|
|
const fileStream = await getFileStream({
|
|
getObjectStream,
|
|
streamId: req.params.streamId,
|
|
blobId: req.params.blobId
|
|
})
|
|
res.writeHead(200, {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Disposition': contentDisposition(fileName)
|
|
})
|
|
fileStream.pipe(res)
|
|
}
|
|
)
|
|
|
|
app.delete(
|
|
'/api/stream/:streamId/blob/:blobId',
|
|
async (req, res, next) => {
|
|
await authMiddlewareCreator(
|
|
streamCommentsWritePermissionsPipelineFactory({
|
|
getStream: getStreamFactory({ db })
|
|
})
|
|
)(req, res, next)
|
|
},
|
|
async (req, res) => {
|
|
const streamId = req.params.streamId
|
|
const [projectDb, projectStorage] = await Promise.all([
|
|
getProjectDbClient({ projectId: streamId }),
|
|
getProjectObjectStorage({ projectId: streamId })
|
|
])
|
|
|
|
const getBlobMetadata = getBlobMetadataFactory({ db: projectDb })
|
|
const deleteObject = deleteObjectFactory({ storage: projectStorage.private })
|
|
const deleteBlob = fullyDeleteBlobFactory({
|
|
getBlobMetadata,
|
|
deleteBlob: deleteBlobFactory({ db: projectDb }),
|
|
deleteObject
|
|
})
|
|
|
|
await deleteBlob({
|
|
streamId: req.params.streamId,
|
|
blobId: req.params.blobId
|
|
})
|
|
res.status(204).send()
|
|
}
|
|
)
|
|
|
|
app.get(
|
|
'/api/stream/:streamId/blobs',
|
|
async (req, res, next) => {
|
|
await authMiddlewareCreator(
|
|
streamReadPermissionsPipelineFactory({
|
|
getStream: getStreamFactory({ db })
|
|
})
|
|
)(req, res, next)
|
|
},
|
|
async (req, res) => {
|
|
let fileName = req.query.fileName //filename can be undefined or null, and that returns all blobs
|
|
if (isArray(fileName)) {
|
|
fileName = fileName[0]
|
|
}
|
|
|
|
const streamId = req.params.streamId
|
|
|
|
const projectDb = await getProjectDbClient({ projectId: req.params.streamId })
|
|
const getBlobMetadataCollection = getBlobMetadataCollectionFactory({
|
|
db: projectDb
|
|
})
|
|
|
|
const blobMetadataCollection = await getBlobMetadataCollection({
|
|
streamId,
|
|
query: fileName as string
|
|
})
|
|
|
|
return res.status(200).send(blobMetadataCollection)
|
|
}
|
|
)
|
|
|
|
app.delete('/api/stream/:streamId/blobs', async (_req, res) => {
|
|
return res.status(501).send('This method is not implemented yet.')
|
|
})
|
|
|
|
return app
|
|
}
|