1+ /* eslint-disable max-classes-per-file */
12import {
23 GetObjectCommand ,
34 ListBucketsCommand ,
@@ -32,39 +33,41 @@ const parseS3Url = (url) => {
3233 return { bucket, key }
3334}
3435
35- export class AssetProxy {
36- constructor ( ) {
37- this . bucketOption = process . env [ 'ASSET_PROXY_BUCKET_OPTION' ] || 'NONE'
38- this . bucketList = process . env [ 'ASSET_PROXY_BUCKET_LIST' ]
39- this . urlExpiry = parseInt ( process . env [ 'ASSET_PROXY_URL_EXPIRY' ] || '300' , 10 )
40- this . isEnabled = this . bucketOption !== BucketOption . NONE
36+ class AssetBuckets {
37+ /**
38+ * @param {string } bucketOption - Bucket option (NONE, ALL, ALL_BUCKETS_IN_ACCOUNT, LIST)
39+ * @param {string[]|null } bucketNames - Array of bucket names (required for LIST option)
40+ */
41+ constructor ( bucketOption , bucketNames ) {
42+ this . bucketOption = bucketOption
43+ this . bucketNames = bucketNames
4144 this . buckets = { }
4245 }
4346
4447 /**
45- * @returns {Promise<AssetProxy> } Initialized AssetProxy instance
48+ * @param {string } bucketOption - Bucket option (NONE, ALL, ALL_BUCKETS_IN_ACCOUNT, LIST)
49+ * @param {string[]|null } bucketNames - Array of bucket names (required for LIST option)
50+ * @returns {Promise<AssetBuckets> } Initialized AssetBuckets instance
4651 */
47- static async create ( ) {
48- const dbInstance = new AssetProxy ( )
49- await dbInstance . _initBuckets ( )
50- return dbInstance
52+ static async create ( bucketOption , bucketNames ) {
53+ const instance = new AssetBuckets ( bucketOption , bucketNames )
54+ await instance . _initBuckets ( )
55+ return instance
5156 }
5257
5358 /**
5459 * @returns {Promise<void> }
5560 */
5661 async _initBuckets ( ) {
5762 switch ( this . bucketOption ) {
58- case BucketOption . LIST :
59- if ( this . bucketList ) {
60- const bucketNames = this . bucketList . split ( ',' ) . map ( ( b ) => b . trim ( ) ) . filter ( ( b ) => b )
63+ case BucketOption . LIST : {
64+ if ( this . bucketNames && this . bucketNames . length > 0 ) {
6165 await Promise . all (
62- bucketNames . map ( async ( name ) => { await this . getBucket ( name ) } )
66+ this . bucketNames . map ( async ( name ) => { await this . getBucket ( name ) } )
6367 )
6468
65- const invalidBuckets = Object . values ( this . buckets )
66- . filter ( ( b ) => b . region === null )
67- . map ( ( b ) => b . name )
69+ const invalidBuckets = Object . keys ( this . buckets )
70+ . filter ( ( bucketName ) => this . buckets [ bucketName ] . region === null )
6871 if ( invalidBuckets . length > 0 ) {
6972 throw new Error (
7073 `Could not access or determine region for the following buckets: ${
@@ -78,33 +81,30 @@ export class AssetProxy {
7881 )
7982 } else {
8083 throw new Error (
81- 'ASSET_PROXY_BUCKET_LIST must be set when ASSET_PROXY_BUCKET_OPTION is LIST'
84+ 'ASSET_PROXY_BUCKET_LIST must not be empty when ASSET_PROXY_BUCKET_OPTION is LIST'
8285 )
8386 }
8487 break
88+ }
8589
86- case BucketOption . ALL_BUCKETS_IN_ACCOUNT :
87- try {
88- const command = new ListBucketsCommand ( { } )
89- const response = await s3Client . send ( command )
90- const buckets = response . Buckets || [ ]
90+ case BucketOption . ALL_BUCKETS_IN_ACCOUNT : {
91+ const command = new ListBucketsCommand ( { } )
92+ const response = await s3Client . send ( command )
93+ const buckets = response . Buckets || [ ]
9194
92- await Promise . all (
93- buckets
94- . map ( ( bucket ) => bucket . Name )
95- . filter ( ( name ) => typeof name === 'string' )
96- . map ( async ( name ) => { await this . getBucket ( name ) } )
97- )
95+ await Promise . all (
96+ buckets
97+ . map ( ( bucket ) => bucket . Name )
98+ . filter ( ( name ) => typeof name === 'string' )
99+ . map ( async ( name ) => { await this . getBucket ( name ) } )
100+ )
98101
99- const count = Object . keys ( this . buckets ) . length
100- logger . info (
101- `Fetched ${ count } buckets from AWS account for asset proxy`
102- )
103- } catch ( error ) {
104- const message = error instanceof Error ? error . message : String ( error )
105- throw new Error ( `Failed to fetch buckets for asset proxy: ${ message } ` )
106- }
102+ const count = Object . keys ( this . buckets ) . length
103+ logger . info (
104+ `Fetched ${ count } buckets from AWS account for asset proxy`
105+ )
107106 break
107+ }
108108
109109 default :
110110 break
@@ -120,10 +120,12 @@ export class AssetProxy {
120120 const command = new HeadBucketCommand ( { Bucket : bucketName } )
121121 const response = await s3Client . send ( command )
122122 const statusCode = response . $metadata . httpStatusCode
123+ let name = null
123124 let region = null
124125
125126 switch ( statusCode ) {
126127 case 200 :
128+ name = bucketName
127129 region = response . BucketRegion === 'EU'
128130 ? 'eu-west-1'
129131 : response . BucketRegion || 'us-east-1'
@@ -141,7 +143,7 @@ export class AssetProxy {
141143 logger . warn ( `Unexpected status code ${ statusCode } for bucket ${ bucketName } ` )
142144 }
143145
144- this . buckets [ bucketName ] = { name : bucketName , region }
146+ this . buckets [ bucketName ] = { name, region }
145147 }
146148 return this . buckets [ bucketName ]
147149 }
@@ -157,6 +159,42 @@ export class AssetProxy {
157159 }
158160 return false
159161 }
162+ }
163+
164+ export class AssetProxy {
165+ /**
166+ * @param {AssetBuckets } buckets - AssetBuckets instance
167+ * @param {number } urlExpiry - Pre-signed URL expiry time in seconds
168+ * @param {string } bucketOption - Bucket option (NONE, ALL, ALL_BUCKETS_IN_ACCOUNT, LIST)
169+ */
170+ constructor ( buckets , urlExpiry , bucketOption ) {
171+ this . buckets = buckets
172+ this . urlExpiry = urlExpiry
173+ this . isEnabled = bucketOption !== BucketOption . NONE
174+ }
175+
176+ /**
177+ * @returns {Promise<AssetProxy> } Initialized AssetProxy instance
178+ */
179+ static async create ( ) {
180+ const bucketOption = process . env [ 'ASSET_PROXY_BUCKET_OPTION' ] || 'NONE'
181+ const urlExpiry = parseInt ( process . env [ 'ASSET_PROXY_URL_EXPIRY' ] || '300' , 10 )
182+ const bucketList = process . env [ 'ASSET_PROXY_BUCKET_LIST' ]
183+
184+ let bucketNames = null
185+ if ( bucketOption === BucketOption . LIST ) {
186+ if ( ! bucketList ) {
187+ throw new Error (
188+ 'ASSET_PROXY_BUCKET_LIST must be set when ASSET_PROXY_BUCKET_OPTION is LIST'
189+ )
190+ }
191+ bucketNames = bucketList . split ( ',' ) . map ( ( b ) => b . trim ( ) ) . filter ( ( b ) => b )
192+ }
193+
194+ const buckets = await AssetBuckets . create ( bucketOption , bucketNames )
195+
196+ return new AssetProxy ( buckets , urlExpiry , bucketOption )
197+ }
160198
161199 /**
162200 * @param {Object } assets - Assets object
@@ -185,7 +223,7 @@ export class AssetProxy {
185223 continue
186224 }
187225
188- if ( ! this . shouldProxyBucket ( bucket ) ) {
226+ if ( ! this . buckets . shouldProxyBucket ( bucket ) ) {
189227 proxiedAssets [ assetKey ] = asset
190228 logger . warn ( `Asset ${ assetKey } bucket ${ bucket } is not configured for proxying` )
191229 // eslint-disable-next-line no-continue
@@ -267,11 +305,11 @@ export class AssetProxy {
267305 }
268306
269307 const { bucket, key } = parseS3Url ( asset . href )
270- if ( ! bucket || ! key || ! this . shouldProxyBucket ( bucket ) ) {
308+ if ( ! bucket || ! key || ! this . buckets . shouldProxyBucket ( bucket ) ) {
271309 return null
272310 }
273311
274- const region = await this . getBucket ( bucket ) . then ( ( b ) => b . region )
312+ const region = await this . buckets . getBucket ( bucket ) . then ( ( b ) => b . region )
275313 if ( ! region ) {
276314 // Should not get here if bucketOption is LIST or ALL_BUCKETS_IN_ACCOUNT
277315 // If bucketOption is ALL, the bucket either does not exist or access is denied
0 commit comments