Files
speckle-server/packages/server/modules/previews/rest/router.ts
T
Kristaps Fabians Geikins bde148f286 chore(server): migrating fully to ESM (#5042)
* 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
2025-07-14 10:26:19 +03:00

318 lines
11 KiB
TypeScript

import { Router } from 'express'
import cors from 'cors'
import { validateScopes, authorizeResolver } from '@/modules/shared'
import { makeOgImage } from '@/modules/previews/ogImage'
import { db } from '@/db/knex'
import {
getObjectPreviewBufferOrFilepathFactory,
sendObjectPreviewFactory,
checkStreamPermissionsFactory
} from '@/modules/previews/services/management'
import {
getObjectPreviewInfoFactory,
getPreviewImageFactory,
storeObjectPreviewFactory
} from '@/modules/previews/repository/previews'
import {
getCommitFactory,
getPaginatedBranchCommitsItemsFactory,
legacyGetPaginatedStreamCommitsPageFactory
} from '@/modules/core/repositories/commits'
import {
getStreamCollaboratorsFactory,
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 { createObjectPreviewFactory } from '@/modules/previews/services/createObjectPreview'
import { createAppTokenFactory } from '@/modules/core/services/tokens'
import {
storeApiTokenFactory,
storeTokenResourceAccessDefinitionsFactory,
storeTokenScopesFactory,
storeUserServerAppTokenFactory
} from '@/modules/core/repositories/tokens'
import {
getPrivateObjectsServerOrigin,
getServerOrigin,
previewServiceShouldUsePrivateObjectsServerUrl
} from '@/modules/shared/helpers/envHelper'
import { requestObjectPreviewFactory } from '@/modules/previews/queues/previews'
import type { Queue } from 'bull'
import type { Knex } from 'knex'
import { fileURLToPath } from 'url'
const httpErrorImage = (httpErrorCode: number) =>
fileURLToPath(
import.meta.resolve(`#/assets/previews/images/preview_${httpErrorCode}.png`)
)
const noPreviewImage = () =>
fileURLToPath(import.meta.resolve('#/assets/previews/images/no_preview.png'))
const buildCreateObjectPreviewFunction = ({
projectDb,
previewRequestQueue,
responseQueueName
}: {
projectDb: Knex
previewRequestQueue: Queue
responseQueueName: string
}) => {
return createObjectPreviewFactory({
requestObjectPreview: requestObjectPreviewFactory({
queue: previewRequestQueue,
responseQueue: responseQueueName
}),
// use the private server origin if defined, otherwise use the public server origin
serverOrigin: previewServiceShouldUsePrivateObjectsServerUrl()
? getPrivateObjectsServerOrigin()
: getServerOrigin(),
storeObjectPreview: storeObjectPreviewFactory({ db: projectDb }),
getStreamCollaborators: getStreamCollaboratorsFactory({ db }),
createAppToken: createAppTokenFactory({
storeApiToken: storeApiTokenFactory({ db }),
storeTokenScopes: storeTokenScopesFactory({ db }),
storeTokenResourceAccessDefinitions: storeTokenResourceAccessDefinitionsFactory({
db
}),
storeUserServerAppToken: storeUserServerAppTokenFactory({ db })
})
})
}
export const previewRouterFactory = ({
previewRequestQueue,
responseQueueName
}: {
previewRequestQueue: Queue
responseQueueName: string
}): Router => {
const app = Router()
app.options('/preview/:streamId/:angle?', cors())
app.get('/preview/:streamId/:angle?', cors(), 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(await 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(await noPreviewImage())
}
const lastCommit = commits[0]
const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({
logger: req.log,
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: buildCreateObjectPreviewFunction({
projectDb,
previewRequestQueue,
responseQueueName
}),
getPreviewImage: getPreviewImageFactory({ db: projectDb })
})
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?', cors())
app.get(
'/preview/:streamId/branches/:branchName/:angle?',
cors(),
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(await 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(await noPreviewImage())
}
const lastCommit = commits[0]
const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({
logger: req.log,
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: buildCreateObjectPreviewFunction({
projectDb,
previewRequestQueue,
responseQueueName
}),
getPreviewImage: getPreviewImageFactory({ db: projectDb })
})
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?', cors())
app.get('/preview/:streamId/commits/:commitId/:angle?', cors(), 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(await 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(await noPreviewImage())
const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({
logger: req.log,
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: buildCreateObjectPreviewFunction({
projectDb,
previewRequestQueue,
responseQueueName
}),
getPreviewImage: getPreviewImageFactory({ db: projectDb })
})
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?', cors())
app.get('/preview/:streamId/objects/:objectId/:angle?', cors(), 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({
logger: req.log,
getObject: getFormattedObjectFactory({ db: projectDb }),
getObjectPreviewInfo: getObjectPreviewInfoFactory({ db: projectDb }),
createObjectPreview: buildCreateObjectPreviewFunction({
projectDb,
previewRequestQueue,
responseQueueName
}),
getPreviewImage: getPreviewImageFactory({ db: projectDb })
})
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
)
})
return app
}