@constructive-io/bucket-provisioner 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +335 -0
- package/client.d.ts +19 -0
- package/client.js +45 -0
- package/cors.d.ts +33 -0
- package/cors.js +88 -0
- package/esm/client.d.ts +19 -0
- package/esm/client.js +42 -0
- package/esm/cors.d.ts +33 -0
- package/esm/cors.js +84 -0
- package/esm/index.d.ts +37 -0
- package/esm/index.js +39 -0
- package/esm/lifecycle.d.ts +29 -0
- package/esm/lifecycle.js +42 -0
- package/esm/policies.d.ts +88 -0
- package/esm/policies.js +121 -0
- package/esm/provisioner.d.ts +137 -0
- package/esm/provisioner.js +397 -0
- package/esm/types.d.ts +155 -0
- package/esm/types.js +19 -0
- package/index.d.ts +37 -0
- package/index.js +53 -0
- package/lifecycle.d.ts +29 -0
- package/lifecycle.js +46 -0
- package/package.json +47 -0
- package/policies.d.ts +88 -0
- package/policies.js +127 -0
- package/provisioner.d.ts +137 -0
- package/provisioner.js +401 -0
- package/types.d.ts +155 -0
- package/types.js +23 -0
package/esm/cors.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS configuration builders.
|
|
3
|
+
*
|
|
4
|
+
* Generates CORS rules for S3 buckets to allow browser-based
|
|
5
|
+
* presigned URL uploads. Without CORS, the browser will block
|
|
6
|
+
* the cross-origin PUT request to the S3 endpoint.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Build the default CORS rules for presigned URL uploads.
|
|
10
|
+
*
|
|
11
|
+
* This allows:
|
|
12
|
+
* - PUT: for presigned uploads from the browser
|
|
13
|
+
* - GET: for presigned downloads and public file access
|
|
14
|
+
* - HEAD: for confirmUpload verification and cache headers
|
|
15
|
+
*
|
|
16
|
+
* @param allowedOrigins - Domains allowed to make cross-origin requests.
|
|
17
|
+
* Use specific domains in production (e.g., ["https://app.example.com"]).
|
|
18
|
+
* Never use ["*"] in production.
|
|
19
|
+
* @param maxAgeSeconds - Preflight cache duration (default: 3600 = 1 hour)
|
|
20
|
+
*/
|
|
21
|
+
export function buildUploadCorsRules(allowedOrigins, maxAgeSeconds = 3600) {
|
|
22
|
+
if (allowedOrigins.length === 0) {
|
|
23
|
+
throw new Error('allowedOrigins must contain at least one origin');
|
|
24
|
+
}
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
allowedOrigins,
|
|
28
|
+
allowedMethods: ['PUT', 'GET', 'HEAD'],
|
|
29
|
+
allowedHeaders: [
|
|
30
|
+
'Content-Type',
|
|
31
|
+
'Content-Length',
|
|
32
|
+
'Content-MD5',
|
|
33
|
+
'x-amz-content-sha256',
|
|
34
|
+
'x-amz-date',
|
|
35
|
+
'x-amz-security-token',
|
|
36
|
+
'Authorization',
|
|
37
|
+
],
|
|
38
|
+
exposedHeaders: [
|
|
39
|
+
'ETag',
|
|
40
|
+
'Content-Length',
|
|
41
|
+
'Content-Type',
|
|
42
|
+
'x-amz-request-id',
|
|
43
|
+
'x-amz-id-2',
|
|
44
|
+
],
|
|
45
|
+
maxAgeSeconds,
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build restrictive CORS rules for private-only buckets.
|
|
51
|
+
*
|
|
52
|
+
* Similar to upload CORS but without GET (private files use
|
|
53
|
+
* presigned URLs which include auth in the query string,
|
|
54
|
+
* so CORS is less of a concern for downloads).
|
|
55
|
+
*
|
|
56
|
+
* @param allowedOrigins - Domains allowed to make cross-origin requests.
|
|
57
|
+
* @param maxAgeSeconds - Preflight cache duration (default: 3600 = 1 hour)
|
|
58
|
+
*/
|
|
59
|
+
export function buildPrivateCorsRules(allowedOrigins, maxAgeSeconds = 3600) {
|
|
60
|
+
if (allowedOrigins.length === 0) {
|
|
61
|
+
throw new Error('allowedOrigins must contain at least one origin');
|
|
62
|
+
}
|
|
63
|
+
return [
|
|
64
|
+
{
|
|
65
|
+
allowedOrigins,
|
|
66
|
+
allowedMethods: ['PUT', 'HEAD'],
|
|
67
|
+
allowedHeaders: [
|
|
68
|
+
'Content-Type',
|
|
69
|
+
'Content-Length',
|
|
70
|
+
'Content-MD5',
|
|
71
|
+
'x-amz-content-sha256',
|
|
72
|
+
'x-amz-date',
|
|
73
|
+
'x-amz-security-token',
|
|
74
|
+
'Authorization',
|
|
75
|
+
],
|
|
76
|
+
exposedHeaders: [
|
|
77
|
+
'ETag',
|
|
78
|
+
'Content-Length',
|
|
79
|
+
'x-amz-request-id',
|
|
80
|
+
],
|
|
81
|
+
maxAgeSeconds,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @constructive-io/bucket-provisioner
|
|
3
|
+
*
|
|
4
|
+
* S3-compatible bucket provisioning library for the Constructive storage module.
|
|
5
|
+
* Creates and configures buckets with the correct privacy policies, CORS rules,
|
|
6
|
+
* versioning, and lifecycle settings for private and public file storage.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { BucketProvisioner } from '@constructive-io/bucket-provisioner';
|
|
11
|
+
*
|
|
12
|
+
* const provisioner = new BucketProvisioner({
|
|
13
|
+
* connection: {
|
|
14
|
+
* provider: 'minio',
|
|
15
|
+
* region: 'us-east-1',
|
|
16
|
+
* endpoint: 'http://minio:9000',
|
|
17
|
+
* accessKeyId: 'minioadmin',
|
|
18
|
+
* secretAccessKey: 'minioadmin',
|
|
19
|
+
* },
|
|
20
|
+
* allowedOrigins: ['https://app.example.com'],
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const result = await provisioner.provision({
|
|
24
|
+
* bucketName: 'my-app-storage',
|
|
25
|
+
* accessType: 'private',
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export { BucketProvisioner } from './provisioner';
|
|
30
|
+
export type { BucketProvisionerOptions } from './provisioner';
|
|
31
|
+
export { createS3Client } from './client';
|
|
32
|
+
export { getPublicAccessBlock, buildPublicReadPolicy, buildCloudFrontOacPolicy, buildPresignedUrlIamPolicy, } from './policies';
|
|
33
|
+
export type { PublicAccessBlockConfig, BucketPolicyDocument, BucketPolicyStatement, } from './policies';
|
|
34
|
+
export { buildUploadCorsRules, buildPrivateCorsRules } from './cors';
|
|
35
|
+
export { buildTempCleanupRule, buildAbortIncompleteMultipartRule } from './lifecycle';
|
|
36
|
+
export type { StorageProvider, StorageConnectionConfig, BucketAccessType, CreateBucketOptions, UpdateCorsOptions, CorsRule, LifecycleRule, ProvisionResult, ProvisionerErrorCode, } from './types';
|
|
37
|
+
export { ProvisionerError } from './types';
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @constructive-io/bucket-provisioner
|
|
3
|
+
*
|
|
4
|
+
* S3-compatible bucket provisioning library for the Constructive storage module.
|
|
5
|
+
* Creates and configures buckets with the correct privacy policies, CORS rules,
|
|
6
|
+
* versioning, and lifecycle settings for private and public file storage.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { BucketProvisioner } from '@constructive-io/bucket-provisioner';
|
|
11
|
+
*
|
|
12
|
+
* const provisioner = new BucketProvisioner({
|
|
13
|
+
* connection: {
|
|
14
|
+
* provider: 'minio',
|
|
15
|
+
* region: 'us-east-1',
|
|
16
|
+
* endpoint: 'http://minio:9000',
|
|
17
|
+
* accessKeyId: 'minioadmin',
|
|
18
|
+
* secretAccessKey: 'minioadmin',
|
|
19
|
+
* },
|
|
20
|
+
* allowedOrigins: ['https://app.example.com'],
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const result = await provisioner.provision({
|
|
24
|
+
* bucketName: 'my-app-storage',
|
|
25
|
+
* accessType: 'private',
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
// Core provisioner
|
|
30
|
+
export { BucketProvisioner } from './provisioner';
|
|
31
|
+
// S3 client factory
|
|
32
|
+
export { createS3Client } from './client';
|
|
33
|
+
// Policy builders
|
|
34
|
+
export { getPublicAccessBlock, buildPublicReadPolicy, buildCloudFrontOacPolicy, buildPresignedUrlIamPolicy, } from './policies';
|
|
35
|
+
// CORS builders
|
|
36
|
+
export { buildUploadCorsRules, buildPrivateCorsRules } from './cors';
|
|
37
|
+
// Lifecycle builders
|
|
38
|
+
export { buildTempCleanupRule, buildAbortIncompleteMultipartRule } from './lifecycle';
|
|
39
|
+
export { ProvisionerError } from './types';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle rule builders.
|
|
3
|
+
*
|
|
4
|
+
* Generates S3 lifecycle configurations for automatic object expiration.
|
|
5
|
+
* Primarily used for temp buckets where uploads should be cleaned up
|
|
6
|
+
* after a configurable period.
|
|
7
|
+
*/
|
|
8
|
+
import type { LifecycleRule } from './types';
|
|
9
|
+
/**
|
|
10
|
+
* Build a lifecycle rule for temp bucket cleanup.
|
|
11
|
+
*
|
|
12
|
+
* Temp buckets hold staging uploads (files with status='pending' that
|
|
13
|
+
* were never confirmed). This rule automatically deletes objects after
|
|
14
|
+
* a set number of days, preventing storage cost accumulation from
|
|
15
|
+
* abandoned uploads.
|
|
16
|
+
*
|
|
17
|
+
* @param expirationDays - Days after which objects expire (default: 1)
|
|
18
|
+
* @param prefix - Key prefix to target (default: "" = entire bucket)
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildTempCleanupRule(expirationDays?: number, prefix?: string): LifecycleRule;
|
|
21
|
+
/**
|
|
22
|
+
* Build a lifecycle rule for incomplete multipart upload cleanup.
|
|
23
|
+
*
|
|
24
|
+
* Incomplete multipart uploads consume storage but serve no purpose.
|
|
25
|
+
* This rule aborts them after a set number of days.
|
|
26
|
+
*
|
|
27
|
+
* @param expirationDays - Days after which incomplete uploads are aborted (default: 1)
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildAbortIncompleteMultipartRule(expirationDays?: number): LifecycleRule;
|
package/esm/lifecycle.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle rule builders.
|
|
3
|
+
*
|
|
4
|
+
* Generates S3 lifecycle configurations for automatic object expiration.
|
|
5
|
+
* Primarily used for temp buckets where uploads should be cleaned up
|
|
6
|
+
* after a configurable period.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Build a lifecycle rule for temp bucket cleanup.
|
|
10
|
+
*
|
|
11
|
+
* Temp buckets hold staging uploads (files with status='pending' that
|
|
12
|
+
* were never confirmed). This rule automatically deletes objects after
|
|
13
|
+
* a set number of days, preventing storage cost accumulation from
|
|
14
|
+
* abandoned uploads.
|
|
15
|
+
*
|
|
16
|
+
* @param expirationDays - Days after which objects expire (default: 1)
|
|
17
|
+
* @param prefix - Key prefix to target (default: "" = entire bucket)
|
|
18
|
+
*/
|
|
19
|
+
export function buildTempCleanupRule(expirationDays = 1, prefix = '') {
|
|
20
|
+
return {
|
|
21
|
+
id: 'temp-cleanup',
|
|
22
|
+
prefix,
|
|
23
|
+
expirationDays,
|
|
24
|
+
enabled: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a lifecycle rule for incomplete multipart upload cleanup.
|
|
29
|
+
*
|
|
30
|
+
* Incomplete multipart uploads consume storage but serve no purpose.
|
|
31
|
+
* This rule aborts them after a set number of days.
|
|
32
|
+
*
|
|
33
|
+
* @param expirationDays - Days after which incomplete uploads are aborted (default: 1)
|
|
34
|
+
*/
|
|
35
|
+
export function buildAbortIncompleteMultipartRule(expirationDays = 1) {
|
|
36
|
+
return {
|
|
37
|
+
id: 'abort-incomplete-multipart',
|
|
38
|
+
prefix: '',
|
|
39
|
+
expirationDays,
|
|
40
|
+
enabled: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3 bucket policy builders.
|
|
3
|
+
*
|
|
4
|
+
* Generates the JSON policy documents for private and public bucket
|
|
5
|
+
* configurations. These are the actual S3 bucket policies that control
|
|
6
|
+
* who can access objects in the bucket.
|
|
7
|
+
*
|
|
8
|
+
* Privacy model:
|
|
9
|
+
* - Private buckets: Block All Public Access enabled, no bucket policy needed.
|
|
10
|
+
* All access goes through presigned URLs generated server-side.
|
|
11
|
+
* - Public buckets: Block Public Access disabled for GetObject only.
|
|
12
|
+
* A bucket policy grants public read access (optionally restricted to a key prefix).
|
|
13
|
+
*/
|
|
14
|
+
import type { BucketAccessType } from './types';
|
|
15
|
+
/**
|
|
16
|
+
* S3 Block Public Access configuration.
|
|
17
|
+
*
|
|
18
|
+
* For private/temp buckets: all four flags are true (maximum lockdown).
|
|
19
|
+
* For public buckets: BlockPublicPolicy and RestrictPublicBuckets are false
|
|
20
|
+
* so that a public-read bucket policy can be applied.
|
|
21
|
+
*/
|
|
22
|
+
export interface PublicAccessBlockConfig {
|
|
23
|
+
BlockPublicAcls: boolean;
|
|
24
|
+
IgnorePublicAcls: boolean;
|
|
25
|
+
BlockPublicPolicy: boolean;
|
|
26
|
+
RestrictPublicBuckets: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the Block Public Access configuration for a bucket access type.
|
|
30
|
+
*
|
|
31
|
+
* - private/temp: all blocks enabled (maximum security)
|
|
32
|
+
* - public: ACL blocks enabled (ACLs are deprecated), but policy blocks
|
|
33
|
+
* disabled so a public-read bucket policy can be attached
|
|
34
|
+
*/
|
|
35
|
+
export declare function getPublicAccessBlock(accessType: BucketAccessType): PublicAccessBlockConfig;
|
|
36
|
+
/**
|
|
37
|
+
* AWS IAM-style policy document for S3 bucket policies.
|
|
38
|
+
*/
|
|
39
|
+
export interface BucketPolicyDocument {
|
|
40
|
+
Version: string;
|
|
41
|
+
Statement: BucketPolicyStatement[];
|
|
42
|
+
}
|
|
43
|
+
export interface BucketPolicyStatement {
|
|
44
|
+
Sid: string;
|
|
45
|
+
Effect: 'Allow' | 'Deny';
|
|
46
|
+
Principal: string | {
|
|
47
|
+
AWS: string;
|
|
48
|
+
} | {
|
|
49
|
+
Service: string;
|
|
50
|
+
};
|
|
51
|
+
Action: string | string[];
|
|
52
|
+
Resource: string | string[];
|
|
53
|
+
Condition?: Record<string, Record<string, string>>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a public-read bucket policy.
|
|
57
|
+
*
|
|
58
|
+
* Grants anonymous GetObject access to the entire bucket or a specific prefix.
|
|
59
|
+
* This is the standard way to serve public files via direct URL or CDN.
|
|
60
|
+
*
|
|
61
|
+
* @param bucketName - S3 bucket name
|
|
62
|
+
* @param keyPrefix - Optional key prefix to restrict public reads (e.g., "public/").
|
|
63
|
+
* If provided, only objects under this prefix are publicly readable.
|
|
64
|
+
* If omitted, the entire bucket is publicly readable.
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildPublicReadPolicy(bucketName: string, keyPrefix?: string): BucketPolicyDocument;
|
|
67
|
+
/**
|
|
68
|
+
* Build a CloudFront Origin Access Control (OAC) bucket policy.
|
|
69
|
+
*
|
|
70
|
+
* This is the recommended way to serve public files through CloudFront
|
|
71
|
+
* without making the S3 bucket itself public. CloudFront authenticates
|
|
72
|
+
* to S3 using the OAC, and the bucket policy only allows CloudFront.
|
|
73
|
+
*
|
|
74
|
+
* @param bucketName - S3 bucket name
|
|
75
|
+
* @param cloudFrontDistributionArn - The CloudFront distribution ARN
|
|
76
|
+
* @param keyPrefix - Optional key prefix to restrict access
|
|
77
|
+
*/
|
|
78
|
+
export declare function buildCloudFrontOacPolicy(bucketName: string, cloudFrontDistributionArn: string, keyPrefix?: string): BucketPolicyDocument;
|
|
79
|
+
/**
|
|
80
|
+
* Build the IAM policy for the presigned URL plugin's S3 credentials.
|
|
81
|
+
*
|
|
82
|
+
* This is the minimum-permission policy that the GraphQL server's
|
|
83
|
+
* S3 access key should have. It only allows PutObject, GetObject,
|
|
84
|
+
* and HeadObject — no delete, no list, no bucket management.
|
|
85
|
+
*
|
|
86
|
+
* @param bucketName - S3 bucket name
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildPresignedUrlIamPolicy(bucketName: string): BucketPolicyDocument;
|
package/esm/policies.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3 bucket policy builders.
|
|
3
|
+
*
|
|
4
|
+
* Generates the JSON policy documents for private and public bucket
|
|
5
|
+
* configurations. These are the actual S3 bucket policies that control
|
|
6
|
+
* who can access objects in the bucket.
|
|
7
|
+
*
|
|
8
|
+
* Privacy model:
|
|
9
|
+
* - Private buckets: Block All Public Access enabled, no bucket policy needed.
|
|
10
|
+
* All access goes through presigned URLs generated server-side.
|
|
11
|
+
* - Public buckets: Block Public Access disabled for GetObject only.
|
|
12
|
+
* A bucket policy grants public read access (optionally restricted to a key prefix).
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Get the Block Public Access configuration for a bucket access type.
|
|
16
|
+
*
|
|
17
|
+
* - private/temp: all blocks enabled (maximum security)
|
|
18
|
+
* - public: ACL blocks enabled (ACLs are deprecated), but policy blocks
|
|
19
|
+
* disabled so a public-read bucket policy can be attached
|
|
20
|
+
*/
|
|
21
|
+
export function getPublicAccessBlock(accessType) {
|
|
22
|
+
if (accessType === 'public') {
|
|
23
|
+
return {
|
|
24
|
+
BlockPublicAcls: true,
|
|
25
|
+
IgnorePublicAcls: true,
|
|
26
|
+
BlockPublicPolicy: false,
|
|
27
|
+
RestrictPublicBuckets: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// private and temp: full lockdown
|
|
31
|
+
return {
|
|
32
|
+
BlockPublicAcls: true,
|
|
33
|
+
IgnorePublicAcls: true,
|
|
34
|
+
BlockPublicPolicy: true,
|
|
35
|
+
RestrictPublicBuckets: true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build a public-read bucket policy.
|
|
40
|
+
*
|
|
41
|
+
* Grants anonymous GetObject access to the entire bucket or a specific prefix.
|
|
42
|
+
* This is the standard way to serve public files via direct URL or CDN.
|
|
43
|
+
*
|
|
44
|
+
* @param bucketName - S3 bucket name
|
|
45
|
+
* @param keyPrefix - Optional key prefix to restrict public reads (e.g., "public/").
|
|
46
|
+
* If provided, only objects under this prefix are publicly readable.
|
|
47
|
+
* If omitted, the entire bucket is publicly readable.
|
|
48
|
+
*/
|
|
49
|
+
export function buildPublicReadPolicy(bucketName, keyPrefix) {
|
|
50
|
+
const resource = keyPrefix
|
|
51
|
+
? `arn:aws:s3:::${bucketName}/${keyPrefix}*`
|
|
52
|
+
: `arn:aws:s3:::${bucketName}/*`;
|
|
53
|
+
return {
|
|
54
|
+
Version: '2012-10-17',
|
|
55
|
+
Statement: [
|
|
56
|
+
{
|
|
57
|
+
Sid: 'PublicReadAccess',
|
|
58
|
+
Effect: 'Allow',
|
|
59
|
+
Principal: '*',
|
|
60
|
+
Action: 's3:GetObject',
|
|
61
|
+
Resource: resource,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a CloudFront Origin Access Control (OAC) bucket policy.
|
|
68
|
+
*
|
|
69
|
+
* This is the recommended way to serve public files through CloudFront
|
|
70
|
+
* without making the S3 bucket itself public. CloudFront authenticates
|
|
71
|
+
* to S3 using the OAC, and the bucket policy only allows CloudFront.
|
|
72
|
+
*
|
|
73
|
+
* @param bucketName - S3 bucket name
|
|
74
|
+
* @param cloudFrontDistributionArn - The CloudFront distribution ARN
|
|
75
|
+
* @param keyPrefix - Optional key prefix to restrict access
|
|
76
|
+
*/
|
|
77
|
+
export function buildCloudFrontOacPolicy(bucketName, cloudFrontDistributionArn, keyPrefix) {
|
|
78
|
+
const resource = keyPrefix
|
|
79
|
+
? `arn:aws:s3:::${bucketName}/${keyPrefix}*`
|
|
80
|
+
: `arn:aws:s3:::${bucketName}/*`;
|
|
81
|
+
return {
|
|
82
|
+
Version: '2012-10-17',
|
|
83
|
+
Statement: [
|
|
84
|
+
{
|
|
85
|
+
Sid: 'AllowCloudFrontOACRead',
|
|
86
|
+
Effect: 'Allow',
|
|
87
|
+
Principal: { Service: 'cloudfront.amazonaws.com' },
|
|
88
|
+
Action: 's3:GetObject',
|
|
89
|
+
Resource: resource,
|
|
90
|
+
Condition: {
|
|
91
|
+
StringEquals: {
|
|
92
|
+
'AWS:SourceArn': cloudFrontDistributionArn,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build the IAM policy for the presigned URL plugin's S3 credentials.
|
|
101
|
+
*
|
|
102
|
+
* This is the minimum-permission policy that the GraphQL server's
|
|
103
|
+
* S3 access key should have. It only allows PutObject, GetObject,
|
|
104
|
+
* and HeadObject — no delete, no list, no bucket management.
|
|
105
|
+
*
|
|
106
|
+
* @param bucketName - S3 bucket name
|
|
107
|
+
*/
|
|
108
|
+
export function buildPresignedUrlIamPolicy(bucketName) {
|
|
109
|
+
return {
|
|
110
|
+
Version: '2012-10-17',
|
|
111
|
+
Statement: [
|
|
112
|
+
{
|
|
113
|
+
Sid: 'PresignedUrlPluginAccess',
|
|
114
|
+
Effect: 'Allow',
|
|
115
|
+
Principal: '*',
|
|
116
|
+
Action: ['s3:PutObject', 's3:GetObject', 's3:HeadObject'],
|
|
117
|
+
Resource: `arn:aws:s3:::${bucketName}/*`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bucket Provisioner — core provisioning logic.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates S3 bucket creation, privacy configuration, CORS setup,
|
|
5
|
+
* versioning, and lifecycle rules. Uses the AWS SDK S3 client for all
|
|
6
|
+
* operations, which works with any S3-compatible backend (MinIO, R2, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Privacy model:
|
|
9
|
+
* - Private/temp buckets: Block All Public Access, no bucket policy, presigned URLs only
|
|
10
|
+
* - Public buckets: Block Public Access partially relaxed, public-read bucket policy applied
|
|
11
|
+
*/
|
|
12
|
+
import type { S3Client } from '@aws-sdk/client-s3';
|
|
13
|
+
import type { StorageConnectionConfig, CreateBucketOptions, UpdateCorsOptions, CorsRule, LifecycleRule, ProvisionResult, BucketAccessType } from './types';
|
|
14
|
+
import type { BucketPolicyDocument, PublicAccessBlockConfig } from './policies';
|
|
15
|
+
/**
|
|
16
|
+
* Options for the BucketProvisioner constructor.
|
|
17
|
+
*/
|
|
18
|
+
export interface BucketProvisionerOptions {
|
|
19
|
+
/** Storage connection config — credentials, endpoint, provider */
|
|
20
|
+
connection: StorageConnectionConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Default allowed origins for CORS rules.
|
|
23
|
+
* These are the domains where your app runs (e.g., ["https://app.example.com"]).
|
|
24
|
+
* Required for browser-based presigned URL uploads.
|
|
25
|
+
*/
|
|
26
|
+
allowedOrigins: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The BucketProvisioner handles creating and configuring S3-compatible
|
|
30
|
+
* buckets with the correct privacy settings, CORS rules, and policies
|
|
31
|
+
* for the Constructive storage module.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const provisioner = new BucketProvisioner({
|
|
36
|
+
* connection: {
|
|
37
|
+
* provider: 'minio',
|
|
38
|
+
* region: 'us-east-1',
|
|
39
|
+
* endpoint: 'http://minio:9000',
|
|
40
|
+
* accessKeyId: 'minioadmin',
|
|
41
|
+
* secretAccessKey: 'minioadmin',
|
|
42
|
+
* },
|
|
43
|
+
* allowedOrigins: ['https://app.example.com'],
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* // Provision a private bucket
|
|
47
|
+
* const result = await provisioner.provision({
|
|
48
|
+
* bucketName: 'my-app-storage',
|
|
49
|
+
* accessType: 'private',
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare class BucketProvisioner {
|
|
54
|
+
private readonly client;
|
|
55
|
+
private readonly config;
|
|
56
|
+
private readonly allowedOrigins;
|
|
57
|
+
constructor(options: BucketProvisionerOptions);
|
|
58
|
+
/**
|
|
59
|
+
* Get the underlying S3Client instance.
|
|
60
|
+
* Useful for advanced operations not covered by the provisioner.
|
|
61
|
+
*/
|
|
62
|
+
getClient(): S3Client;
|
|
63
|
+
/**
|
|
64
|
+
* Provision a fully configured S3 bucket.
|
|
65
|
+
*
|
|
66
|
+
* This is the main entry point. It:
|
|
67
|
+
* 1. Creates the bucket (or verifies it exists)
|
|
68
|
+
* 2. Configures Block Public Access based on access type
|
|
69
|
+
* 3. Applies the appropriate bucket policy (public-read or none)
|
|
70
|
+
* 4. Sets CORS rules for presigned URL uploads
|
|
71
|
+
* 5. Optionally enables versioning
|
|
72
|
+
* 6. Optionally adds lifecycle rules (auto-enabled for temp buckets)
|
|
73
|
+
*
|
|
74
|
+
* @param options - Bucket creation options
|
|
75
|
+
* @returns ProvisionResult with all configuration details
|
|
76
|
+
*/
|
|
77
|
+
provision(options: CreateBucketOptions): Promise<ProvisionResult>;
|
|
78
|
+
/**
|
|
79
|
+
* Create an S3 bucket. Handles the "bucket already exists" case gracefully.
|
|
80
|
+
*/
|
|
81
|
+
createBucket(bucketName: string, region?: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Check if a bucket exists and is accessible.
|
|
84
|
+
*/
|
|
85
|
+
bucketExists(bucketName: string): Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Configure S3 Block Public Access settings.
|
|
88
|
+
*/
|
|
89
|
+
setPublicAccessBlock(bucketName: string, config: PublicAccessBlockConfig): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Apply an S3 bucket policy.
|
|
92
|
+
*/
|
|
93
|
+
setBucketPolicy(bucketName: string, policy: BucketPolicyDocument): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Delete an S3 bucket policy (used to clear leftover public policies).
|
|
96
|
+
*/
|
|
97
|
+
deleteBucketPolicy(bucketName: string): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Set CORS configuration on an S3 bucket.
|
|
100
|
+
*/
|
|
101
|
+
setCors(bucketName: string, rules: CorsRule[]): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Enable versioning on an S3 bucket.
|
|
104
|
+
*/
|
|
105
|
+
enableVersioning(bucketName: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Set lifecycle rules on an S3 bucket.
|
|
108
|
+
*/
|
|
109
|
+
setLifecycleRules(bucketName: string, rules: LifecycleRule[]): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Update CORS configuration on an existing S3 bucket.
|
|
112
|
+
*
|
|
113
|
+
* Call this when the `allowed_origins` column changes on a bucket row.
|
|
114
|
+
* Builds the appropriate CORS rule set for the bucket's access type
|
|
115
|
+
* and applies it to the S3 bucket.
|
|
116
|
+
*
|
|
117
|
+
* @param options - Bucket name, access type, and new allowed origins
|
|
118
|
+
* @returns The CORS rules that were applied
|
|
119
|
+
*/
|
|
120
|
+
updateCors(options: UpdateCorsOptions): Promise<CorsRule[]>;
|
|
121
|
+
/**
|
|
122
|
+
* Inspect the current configuration of an existing bucket.
|
|
123
|
+
*
|
|
124
|
+
* Reads the bucket's policy, CORS, versioning, lifecycle, and public access
|
|
125
|
+
* settings and returns them in a structured format. Useful for auditing
|
|
126
|
+
* or verifying that a bucket is correctly configured.
|
|
127
|
+
*
|
|
128
|
+
* @param bucketName - S3 bucket name
|
|
129
|
+
* @param accessType - Expected access type (used in the result)
|
|
130
|
+
*/
|
|
131
|
+
inspect(bucketName: string, accessType: BucketAccessType): Promise<ProvisionResult>;
|
|
132
|
+
private getPublicAccessBlock;
|
|
133
|
+
private getBucketPolicy;
|
|
134
|
+
private getBucketCors;
|
|
135
|
+
private getBucketVersioning;
|
|
136
|
+
private getBucketLifecycle;
|
|
137
|
+
}
|