graphile-settings 6.24.8 → 6.25.1

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/esm/index.d.ts CHANGED
@@ -35,4 +35,4 @@ export { ConstructivePreset, createConstructivePreset } from './presets/construc
35
35
  export * from './plugins/index';
36
36
  export * from './presets/index';
37
37
  export { makePgService };
38
- export { getPresignedUrlS3Config } from './presigned-url-resolver';
38
+ export { getStorageCredentials } from './presigned-url-resolver';
package/esm/index.js CHANGED
@@ -46,4 +46,4 @@ export * from './presets/index';
46
46
  // Re-export makePgService for convenience
47
47
  export { makePgService };
48
48
  // Presigned URL utilities
49
- export { getPresignedUrlS3Config } from './presigned-url-resolver';
49
+ export { getStorageCredentials } from './presigned-url-resolver';
@@ -12,7 +12,7 @@ import { RealtimeSubscriptionsPreset } from 'graphile-realtime-subscriptions';
12
12
  import { createMatchesOperatorFactory, createTrgmOperatorFactories, UnifiedSearchPreset } from 'graphile-search';
13
13
  import { UploadPreset } from 'graphile-upload-plugin';
14
14
  import { ConflictDetectorPreset, EnableAllFilterColumnsPreset, InflectorLoggerPreset, InflektPreset, ManyToManyOptInPreset, MetaSchemaPreset, MinimalPreset, NoUniqueLookupPreset, PgTypeMappingsPreset, RequiredInputPreset } from '../plugins';
15
- import { getPresignedUrlS3Config } from '../presigned-url-resolver';
15
+ import { getStorageCredentials } from '../presigned-url-resolver';
16
16
  import { constructiveUploadFieldDefinitions } from '../upload-resolver';
17
17
  /**
18
18
  * grafast / @dataplan/pg require Node >= 22 (they call Promise.withResolvers()
@@ -138,7 +138,7 @@ export function createConstructivePreset(options) {
138
138
  }
139
139
  if (opts.enablePresignedUploads) {
140
140
  presets.push(PresignedUrlPreset({
141
- s3: getPresignedUrlS3Config,
141
+ credentials: getStorageCredentials,
142
142
  }), BucketProvisionerPreset());
143
143
  }
144
144
  if (opts.enableAggregates) {
@@ -1,23 +1,10 @@
1
1
  /**
2
- * Presigned URL resolver for the Constructive presigned URL plugin.
2
+ * Object-store credentials for the presigned URL plugin.
3
3
  *
4
- * Reads CDN/S3 configuration from the standard env system
5
- * (getEnvOptions → pgpmDefaults + config files + env vars) and lazily
6
- * initializes an S3Client on first use.
7
- *
8
- * Follows the same lazy-init pattern as upload-resolver.ts.
9
- */
10
- import type { S3Config } from 'graphile-presigned-url-plugin';
11
- /**
12
- * Lazily initialize and return the S3Config for the presigned URL plugin.
13
- *
14
- * Reads CDN config on first call via getEnvOptions() (which already merges
15
- * pgpmDefaults → config file → env vars), creates an S3Client, and caches
16
- * the result. Same CDN config as upload-resolver.ts.
17
- *
18
- * NOTE: The `bucket` field here is only the connection's default and is never
19
- * uploaded to. Every managed upload names its bucket explicitly, resolved from
20
- * the tenant's logical bucket row; there is no environment-global upload
21
- * bucket.
4
+ * Credentials are the only storage input read from the environment
5
+ * (`STORAGE_ACCESS_KEY_ID` / `STORAGE_SECRET_ACCESS_KEY`). Endpoint, provider,
6
+ * region, bucket and public URL prefix are resolved per request from the
7
+ * tenant's `storage_module` row by the plugin itself.
22
8
  */
