Files
speckle-server/packages/server/modules/previews/index.ts
T
Iain Sproat 1c0b6e228d chore(server/middleware): allow CORS to be configurable (#4018)
- renames corsMiddleware to corsMiddlewareFactory to follow naming convention
- optionally allow cors middleware to be configured
- amend direct calls of `cors` package to now use middleware factory
2025-02-18 13:50:34 +00:00

265 lines
9.5 KiB
TypeScript

/* istanbul ignore file */
import { validateScopes, authorizeResolver } from '@/modules/shared'
import { makeOgImage } from '@/modules/previews/ogImage'
import { moduleLogger } from '@/logging/logging'
import { messageProcessor } from '@/modules/previews/resultListener'
import { db } from '@/db/knex'
import {
getObjectPreviewBufferOrFilepathFactory,
sendObjectPreviewFactory,
checkStreamPermissionsFactory
} from '@/modules/previews/services/management'
import {
getObjectPreviewInfoFactory,
createObjectPreviewFactory,
getPreviewImageFactory
} from '@/modules/previews/repository/previews'
import {
getCommitFactory,
getPaginatedBranchCommitsItemsFactory,
legacyGetPaginatedStreamCommitsPageFactory
} from '@/modules/core/repositories/commits'
import { SpeckleModule } from '@/modules/shared/helpers/typeHelper'
import { getStreamFactory } from '@/modules/core/repositories/streams'
import { getPaginatedBranchCommitsItemsByNameFactory } from '@/modules/core/services/commit/retrieval'
import { getStreamBranchByNameFactory } from '@/modules/core/repositories/branches'
import { getFormattedObjectFactory } from '@/modules/core/repositories/objects'
import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
import { listenFor } from '@/modules/core/utils/dbNotificationListener'
import { disablePreviews } from '@/modules/shared/helpers/envHelper'
import { corsMiddlewareFactory } from '@/modules/core/configs/cors'
const httpErrorImage = (httpErrorCode: number) =>
require.resolve(`#/assets/previews/images/preview_${httpErrorCode}.png`)
const noPreviewImage = require.resolve('#/assets/previews/images/no_preview.png')
export const init: SpeckleModule['init'] = (app, isInitial) => {
if (disablePreviews()) {
moduleLogger.warn('📸 Object preview module is DISABLED')
} else {
moduleLogger.info('📸 Init object preview module')
}
app.options('/preview/:streamId/:angle?', corsMiddlewareFactory())
app.get('/preview/:streamId/:angle?', corsMiddlewareFactory(), async (req, res) => {
const projectDb = await getProjectDbClient({ projectId: req.params.streamId })
const checkStreamPermissions = checkStreamPermissionsFactory({
validateScopes,
authorizeResolver,
// getting the stream from the main DB, cause it needs to join on roles
getStream: getStreamFactory({ db })
})
const { hasPermissions, httpErrorCode } = await checkStreamPermissions(req)
if (!hasPermissions) {
// return res.status( httpErrorCode ).end()
return res.sendFile(httpErrorImage(httpErrorCode))
}
const getCommitsByStreamId = legacyGetPaginatedStreamCommitsPageFactory({
db: projectDb
})
const { commits } = await getCommitsByStreamId({
streamId: req.params.streamId,
limit: 1,
ignoreGlobalsBranch: true,
cursor: undefined
})
if (!commits || commits.length === 0) {
return res.sendFile(noPreviewImage)
}
const lastCommit = commits[0]
const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: createObjectPreviewFactory({ db: projectDb }),
getPreviewImage: getPreviewImageFactory({ db: projectDb }),
logger: req.log
})
const sendObjectPreview = sendObjectPreviewFactory({
// getting the stream from the projectDb here, to handle preview data properly
getStream: getStreamFactory({ db: projectDb }),
getObjectPreviewBufferOrFilepath,
makeOgImage
})
return sendObjectPreview(
req,
res,
req.params.streamId,
lastCommit.referencedObject,
req.params.angle
)
})
app.options(
'/preview/:streamId/branches/:branchName/:angle?',
corsMiddlewareFactory()
)
app.get(
'/preview/:streamId/branches/:branchName/:angle?',
corsMiddlewareFactory(),
async (req, res) => {
const checkStreamPermissions = checkStreamPermissionsFactory({
validateScopes,
authorizeResolver,
// getting the stream from the main DB, cause it needs to join on roles
getStream: getStreamFactory({ db })
})
const { hasPermissions, httpErrorCode } = await checkStreamPermissions(req)
if (!hasPermissions) {
// return res.status( httpErrorCode ).end()
return res.sendFile(httpErrorImage(httpErrorCode))
}
const projectDb = await getProjectDbClient({ projectId: req.params.streamId })
let commitsObj
try {
const getCommitsByBranchName = getPaginatedBranchCommitsItemsByNameFactory({
getStreamBranchByName: getStreamBranchByNameFactory({ db: projectDb }),
getPaginatedBranchCommitsItems: getPaginatedBranchCommitsItemsFactory({
db: projectDb
})
})
commitsObj = await getCommitsByBranchName({
streamId: req.params.streamId,
branchName: req.params.branchName,
limit: 1,
cursor: undefined
})
} catch {
commitsObj = {}
}
const { commits } = commitsObj
if (!commits || commits.length === 0) {
return res.sendFile(noPreviewImage)
}
const lastCommit = commits[0]
const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: createObjectPreviewFactory({ db: projectDb }),
getPreviewImage: getPreviewImageFactory({ db: projectDb }),
logger: req.log
})
const sendObjectPreview = sendObjectPreviewFactory({
// getting the stream from the projectDb here, to handle preview data properly
getStream: getStreamFactory({ db: projectDb }),
getObjectPreviewBufferOrFilepath,
makeOgImage
})
return sendObjectPreview(
req,
res,
req.params.streamId,
lastCommit.referencedObject,
req.params.angle
)
}
)
app.options('/preview/:streamId/commits/:commitId/:angle?', corsMiddlewareFactory())
app.get(
'/preview/:streamId/commits/:commitId/:angle?',
corsMiddlewareFactory(),
async (req, res) => {
const checkStreamPermissions = checkStreamPermissionsFactory({
validateScopes,
authorizeResolver,
// getting the stream from the main DB, cause it needs to join on roles
getStream: getStreamFactory({ db })
})
const { hasPermissions, httpErrorCode } = await checkStreamPermissions(req)
if (!hasPermissions) {
// return res.status( httpErrorCode ).end()
return res.sendFile(httpErrorImage(httpErrorCode))
}
const projectDb = await getProjectDbClient({ projectId: req.params.streamId })
const getCommit = getCommitFactory({ db: projectDb })
const commit = await getCommit(req.params.commitId, {
streamId: req.params.streamId
})
if (!commit) return res.sendFile(noPreviewImage)
const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: createObjectPreviewFactory({ db: projectDb }),
getPreviewImage: getPreviewImageFactory({ db: projectDb }),
logger: req.log
})
const sendObjectPreview = sendObjectPreviewFactory({
// getting the stream from the projectDb here, to handle preview data properly
getStream: getStreamFactory({ db: projectDb }),
getObjectPreviewBufferOrFilepath,
makeOgImage
})
return sendObjectPreview(
req,
res,
req.params.streamId,
commit.referencedObject,
req.params.angle
)
}
)
app.options('/preview/:streamId/objects/:objectId/:angle?', corsMiddlewareFactory())
app.get(
'/preview/:streamId/objects/:objectId/:angle?',
corsMiddlewareFactory(),
async (req, res) => {
const checkStreamPermissions = checkStreamPermissionsFactory({
validateScopes,
authorizeResolver,
// getting the stream from the main DB, cause it needs to join on roles
getStream: getStreamFactory({ db })
})
const { hasPermissions } = await checkStreamPermissions(req)
if (!hasPermissions) {
return res.status(403).end()
}
const projectDb = await getProjectDbClient({ projectId: req.params.streamId })
const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: createObjectPreviewFactory({ db: projectDb }),
getPreviewImage: getPreviewImageFactory({ db: projectDb }),
logger: req.log
})
const sendObjectPreview = sendObjectPreviewFactory({
// getting the stream from the projectDb here, to handle preview data properly
getStream: getStreamFactory({ db: projectDb }),
getObjectPreviewBufferOrFilepath,
makeOgImage
})
return sendObjectPreview(
req,
res,
req.params.streamId,
req.params.objectId,
req.params.angle
)
}
)
if (isInitial) {
listenFor('preview_generation_update', messageProcessor)
}
}
export const finalize = () => {}