Skip to content

Commit

Permalink
fix: S3 bucket name used from the application configuration instead o…
Browse files Browse the repository at this point in the history
…f infered from CDN_URL (#10661)

Signed-off-by: Matt Krick <[email protected]>
Co-authored-by: Matt Krick <[email protected]>
  • Loading branch information
rafaelromcar-parabol and mattkrick authored Jan 21, 2025
1 parent 5e9705b commit 7f3051d
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 8 deletions.
23 changes: 17 additions & 6 deletions packages/server/fileStorage/S3FileStoreManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,16 @@ export default class S3Manager extends FileStoreManager {
// e.g. development, production
private envSubDir: string

// e.g. action-files.parabol.co
private bucket: string

// e.g. https://action-files.parabol.co
baseUrl: string
private s3: S3Client
constructor() {
super()
const {CDN_BASE_URL, AWS_REGION, AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID} = process.env
const {CDN_BASE_URL, AWS_REGION, AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID, AWS_S3_BUCKET} =
process.env
if (!CDN_BASE_URL || CDN_BASE_URL === 'key_CDN_BASE_URL') {
throw new Error('CDN_BASE_URL ENV VAR NOT SET')
}
Expand All @@ -50,18 +54,24 @@ export default class S3Manager extends FileStoreManager {
if (pathname.endsWith('/'))
throw new Error('CDN_BASE_URL must end with the env, no trailing slash, e.g. /production')

if (!AWS_S3_BUCKET) {
throw new Error('AWS_S3_BUCKET required when using AWS S3 as filestore provider')
}

this.envSubDir = pathname.split('/').at(-1) as string

this.baseUrl = baseUrl.href.slice(0, baseUrl.href.lastIndexOf(this.envSubDir))
this.bucket = AWS_S3_BUCKET

// credentials are optional since the file store could be public & not need a key to write
const credentials =
AWS_ACCESS_KEY_ID && AWS_SECRET_ACCESS_KEY
? {accessKeyId: AWS_ACCESS_KEY_ID, secretAccessKey: AWS_SECRET_ACCESS_KEY}
: undefined
this.s3 = new S3Client({
credentials,
// The bucket is inferred from the CDN_BASE_URL
bucketEndpoint: true,
// Using true fails to work on the checkExist method, it returns a 403
bucketEndpoint: false,
region: AWS_REGION,
followRegionRedirects: true,
retryStrategy: new CloudflareRetry(3)
Expand All @@ -75,7 +85,7 @@ export default class S3Manager extends FileStoreManager {
protected async putFile(file: ArrayBufferLike, fullPath: string) {
const s3Params = {
Body: Buffer.from(file),
Bucket: this.baseUrl,
Bucket: this.bucket,
Key: fullPath,
ContentType: mime.lookup(fullPath) || 'application/octet-stream'
}
Expand All @@ -98,17 +108,18 @@ export default class S3Manager extends FileStoreManager {
async checkExists(key: string, assetDir?: FileAssetDir) {
const Key = this.prependPath(key, assetDir)
try {
await this.s3.send(new HeadObjectCommand({Bucket: this.baseUrl, Key}))
await this.s3.send(new HeadObjectCommand({Bucket: this.bucket, Key}))
} catch (e) {
if (e instanceof Error && e.name === 'NotFound') return false
Logger.log(`Invalid error ${(e as Error).name}`)
}
return true
}

async presignUrl(url: string) {
// Important to decodeURI so `getSignedUrl` doesn't double encode e.g. local|123/avatars/123.jpg
const key = decodeURI(url.slice(this.baseUrl.length))
const command = new GetObjectCommand({Bucket: this.baseUrl, Key: key})
const command = new GetObjectCommand({Bucket: this.bucket, Key: key})
const encodedUri = await getSignedUrl(this.s3, command, {expiresIn: 604800})
return encodedUri
}
Expand Down
4 changes: 2 additions & 2 deletions scripts/toolboxSrc/pushToCDN.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import fs from 'fs'
import getFileStoreManager from 'parabol-server/fileStorage/getFileStoreManager'
import {Logger} from 'parabol-server/utils/Logger'
import path from 'path'
import getProjectRoot from '../webpack/utils/getProjectRoot'
import {Logger} from 'parabol-server/utils/Logger'

const PROJECT_ROOT = getProjectRoot()

Expand Down Expand Up @@ -66,7 +66,7 @@ const pushServerAssetsToCDN = async () => {
// static assets in /dist/images are already hosted at /static/images
if (process.env.FILE_STORE_PROVIDER === 'local') return
const targetObject = `images/${filename}`
const exists = await fileStoreManager.checkExists(targetObject)
const exists = await fileStoreManager.checkExists(targetObject, 'build')
if (exists) return false
const buffer = await fs.promises.readFile(path.join(localServerAssetsDir, filename))
const url = await fileStoreManager.putBuildFile(buffer, targetObject)
Expand Down

0 comments on commit 7f3051d

Please sign in to comment.