23
- export declare function getPresignedUrlS3Config(): S3Config;
9
+ import type { StorageCredentials } from 'graphile-presigned-url-plugin';
10
+ export declare function getStorageCredentials(): StorageCredentials;
@@ -1,61 +1,17 @@
1
1
  /**
2
- * Presigned URL resolver for the Constructive presigned URL plugin.
2
+ * Object-store credentials for the presigned URL plugin.
3
3
  *
4
- * Reads CDN/S3 configuration from the standard env system
5
- * (getEnvOptions → pgpmDefaults + config files + env vars) and lazily
6
- * initializes an S3Client on first use.
7
- *
8
- * Follows the same lazy-init pattern as upload-resolver.ts.
4
+ * Credentials are the only storage input read from the environment
5
+ * (`STORAGE_ACCESS_KEY_ID` / `STORAGE_SECRET_ACCESS_KEY`). Endpoint, provider,
6
+ * region, bucket and public URL prefix are resolved per request from the
7
+ * tenant's `storage_module` row by the plugin itself.
9
8
  */
10
9
  import { getEnvOptions } from '@constructive-io/graphql-env';
11
- import { createS3Client } from '@constructive-io/s3-utils';
12
- import { Logger } from '@pgpmjs/logger';
13
- const log = new Logger('presigned-url-resolver');
14
- let s3Config = null;
15
- /**
16
- * Lazily initialize and return the S3Config for the presigned URL plugin.
17
- *
18
- * Reads CDN config on first call via getEnvOptions() (which already merges
19
- * pgpmDefaults → config file → env vars), creates an S3Client, and caches
20
- * the result. Same CDN config as upload-resolver.ts.
21
- *
22
- * NOTE: The `bucket` field here is only the connection's default and is never
23
- * uploaded to. Every managed upload names its bucket explicitly, resolved from
24
- * the tenant's logical bucket row; there is no environment-global upload
25
- * bucket.
26
- */
27
- export function getPresignedUrlS3Config() {
28
- if (s3Config)
29
- return s3Config;
30
- const { cdn } = getEnvOptions();
31
- if (!cdn) {
32
- throw new Error('[presigned-url-resolver] CDN config not found. ' +
33
- 'Ensure CDN environment variables (AWS_ACCESS_KEY, AWS_SECRET_KEY, etc.) ' +
34
- 'are set or that pgpmDefaults provides CDN fields.');
35
- }
36
- const { bucketName, awsRegion, awsAccessKey, awsSecretKey, endpoint, publicUrlPrefix } = cdn;
37
- if (!awsAccessKey || !awsSecretKey) {
38
- throw new Error('[presigned-url-resolver] Missing S3 credentials. ' +
39
- 'Set AWS_ACCESS_KEY and AWS_SECRET_KEY environment variables.');
40
- }
41
- if (!bucketName) {
42
- throw new Error('[presigned-url-resolver] Missing CDN bucket name. ' +
43
- 'Set CDN_BUCKET_NAME environment variable.');
10
+ export function getStorageCredentials() {
11
+ const { accessKeyId, secretAccessKey } = getEnvOptions().storage ?? {};
12
+ if (!accessKeyId || !secretAccessKey) {
13
+ throw new Error('STORAGE_CREDENTIALS_MISSING: object storage requires STORAGE_ACCESS_KEY_ID and ' +
14
+ 'STORAGE_SECRET_ACCESS_KEY');
44
15
  }
45
- log.info(`[presigned-url-resolver] Initializing: bucket=${bucketName} endpoint=${endpoint}`);
46
- const client = createS3Client({
47
- provider: (cdn.provider || 'minio'),
48
- region: awsRegion,
49
- accessKeyId: awsAccessKey,
50
- secretAccessKey: awsSecretKey,
51
- ...(endpoint ? { endpoint } : {}),
52
- });
53
- s3Config = {
54
- client,
55
- bucket: bucketName,
56
- region: awsRegion,
57
- publicUrlPrefix,
58
- ...(endpoint ? { endpoint, forcePathStyle: true } : {}),
59
- };
60
- return s3Config;
16
+ return { accessKeyId, secretAccessKey };
61
17
  }
@@ -9,21 +9,15 @@
9
9
  * It used to be a second storage model: stream to `BUCKET_NAME` under a random
10
10
  * key, hand back a URL, record nothing. Objects written that way belonged to no
11
11
  * database, could not be deduplicated, listed, or access-controlled, and storage
12
- * GC could not see that a document still pointed at them. There is no
13
- * environment bucket in this path any more; `cdn.*` supplies S3 credentials and
14
- * an endpoint only.
12
+ * GC could not see that a document still pointed at them. Nothing in this path
13
+ * comes from the environment but the object-store credentials: the bucket and
14
+ * its connection (endpoint/provider/region) are the tenant's storage module.
15
15
  *
16
16
  * Compatibility: `image`/`upload` columns still receive `url` alongside the new
17
17
  * `id`/`key`/`bucket_id`/`size` fields, so existing readers of `photo.url` keep
18
18
  * working while they migrate to `id` + the files row's late-bound `downloadUrl`.
19
19
  *
20
- * ENV VARS (S3 connection only):
21
- * BUCKET_PROVIDER - 'minio' | 's3' (default: 'minio')
22
- * AWS_REGION - AWS region (default: 'us-east-1')
23
- * Defaults come from `pgpmDefaults.cdn` (dev-only values; set these in production).
24
- * AWS_ACCESS_KEY - access key
25
- * AWS_SECRET_KEY - secret key
26
- * CDN_ENDPOINT - S3-compatible endpoint
20
+ * ENV VARS: STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY (required).
27
21
  */
28
22
  import type { UploadFieldDefinition } from 'graphile-upload-plugin';
29
23
  /**
@@ -9,70 +9,28 @@
9
9
  * It used to be a second storage model: stream to `BUCKET_NAME` under a random
10
10
  * key, hand back a URL, record nothing. Objects written that way belonged to no
11
11
  * database, could not be deduplicated, listed, or access-controlled, and storage
12
- * GC could not see that a document still pointed at them. There is no
13
- * environment bucket in this path any more; `cdn.*` supplies S3 credentials and
14
- * an endpoint only.
12
+ * GC could not see that a document still pointed at them. Nothing in this path
13
+ * comes from the environment but the object-store credentials: the bucket and
14
+ * its connection (endpoint/provider/region) are the tenant's storage module.
15
15
  *
16
16
  * Compatibility: `image`/`upload` columns still receive `url` alongside the new
17
17
  * `id`/`key`/`bucket_id`/`size` fields, so existing readers of `photo.url` keep
18
18
  * working while they migrate to `id` + the files row's late-bound `downloadUrl`.
19
19
  *
20
- * ENV VARS (S3 connection only):
21
- * BUCKET_PROVIDER - 'minio' | 's3' (default: 'minio')
22
- * AWS_REGION - AWS region (default: 'us-east-1')
23
- * Defaults come from `pgpmDefaults.cdn` (dev-only values; set these in production).
24
- * AWS_ACCESS_KEY - access key
25
- * AWS_SECRET_KEY - secret key
26
- * CDN_ENDPOINT - S3-compatible endpoint
20
+ * ENV VARS: STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY (required).
27
21
  */
28
- import { getEnvOptions } from '@constructive-io/graphql-env';
29
22
  import Streamer from '@constructive-io/s3-streamer';
30
- import { Logger } from '@pgpmjs/logger';
31
23
  import { createHash, randomUUID } from 'crypto';
32
24
  import { finalizeStagedUpload, resolveManagedUploadTarget, withRequestPgClient, } from 'graphile-presigned-url-plugin';
33
25
  import { checkTypeAgreement } from 'mime-bytes';
34
26
  import { Transform } from 'stream';
35
- import { getPresignedUrlS3Config } from './presigned-url-resolver';
36
- const log = new Logger('upload-resolver');
27
+ import { getStorageCredentials } from './presigned-url-resolver';
37
28
  const DEFAULT_IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/svg+xml'];
38
- let streamer = null;
39
- /**
40
- * The S3 streamer, built from the CDN connection settings.
41
- *
42
- * Deliberately constructed with no `defaultBucket`: every upload names the
43
- * bucket it resolved, and a default here would be an environment-owned bucket
44
- * standing in for a tenant's.
45
- */
46
- function getStreamer() {
47
- if (streamer)
48
- return streamer;
49
- const { cdn } = getEnvOptions();
50
- if (process.env.NODE_ENV === 'production' && (!cdn.awsAccessKey || !cdn.awsSecretKey)) {
51
- log.warn('[upload-resolver] WARNING: CDN credentials not configured in production.');
52
- }
53
- const provider = cdn.provider;
54
- log.info(`[upload-resolver] Initializing: provider=${provider}`);
55
- streamer = new Streamer({
56
- provider,
57
- awsRegion: cdn.awsRegion,
58
- awsAccessKey: cdn.awsAccessKey,
59
- awsSecretKey: cdn.awsSecretKey,
60
- endpoint: cdn.endpoint,
61
- });
62
- return streamer;
63
- }
64
- /**
65
- * The upload lane's view of the presigned plugin's options: the same S3
66
- * connection the presigned lane uses, so both transports resolve identical
67
- * coordinates for a bucket.
68
- *
69
- * Built on first upload rather than at import time.
70
- */
71
29
  let managedOptions = null;
72
30
  function getManagedOptions() {
73
31
  if (!managedOptions) {
74
32
  managedOptions = {
75
- s3: getPresignedUrlS3Config,
33
+ credentials: getStorageCredentials,
76
34
  };
77
35
  }
78
36
  return managedOptions;
@@ -168,7 +126,7 @@ async function uploadResolver(upload, _args, context, info) {
168
126
  `bucket "${target.bucket.key}" is private, whose only URLs expire. Use an upload column, ` +
169
127
  'which stores the file id and resolves a fresh download URL on read.');
170
128
  }
171
- const s3 = getStreamer();
129
+ const s3 = new Streamer({ client: target.s3.client, defaultBucket: target.physicalName });
172
130
  const { filename } = upload;
173
131
  // Validate before persisting: content type comes from the leading bytes, not
174
132
  // from the client's claim about them.
package/index.d.ts CHANGED
@@ -35,4 +35,4 @@ export { ConstructivePreset, createConstructivePreset } from './presets/construc
35
35
  export * from './plugins/index';
36
36
  export * from './presets/index';
37
37
  export { makePgService };
38
- export { getPresignedUrlS3Config } from './presigned-url-resolver';
38
+ export { getStorageCredentials } from './presigned-url-resolver';
package/index.js CHANGED
@@ -42,7 +42,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
42
42
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.getPresignedUrlS3Config = exports.makePgService = exports.createConstructivePreset = exports.ConstructivePreset = exports.createGrafastCacheLimitsPreset = exports.createGrafastCacheLimitsPlugin = exports.applyGrafastCacheLimits = void 0;
45
+ exports.getStorageCredentials = exports.makePgService = exports.createConstructivePreset = exports.ConstructivePreset = exports.createGrafastCacheLimitsPreset = exports.createGrafastCacheLimitsPlugin = exports.applyGrafastCacheLimits = void 0;
46
46
  // Import modules for type augmentation
47
47
  // These add properties to the GraphileConfig.Preset interface:
48
48
  // - grafserv: adds 'grafserv' property
@@ -65,4 +65,4 @@ __exportStar(require("./plugins/index"), exports);
65
65
  __exportStar(require("./presets/index"), exports);
66
66
  // Presigned URL utilities
67
67
  var presigned_url_resolver_1 = require("./presigned-url-resolver");
68
- Object.defineProperty(exports, "getPresignedUrlS3Config", { enumerable: true, get: function () { return presigned_url_resolver_1.getPresignedUrlS3Config; } });
68
+ Object.defineProperty(exports, "getStorageCredentials", { enumerable: true, get: function () { return presigned_url_resolver_1.getStorageCredentials; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-settings",
3
- "version": "6.24.8",
3
+ "version": "6.25.1",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "graphile settings",
6
6
  "main": "index.js",
@@ -30,38 +30,37 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@aws-sdk/client-s3": "^3.1052.0",
33
- "@constructive-io/bucket-provisioner": "^0.29.0",
34
- "@constructive-io/graphql-env": "^3.34.6",
35
- "@constructive-io/graphql-types": "^3.33.6",
36
- "@constructive-io/s3-streamer": "^2.44.6",
37
- "@constructive-io/s3-utils": "^2.34.0",
33
+ "@constructive-io/bucket-provisioner": "^0.30.0",
34
+ "@constructive-io/graphql-env": "^3.35.0",
35
+ "@constructive-io/graphql-types": "^3.34.0",
36
+ "@constructive-io/s3-streamer": "^2.45.0",
38
37
  "@constructive-io/upload-names": "^2.31.0",
39
38
  "@dataplan/json": "1.0.1",
40
39
  "@dataplan/pg": "1.1.1",
41
40
  "@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
42
41
  "@pgpmjs/logger": "^2.26.0",
43
- "@pgpmjs/types": "^2.56.3",
42
+ "@pgpmjs/types": "^2.57.0",
44
43
  "@pgsql/quotes": "^18.2.4",
45
44
  "cors": "^2.8.6",
46
45
  "express": "^5.2.1",
47
46
  "grafast": "1.1.2",
48
47
  "grafserv": "1.0.1",
49
- "graphile-bucket-provisioner-plugin": "1.16.0",
48
+ "graphile-bucket-provisioner-plugin": "1.17.0",
50
49
  "graphile-build": "5.1.1",
51
50
  "graphile-build-pg": "5.1.3",
52
- "graphile-bulk-mutations": "^1.17.7",
51
+ "graphile-bulk-mutations": "^1.17.9",
53
52
  "graphile-config": "1.1.0",
54
- "graphile-connection-filter": "^2.17.7",
55
- "graphile-history": "^1.9.7",
56
- "graphile-i18n": "^2.18.7",
57
- "graphile-llm": "^1.18.7",
58
- "graphile-ltree": "^2.17.7",
53
+ "graphile-connection-filter": "^2.17.9",
54
+ "graphile-history": "^1.9.9",
55
+ "graphile-i18n": "^2.18.9",
56
+ "graphile-llm": "^1.18.9",
57
+ "graphile-ltree": "^2.17.9",
59
58
  "graphile-meta": "^1.10.0",
60
- "graphile-pg-aggregates": "^2.17.7",
61
- "graphile-postgis": "^3.18.7",
62
- "graphile-presigned-url-plugin": "^1.21.1",
59
+ "graphile-pg-aggregates": "^2.17.9",
60
+ "graphile-postgis": "^3.18.9",
61
+ "graphile-presigned-url-plugin": "^1.22.1",
63
62
  "graphile-realtime-subscriptions": "^1.12.0",
64
- "graphile-search": "^2.17.7",
63
+ "graphile-search": "^2.17.9",
65
64
  "graphile-upload-plugin": "^2.27.0",
66
65
  "graphile-utils": "5.0.3",
67
66
  "graphql": "16.13.0",
@@ -76,11 +75,12 @@
76
75
  "tamedevil": "0.1.1"
77
76
  },
78
77
  "devDependencies": {
78
+ "@constructive-io/s3-utils": "^2.34.0",
79
79
  "@types/cors": "^2.8.17",
80
80
  "@types/express": "^5.0.6",
81
81
  "@types/pg": "^8.20.4",
82
82
  "@types/request-ip": "^0.0.41",
83
- "graphile-test": "^5.17.7",
83
+ "graphile-test": "^5.17.9",
84
84
  "makage": "^0.8.0",
85
85
  "nodemon": "^3.1.14",
86
86
  "ts-node": "^10.9.2"
@@ -92,5 +92,5 @@
92
92
  "constructive",
93
93
  "graphql"
94
94
  ],
95
- "gitHead": "a95137c03998f4ef18aaba770bdeb598077e5b51"
95
+ "gitHead": "850fc5e75a3a31e2db2fc28aa58377e886fe3c6a"
96
96
  }
@@ -142,7 +142,7 @@ function createConstructivePreset(options) {
142
142
  }
143
143
  if (opts.enablePresignedUploads) {
144
144
  presets.push((0, graphile_presigned_url_plugin_1.PresignedUrlPreset)({
145
- s3: presigned_url_resolver_1.getPresignedUrlS3Config,
145
+ credentials: presigned_url_resolver_1.getStorageCredentials,
146
146
  }), (0, graphile_bucket_provisioner_plugin_1.BucketProvisionerPreset)());
147
147
  }
148
148
  if (opts.enableAggregates) {
@@ -1,23 +1,10 @@
1
1
  /**
2
- * Presigned URL resolver for the Constructive presigned URL plugin.
2
+ * Object-store credentials for the presigned URL plugin.
3
3
  *
4
- * Reads CDN/S3 configuration from the standard env system
5
- * (getEnvOptions → pgpmDefaults + config files + env vars) and lazily
6
- * initializes an S3Client on first use.
7
- *
8
- * Follows the same lazy-init pattern as upload-resolver.ts.
9
- */
10
- import type { S3Config } from 'graphile-presigned-url-plugin';
11
- /**
12
- * Lazily initialize and return the S3Config for the presigned URL plugin.
13
- *
14
- * Reads CDN config on first call via getEnvOptions() (which already merges
15
- * pgpmDefaults → config file → env vars), creates an S3Client, and caches
16
- * the result. Same CDN config as upload-resolver.ts.
17
- *
18
- * NOTE: The `bucket` field here is only the connection's default and is never
19
- * uploaded to. Every managed upload names its bucket explicitly, resolved from
20
- * the tenant's logical bucket row; there is no environment-global upload
21
- * bucket.
4
+ * Credentials are the only storage input read from the environment
5
+ * (`STORAGE_ACCESS_KEY_ID` / `STORAGE_SECRET_ACCESS_KEY`). Endpoint, provider,
6
+ * region, bucket and public URL prefix are resolved per request from the
7
+ * tenant's `storage_module` row by the plugin itself.
22
8
  */
23
- export declare function getPresignedUrlS3Config(): S3Config;
9
+ import type { StorageCredentials } from 'graphile-presigned-url-plugin';
10
+ export declare function getStorageCredentials(): StorageCredentials;
@@ -1,64 +1,20 @@
1
1
  "use strict";
2
2
  /**
3
- * Presigned URL resolver for the Constructive presigned URL plugin.
3
+ * Object-store credentials for the presigned URL plugin.
4
4
  *
5
- * Reads CDN/S3 configuration from the standard env system
6
- * (getEnvOptions → pgpmDefaults + config files + env vars) and lazily
7
- * initializes an S3Client on first use.
8
- *
9
- * Follows the same lazy-init pattern as upload-resolver.ts.
5
+ * Credentials are the only storage input read from the environment
6
+ * (`STORAGE_ACCESS_KEY_ID` / `STORAGE_SECRET_ACCESS_KEY`). Endpoint, provider,
7
+ * region, bucket and public URL prefix are resolved per request from the
8
+ * tenant's `storage_module` row by the plugin itself.
10
9
  */
11
10
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.getPresignedUrlS3Config = getPresignedUrlS3Config;
11
+ exports.getStorageCredentials = getStorageCredentials;
13
12
  const graphql_env_1 = require("@constructive-io/graphql-env");
14
- const s3_utils_1 = require("@constructive-io/s3-utils");
15
- const logger_1 = require("@pgpmjs/logger");
16
- const log = new logger_1.Logger('presigned-url-resolver');
17
- let s3Config = null;
18
- /**
19
- * Lazily initialize and return the S3Config for the presigned URL plugin.
20
- *
21
- * Reads CDN config on first call via getEnvOptions() (which already merges
22
- * pgpmDefaults → config file → env vars), creates an S3Client, and caches
23
- * the result. Same CDN config as upload-resolver.ts.
24
- *
25
- * NOTE: The `bucket` field here is only the connection's default and is never
26
- * uploaded to. Every managed upload names its bucket explicitly, resolved from
27
- * the tenant's logical bucket row; there is no environment-global upload
28
- * bucket.
29
- */
30
- function getPresignedUrlS3Config() {
31
- if (s3Config)
32
- return s3Config;
33
- const { cdn } = (0, graphql_env_1.getEnvOptions)();
34
- if (!cdn) {
35
- throw new Error('[presigned-url-resolver] CDN config not found. ' +
36
- 'Ensure CDN environment variables (AWS_ACCESS_KEY, AWS_SECRET_KEY, etc.) ' +
37
- 'are set or that pgpmDefaults provides CDN fields.');
38
- }
39
- const { bucketName, awsRegion, awsAccessKey, awsSecretKey, endpoint, publicUrlPrefix } = cdn;
40
- if (!awsAccessKey || !awsSecretKey) {
41
- throw new Error('[presigned-url-resolver] Missing S3 credentials. ' +
42
- 'Set AWS_ACCESS_KEY and AWS_SECRET_KEY environment variables.');
43
- }
44
- if (!bucketName) {
45
- throw new Error('[presigned-url-resolver] Missing CDN bucket name. ' +
46
- 'Set CDN_BUCKET_NAME environment variable.');
13
+ function getStorageCredentials() {
14
+ const { accessKeyId, secretAccessKey } = (0, graphql_env_1.getEnvOptions)().storage ?? {};
15
+ if (!accessKeyId || !secretAccessKey) {
16
+ throw new Error('STORAGE_CREDENTIALS_MISSING: object storage requires STORAGE_ACCESS_KEY_ID and ' +
17
+ 'STORAGE_SECRET_ACCESS_KEY');
47
18
  }
48
- log.info(`[presigned-url-resolver] Initializing: bucket=${bucketName} endpoint=${endpoint}`);
49
- const client = (0, s3_utils_1.createS3Client)({
50
- provider: (cdn.provider || 'minio'),
51
- region: awsRegion,
52
- accessKeyId: awsAccessKey,
53
- secretAccessKey: awsSecretKey,
54
- ...(endpoint ? { endpoint } : {}),
55
- });
56
- s3Config = {
57
- client,
58
- bucket: bucketName,
59
- region: awsRegion,
60
- publicUrlPrefix,
61
- ...(endpoint ? { endpoint, forcePathStyle: true } : {}),
62
- };
63
- return s3Config;
19
+ return { accessKeyId, secretAccessKey };
64
20
  }
@@ -9,21 +9,15 @@
9
9
  * It used to be a second storage model: stream to `BUCKET_NAME` under a random
10
10
  * key, hand back a URL, record nothing. Objects written that way belonged to no
11
11
  * database, could not be deduplicated, listed, or access-controlled, and storage
12
- * GC could not see that a document still pointed at them. There is no
13
- * environment bucket in this path any more; `cdn.*` supplies S3 credentials and
14
- * an endpoint only.
12
+ * GC could not see that a document still pointed at them. Nothing in this path
13
+ * comes from the environment but the object-store credentials: the bucket and
14
+ * its connection (endpoint/provider/region) are the tenant's storage module.
15
15
  *
16
16
  * Compatibility: `image`/`upload` columns still receive `url` alongside the new
17
17
  * `id`/`key`/`bucket_id`/`size` fields, so existing readers of `photo.url` keep
18
18
  * working while they migrate to `id` + the files row's late-bound `downloadUrl`.
19
19
  *
20
- * ENV VARS (S3 connection only):
21
- * BUCKET_PROVIDER - 'minio' | 's3' (default: 'minio')
22
- * AWS_REGION - AWS region (default: 'us-east-1')
23
- * Defaults come from `pgpmDefaults.cdn` (dev-only values; set these in production).
24
- * AWS_ACCESS_KEY - access key
25
- * AWS_SECRET_KEY - secret key
26
- * CDN_ENDPOINT - S3-compatible endpoint
20
+ * ENV VARS: STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY (required).
27
21
  */
28
22
  import type { UploadFieldDefinition } from 'graphile-upload-plugin';
29
23
  /**
@@ -10,75 +10,33 @@
10
10
  * It used to be a second storage model: stream to `BUCKET_NAME` under a random
11
11
  * key, hand back a URL, record nothing. Objects written that way belonged to no
12
12
  * database, could not be deduplicated, listed, or access-controlled, and storage
13
- * GC could not see that a document still pointed at them. There is no
14
- * environment bucket in this path any more; `cdn.*` supplies S3 credentials and
15
- * an endpoint only.
13
+ * GC could not see that a document still pointed at them. Nothing in this path
14
+ * comes from the environment but the object-store credentials: the bucket and
15
+ * its connection (endpoint/provider/region) are the tenant's storage module.
16
16
  *
17
17
  * Compatibility: `image`/`upload` columns still receive `url` alongside the new
18
18
  * `id`/`key`/`bucket_id`/`size` fields, so existing readers of `photo.url` keep
19
19
  * working while they migrate to `id` + the files row's late-bound `downloadUrl`.
20
20
  *
21
- * ENV VARS (S3 connection only):
22
- * BUCKET_PROVIDER - 'minio' | 's3' (default: 'minio')
23
- * AWS_REGION - AWS region (default: 'us-east-1')
24
- * Defaults come from `pgpmDefaults.cdn` (dev-only values; set these in production).
25
- * AWS_ACCESS_KEY - access key
26
- * AWS_SECRET_KEY - secret key
27
- * CDN_ENDPOINT - S3-compatible endpoint
21
+ * ENV VARS: STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY (required).
28
22
  */
29
23
  var __importDefault = (this && this.__importDefault) || function (mod) {
30
24
  return (mod && mod.__esModule) ? mod : { "default": mod };
31
25
  };
32
26
  Object.defineProperty(exports, "__esModule", { value: true });
33
27
  exports.constructiveUploadFieldDefinitions = void 0;
34
- const graphql_env_1 = require("@constructive-io/graphql-env");
35
28
  const s3_streamer_1 = __importDefault(require("@constructive-io/s3-streamer"));
36
- const logger_1 = require("@pgpmjs/logger");
37
29
  const crypto_1 = require("crypto");
38
30
  const graphile_presigned_url_plugin_1 = require("graphile-presigned-url-plugin");
39
31
  const mime_bytes_1 = require("mime-bytes");
40
32
  const stream_1 = require("stream");
41
33
  const presigned_url_resolver_1 = require("./presigned-url-resolver");
42
- const log = new logger_1.Logger('upload-resolver');
43
34
  const DEFAULT_IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/svg+xml'];
44
- let streamer = null;
45
- /**
46
- * The S3 streamer, built from the CDN connection settings.
47
- *
48
- * Deliberately constructed with no `defaultBucket`: every upload names the
49
- * bucket it resolved, and a default here would be an environment-owned bucket
50
- * standing in for a tenant's.
51
- */
52
- function getStreamer() {
53
- if (streamer)
54
- return streamer;
55
- const { cdn } = (0, graphql_env_1.getEnvOptions)();
56
- if (process.env.NODE_ENV === 'production' && (!cdn.awsAccessKey || !cdn.awsSecretKey)) {
57
- log.warn('[upload-resolver] WARNING: CDN credentials not configured in production.');
58
- }
59
- const provider = cdn.provider;
60
- log.info(`[upload-resolver] Initializing: provider=${provider}`);
61
- streamer = new s3_streamer_1.default({
62
- provider,
63
- awsRegion: cdn.awsRegion,
64
- awsAccessKey: cdn.awsAccessKey,
65
- awsSecretKey: cdn.awsSecretKey,
66
- endpoint: cdn.endpoint,
67
- });
68
- return streamer;
69
- }
70
- /**
71
- * The upload lane's view of the presigned plugin's options: the same S3
72
- * connection the presigned lane uses, so both transports resolve identical
73
- * coordinates for a bucket.
74
- *
75
- * Built on first upload rather than at import time.
76
- */
77
35
  let managedOptions = null;
78
36
  function getManagedOptions() {
79
37
  if (!managedOptions) {
80
38
  managedOptions = {
81
- s3: presigned_url_resolver_1.getPresignedUrlS3Config,
39
+ credentials: presigned_url_resolver_1.getStorageCredentials,
82
40
  };
83
41
  }
84
42
  return managedOptions;
@@ -174,7 +132,7 @@ async function uploadResolver(upload, _args, context, info) {
174
132
  `bucket "${target.bucket.key}" is private, whose only URLs expire. Use an upload column, ` +
175
133
  'which stores the file id and resolves a fresh download URL on read.');
176
134
  }
177
- const s3 = getStreamer();
135
+ const s3 = new s3_streamer_1.default({ client: target.s3.client, defaultBucket: target.physicalName });
178
136
  const { filename } = upload;
179
137
  // Validate before persisting: content type comes from the leading bytes, not
180
138
  // from the client's claim about them.