Skip to content

Commit d6c7685

Browse files
committed
feat: wire BucketProvisionerPreset into ConstructivePreset via getEnvOptions
- Add bucket-provisioner-resolver.ts in graphile-settings (lazy init via getEnvOptions, same pattern as presigned-url-resolver.ts) - Wire BucketProvisionerPreset into constructive-preset.ts with lazy connection getter - Update JSDoc examples in bucket-provisioner-plugin to show getEnvOptions pattern instead of raw process.env - Export getBucketProvisionerConnection from graphile-settings index
1 parent eb856fe commit d6c7685

7 files changed

Lines changed: 112 additions & 33 deletions

File tree

graphile/graphile-bucket-provisioner-plugin/src/index.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,24 @@
1212
* @example
1313
* ```typescript
1414
* import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
15+
* import { getEnvOptions } from '@constructive-io/graphql-env';
16+
*
17+
* // Use a lazy getter so env vars are read at runtime, not import time
18+
* function getConnection() {
19+
* const { cdn } = getEnvOptions();
20+
* return {
21+
* provider: cdn?.provider || 'minio',
22+
* region: cdn?.awsRegion || 'us-east-1',
23+
* endpoint: cdn?.endpoint || 'http://minio:9000',
24+
* accessKeyId: cdn?.awsAccessKey!,
25+
* secretAccessKey: cdn?.awsSecretKey!,
26+
* };
27+
* }
1528
*
1629
* const preset = {
1730
* extends: [
1831
* BucketProvisionerPreset({
19-
* connection: {
20-
* provider: 'minio',
21-
* region: 'us-east-1',
22-
* endpoint: 'http://minio:9000',
23-
* accessKeyId: process.env.MINIO_ACCESS_KEY!,
24-
* secretAccessKey: process.env.MINIO_SECRET_KEY!,
25-
* },
32+
* connection: getConnection, // pass function ref, NOT getConnection()
2633
* allowedOrigins: ['https://app.example.com'],
2734
* bucketNamePrefix: 'myapp',
2835
* }),

graphile/graphile-bucket-provisioner-plugin/src/preset.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,24 @@ import { createBucketProvisionerPlugin } from './plugin';
1515
* @example
1616
* ```typescript
1717
* import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
18+
* import { getEnvOptions } from '@constructive-io/graphql-env';
19+
*
20+
* // Use a lazy getter so env vars are read at runtime, not import time
21+
* function getConnection() {
22+
* const { cdn } = getEnvOptions();
23+
* return {
24+
* provider: cdn?.provider || 'minio',
25+
* region: cdn?.awsRegion || 'us-east-1',
26+
* endpoint: cdn?.endpoint || 'http://minio:9000',
27+
* accessKeyId: cdn?.awsAccessKey!,
28+
* secretAccessKey: cdn?.awsSecretKey!,
29+
* };
30+
* }
1831
*
1932
* const preset = {
2033
* extends: [
2134
* BucketProvisionerPreset({
22-
* connection: {
23-
* provider: 'minio',
24-
* region: 'us-east-1',
25-
* endpoint: 'http://minio:9000',
26-
* accessKeyId: process.env.MINIO_ACCESS_KEY!,
27-
* secretAccessKey: process.env.MINIO_SECRET_KEY!,
28-
* },
35+
* connection: getConnection, // pass function ref, NOT getConnection()
2936
* allowedOrigins: ['https://app.example.com'],
3037
* bucketNamePrefix: 'myapp',
3138
* }),

graphile/graphile-settings/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"express": "^5.2.1",
4545
"grafast": "1.0.0",
4646
"grafserv": "1.0.0",
47+
"graphile-bucket-provisioner-plugin": "workspace:*",
4748
"graphile-build": "5.0.0",
4849
"graphile-build-pg": "5.0.0",
4950
"graphile-config": "1.0.0",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Bucket provisioner resolver for the Constructive bucket provisioner plugin.
3+
*
4+
* Reads CDN/S3 configuration from the standard env system
5+
* (getEnvOptions -> pgpmDefaults + config files + env vars) and lazily
6+
* returns a StorageConnectionConfig on first use.
7+
*
8+
* Follows the same lazy-init pattern as presigned-url-resolver.ts.
9+
*/
10+
11+
import { getEnvOptions } from '@constructive-io/graphql-env';
12+
import { Logger } from '@pgpmjs/logger';
13+
import type { StorageConnectionConfig } from 'graphile-bucket-provisioner-plugin';
14+
15+
const log = new Logger('bucket-provisioner-resolver');
16+
17+
let connectionConfig: StorageConnectionConfig | null = null;
18+
19+
/**
20+
* Lazily initialize and return the StorageConnectionConfig for the
21+
* bucket provisioner plugin.
22+
*
23+
* Reads CDN config on first call via getEnvOptions() (which already merges
24+
* pgpmDefaults -> config file -> env vars) and caches the result.
25+
* Same CDN config source as presigned-url-resolver.ts.
26+
*/
27+
export function getBucketProvisionerConnection(): StorageConnectionConfig {
28+
if (connectionConfig) return connectionConfig;
29+
30+
const { cdn } = getEnvOptions();
31+
32+
// cdn is guaranteed populated — pgpmDefaults provides all CDN fields
33+
const { provider, awsRegion, awsAccessKey, awsSecretKey, endpoint } = cdn!;
34+
35+
log.info(
36+
`[bucket-provisioner-resolver] Initializing: provider=${provider} endpoint=${endpoint}`,
37+
);
38+
39+
connectionConfig = {
40+
provider: (provider as StorageConnectionConfig['provider']) || 'minio',
41+
region: awsRegion || 'us-east-1',
42+
accessKeyId: awsAccessKey!,
43+
secretAccessKey: awsSecretKey!,
44+
...(endpoint ? { endpoint, forcePathStyle: true } : {}),
45+
};
46+
47+
return connectionConfig;
48+
}

graphile/graphile-settings/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,6 @@ export { streamToStorage } from './upload-resolver';
6262

6363
// Presigned URL utilities
6464
export { getPresignedUrlS3Config } from './presigned-url-resolver';
65+
66+
// Bucket provisioner utilities
67+
export { getBucketProvisionerConnection } from './bucket-provisioner-resolver';

graphile/graphile-settings/src/presets/constructive-preset.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import { UnifiedSearchPreset, createMatchesOperatorFactory, createTrgmOperatorFa
1616
import { GraphilePostgisPreset, createPostgisOperatorFactory } from 'graphile-postgis';
1717
import { UploadPreset } from 'graphile-upload-plugin';
1818
import { PresignedUrlPreset } from 'graphile-presigned-url-plugin';
19+
import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
1920
import { SqlExpressionValidatorPreset } from 'graphile-sql-expression-validator';
2021
import { constructiveUploadFieldDefinitions } from '../upload-resolver';
2122
import { getPresignedUrlS3Config } from '../presigned-url-resolver';
23+
import { getBucketProvisionerConnection } from '../bucket-provisioner-resolver';
2224

2325
/**
2426
* Constructive PostGraphile v5 Preset
@@ -39,6 +41,8 @@ import { getPresignedUrlS3Config } from '../presigned-url-resolver';
3941
* - PostGIS connection filter operators (spatial filtering on geometry/geography columns)
4042
* - Upload plugin (file upload to S3/MinIO for image, upload, attachment domain columns)
4143
* - Presigned URL plugin (requestUploadUrl, confirmUpload mutations + downloadUrl computed field)
44+
* - Bucket provisioner plugin (auto-provisions S3 buckets on @storageBuckets table mutations,
45+
* CORS management, provisionBucket mutation for manual/retry)
4246
* - SQL expression validator (validates @sqlExpression columns in mutations)
4347
* - PG type mappings (maps custom types like email, url to GraphQL scalars)
4448
* - pgvector search (auto-discovers vector columns: filter fields, distance computed fields,
@@ -87,6 +91,10 @@ export const ConstructivePreset: GraphileConfig.Preset = {
8791
maxFileSize: 10 * 1024 * 1024, // 10MB
8892
}),
8993
PresignedUrlPreset({ s3: getPresignedUrlS3Config }),
94+
BucketProvisionerPreset({
95+
connection: getBucketProvisionerConnection,
96+
allowedOrigins: ['http://localhost:3000'],
97+
}),
9098
SqlExpressionValidatorPreset(),
9199
PgTypeMappingsPreset,
92100
RequiredInputPreset,

pnpm-lock.yaml

Lines changed: 24 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)