Files
speckle-server/packages/server/modules/blobstorage/clients/objectStorage.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

149 lines
4.1 KiB
TypeScript

import {
getS3AccessKey,
getS3BucketName,
getS3Endpoint,
getS3PublicEndpoint,
getS3Region,
getS3SecretKey
} from '@/modules/shared/helpers/envHelper'
import {
HeadObjectCommand,
PutObjectCommand,
S3Client,
S3ClientConfig
} from '@aws-sdk/client-s3'
import { getSignedUrl as s3GetSignedUrl } from '@aws-sdk/s3-request-presigner'
import type { Optional } from '@speckle/shared'
import {
GetBlobMetadataFromStorage,
GetSignedUrl
} from '@/modules/blobstorage/domain/operations'
export type GetProjectObjectStorage = (args: {
projectId: string
}) => Promise<ObjectStorage>
export type GetObjectStorageParams = {
credentials: {
accessKeyId: string
secretAccessKey: string
}
endpoint: string
region: string
bucket: string
}
export type ObjectStorage = {
client: S3Client
bucket: string
params: GetObjectStorageParams
}
/**
* Get object storage client
*/
export const getObjectStorage = (params: GetObjectStorageParams): ObjectStorage => {
const { bucket, credentials, endpoint, region } = params
const config: S3ClientConfig = {
credentials,
endpoint,
region,
forcePathStyle: true
}
const client = new S3Client(config)
return { client, bucket, params }
}
let mainObjectStorage: Optional<ObjectStorage> = undefined
let publicMainObjectStorage: Optional<ObjectStorage> = undefined
/**
* Get main object storage client
*
* This is used for connecting the server to the S3 host. Where the S3 host is
* on the same private network as the server (e.g. in a Docker network),
* the S3_ENDPOINT can use the private IP or DNS name of the S3 host.
*
* S3_PUBLIC_ENDPOINT can be used to connect to the S3 host via the
* public internet (or localhost network if running locally or testing).
*/
export const getMainObjectStorage = (): ObjectStorage => {
if (mainObjectStorage) return mainObjectStorage
const mainParams: GetObjectStorageParams = {
credentials: {
accessKeyId: getS3AccessKey(),
secretAccessKey: getS3SecretKey()
},
endpoint: getS3Endpoint(),
region: getS3Region(),
bucket: getS3BucketName()
}
mainObjectStorage = getObjectStorage(mainParams)
return mainObjectStorage
}
/**
* (Optional) Used to connect to the S3 host via the public endpoint.
* This is useful for clients that need to access the S3 bucket directly, e.g
* during testing or when the S3 host is not on the same private network as the server.
*
* If `S3_PUBLIC_ENDPOINT` is not set, it will return the same object storage
* as `getMainObjectStorage`.
*/
export const getPublicMainObjectStorage = (): ObjectStorage => {
if (publicMainObjectStorage) return publicMainObjectStorage
const endpoint = getS3PublicEndpoint()
if (!endpoint) {
// If no public endpoint is set, return the main object storage
return getMainObjectStorage()
}
const mainParams: GetObjectStorageParams = {
credentials: {
accessKeyId: getS3AccessKey(),
secretAccessKey: getS3SecretKey()
},
endpoint,
region: getS3Region(),
bucket: getS3BucketName()
}
publicMainObjectStorage = getObjectStorage(mainParams)
return publicMainObjectStorage
}
export const getSignedUrlFactory = (deps: {
objectStorage: ObjectStorage
}): GetSignedUrl => {
const { objectStorage } = deps
const { client, bucket } = objectStorage
return async (params) => {
const { objectKey, urlExpiryDurationSeconds } = params
const command = new PutObjectCommand({ Bucket: bucket, Key: objectKey })
return s3GetSignedUrl(client, command, { expiresIn: urlExpiryDurationSeconds })
}
}
export const getBlobMetadataFromStorage = (deps: {
objectStorage: ObjectStorage
}): GetBlobMetadataFromStorage => {
const { objectStorage } = deps
const { client, bucket } = objectStorage
return async (params) => {
const { objectKey } = params
// https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/command/HeadObjectCommand/
const headObjectCommand = new HeadObjectCommand({ Bucket: bucket, Key: objectKey })
const metadata = await client.send(headObjectCommand)
return {
contentLength: metadata.ContentLength,
eTag: metadata.ETag
}
}
}