graphile-settings 6.19.0 → 6.20.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.
|
@@ -13,7 +13,7 @@ import { createMatchesOperatorFactory, createTrgmOperatorFactories, UnifiedSearc
|
|
|
13
13
|
import { UploadPreset } from 'graphile-upload-plugin';
|
|
14
14
|
import { getBucketProvisionerConnection } from '../bucket-provisioner-resolver';
|
|
15
15
|
import { ConflictDetectorPreset, EnableAllFilterColumnsPreset, InflectorLoggerPreset, InflektPreset, ManyToManyOptInPreset, MetaSchemaPreset, MinimalPreset, NoUniqueLookupPreset, PgTypeMappingsPreset, RequiredInputPreset } from '../plugins';
|
|
16
|
-
import { createBucketNameResolver, createEnsureBucketProvisioned,
|
|
16
|
+
import { createBucketNameResolver, createEnsureBucketProvisioned, getAllowedOrigins, getPresignedUrlS3Config } from '../presigned-url-resolver';
|
|
17
17
|
import { constructiveUploadFieldDefinitions } from '../upload-resolver';
|
|
18
18
|
/**
|
|
19
19
|
* grafast / @dataplan/pg require Node >= 22 (they call Promise.withResolvers()
|
|
@@ -145,16 +145,7 @@ export function createConstructivePreset(options) {
|
|
|
145
145
|
}), BucketProvisionerPreset({
|
|
146
146
|
connection: getBucketProvisionerConnection,
|
|
147
147
|
allowedOrigins: getAllowedOrigins(),
|
|
148
|
-
|
|
149
|
-
// eager provisionBucket mutation mints the identical physical name
|
|
150
|
-
// (`{prefix}-{bucketKey}-{databaseId}`) instead of falling back to the
|
|
151
|
-
// bare logical bucket key.
|
|
152
|
-
resolveBucketName: createProvisionerBucketNameResolver(),
|
|
153
|
-
// S3 buckets are provisioned lazily (on first upload) or explicitly via
|
|
154
|
-
// the provisionBucket mutation. Disable the auto-provision-on-create
|
|
155
|
-
// hook so a createBucket GraphQL mutation records the row without
|
|
156
|
-
// eagerly minting an S3 bucket that may never receive an upload.
|
|
157
|
-
autoProvision: false
|
|
148
|
+
resolveBucketName: createBucketNameResolver()
|
|
158
149
|
}));
|
|
159
150
|
}
|
|
160
151
|
if (opts.enableAggregates) {
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Follows the same lazy-init pattern as upload-resolver.ts.
|
|
12
12
|
*/
|
|
13
|
-
import type { BucketNameResolver as ProvisionerBucketNameResolver } from 'graphile-bucket-provisioner-plugin';
|
|
14
13
|
import type { BucketNameResolver, EnsureBucketProvisioned, S3Config } from 'graphile-presigned-url-plugin';
|
|
15
14
|
/**
|
|
16
15
|
* Lazily initialize and return the S3Config for the presigned URL plugin.
|
|
@@ -36,16 +35,6 @@ export declare function getPresignedUrlS3Config(): S3Config;
|
|
|
36
35
|
* S3 buckets per logical bucket key.
|
|
37
36
|
*/
|
|
38
37
|
export declare function createBucketNameResolver(): BucketNameResolver;
|
|
39
|
-
/**
|
|
40
|
-
* Create the bucket name resolver for the bucket provisioner plugin
|
|
41
|
-
* (argument order: `(bucketKey, databaseId)`).
|
|
42
|
-
*
|
|
43
|
-
* Produces the exact same physical name as createBucketNameResolver()
|
|
44
|
-
* (`{prefix}-{bucketKey}-{digest}`) so the eager `provisionBucket`
|
|
45
|
-
* mutation mints the identical tenant-aware name that the lazy first-upload
|
|
46
|
-
* path would. Throws on a missing prefix — no default bucket name.
|
|
47
|
-
*/
|
|
48
|
-
export declare function createProvisionerBucketNameResolver(): ProvisionerBucketNameResolver;
|
|
49
38
|
/**
|
|
50
39
|
* Resolve CORS allowed origins from the env/config system.
|
|
51
40
|
*
|
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Follows the same lazy-init pattern as upload-resolver.ts.
|
|
12
12
|
*/
|
|
13
|
-
import { BucketProvisioner } from '@constructive-io/bucket-provisioner';
|
|
13
|
+
import { BucketProvisioner, mintPhysicalBucketName } from '@constructive-io/bucket-provisioner';
|
|
14
14
|
import { getEnvOptions } from '@constructive-io/graphql-env';
|
|
15
15
|
import { createS3Client } from '@constructive-io/s3-utils';
|
|
16
16
|
import { Logger } from '@pgpmjs/logger';
|
|
17
|
-
import { createHash } from 'crypto';
|
|
18
17
|
import { getBucketProvisionerConnection } from './bucket-provisioner-resolver';
|
|
19
18
|
const log = new Logger('presigned-url-resolver');
|
|
20
19
|
let s3Config = null;
|
|
@@ -80,59 +79,6 @@ function getBucketNamePrefix() {
|
|
|
80
79
|
}
|
|
81
80
|
return prefix;
|
|
82
81
|
}
|
|
83
|
-
/** S3's hard ceiling on a bucket name. */
|
|
84
|
-
const MAX_BUCKET_NAME_LENGTH = 63;
|
|
85
|
-
/** S3's floor, which a degenerate prefix/key could otherwise fall under. */
|
|
86
|
-
const MIN_BUCKET_NAME_LENGTH = 3;
|
|
87
|
-
/** Hex characters of the identity digest kept as the uniqueness tail. */
|
|
88
|
-
const IDENTITY_DIGEST_LENGTH = 12;
|
|
89
|
-
/** Readable budget: how much of the name the prefix and key may each occupy. */
|
|
90
|
-
const PREFIX_BUDGET = 20;
|
|
91
|
-
const BUCKET_KEY_BUDGET = 63 - IDENTITY_DIGEST_LENGTH - PREFIX_BUDGET - 3;
|
|
92
|
-
/**
|
|
93
|
-
* Reduce a component to the S3 bucket-name alphabet: lowercase, `[a-z0-9-]`,
|
|
94
|
-
* with runs of separators collapsed and no leading or trailing hyphen.
|
|
95
|
-
*
|
|
96
|
-
* Dots are legal in a bucket name but deliberately dropped — a dotted name
|
|
97
|
-
* cannot be used with virtual-hosted-style HTTPS, because the wildcard
|
|
98
|
-
* certificate does not match a further label.
|
|
99
|
-
*/
|
|
100
|
-
function sanitizeBucketNameComponent(value) {
|
|
101
|
-
return value
|
|
102
|
-
.toLowerCase()
|
|
103
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
104
|
-
.replace(/^-+|-+$/g, '');
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* The single physical-bucket naming policy:
|
|
108
|
-
* `{prefix}-{bucketKey}-{digest}` (e.g. `myapp-public-3f9c1a2b7e04`).
|
|
109
|
-
*
|
|
110
|
-
* Both the presigned-upload (lazy) path and the bucket-provisioner (eager) path
|
|
111
|
-
* derive names from this one function, so a bucket's physical name is identical
|
|
112
|
-
* regardless of which path mints it.
|
|
113
|
-
*
|
|
114
|
-
* The name is bounded and S3-legal by construction: the prefix and key are
|
|
115
|
-
* sanitized to `[a-z0-9-]` and truncated to a readable budget, and the tail is a
|
|
116
|
-
* digest of the *untruncated* identity — so two buckets whose keys agree only
|
|
117
|
-
* past the truncation point, or the same key in two databases, still get distinct
|
|
118
|
-
* names. Names remain stable for a given (prefix, databaseId, bucketKey) because
|
|
119
|
-
* nothing here reads the clock or a counter; and an already-provisioned bucket
|
|
120
|
-
* never consults this function at all, since `platform_buckets.physical_name` is
|
|
121
|
-
* authoritative once recorded.
|
|
122
|
-
*/
|
|
123
|
-
function mintPhysicalBucketName(prefix, databaseId, bucketKey) {
|
|
124
|
-
const identity = `${prefix}/${databaseId}/${bucketKey}`;
|
|
125
|
-
const digest = createHash('sha256').update(identity).digest('hex').slice(0, IDENTITY_DIGEST_LENGTH);
|
|
126
|
-
const safePrefix = sanitizeBucketNameComponent(prefix).slice(0, PREFIX_BUDGET).replace(/-+$/, '');
|
|
127
|
-
const safeKey = sanitizeBucketNameComponent(bucketKey).slice(0, BUCKET_KEY_BUDGET).replace(/-+$/, '');
|
|
128
|
-
const name = [safePrefix, safeKey, digest].filter((part) => part.length > 0).join('-');
|
|
129
|
-
// The digest alone already satisfies both bounds, so this is only reachable
|
|
130
|
-
// when both readable components sanitize away to nothing.
|
|
131
|
-
if (name.length < MIN_BUCKET_NAME_LENGTH || name.length > MAX_BUCKET_NAME_LENGTH) {
|
|
132
|
-
throw new Error(`[presigned-url-resolver] Cannot mint a legal S3 bucket name for key "${bucketKey}": got "${name}"`);
|
|
133
|
-
}
|
|
134
|
-
return name;
|
|
135
|
-
}
|
|
136
82
|
/**
|
|
137
83
|
* Create a per-(database, bucketKey) bucket name resolver for the presigned
|
|
138
84
|
* URL plugin (argument order: `(databaseId, bucketKey)`).
|
|
@@ -147,19 +93,6 @@ export function createBucketNameResolver() {
|
|
|
147
93
|
const prefix = getBucketNamePrefix();
|
|
148
94
|
return (databaseId, bucketKey) => mintPhysicalBucketName(prefix, databaseId, bucketKey);
|
|
149
95
|
}
|
|
150
|
-
/**
|
|
151
|
-
* Create the bucket name resolver for the bucket provisioner plugin
|
|
152
|
-
* (argument order: `(bucketKey, databaseId)`).
|
|
153
|
-
*
|
|
154
|
-
* Produces the exact same physical name as createBucketNameResolver()
|
|
155
|
-
* (`{prefix}-{bucketKey}-{digest}`) so the eager `provisionBucket`
|
|
156
|
-
* mutation mints the identical tenant-aware name that the lazy first-upload
|
|
157
|
-
* path would. Throws on a missing prefix — no default bucket name.
|
|
158
|
-
*/
|
|
159
|
-
export function createProvisionerBucketNameResolver() {
|
|
160
|
-
const prefix = getBucketNamePrefix();
|
|
161
|
-
return (bucketKey, databaseId) => mintPhysicalBucketName(prefix, databaseId, bucketKey);
|
|
162
|
-
}
|
|
163
96
|
/**
|
|
164
97
|
* Resolve CORS allowed origins from the env/config system.
|
|
165
98
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-settings",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.20.1",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "graphile settings",
|
|
6
6
|
"main": "index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@aws-sdk/client-s3": "^3.1052.0",
|
|
33
|
-
"@constructive-io/bucket-provisioner": "^0.25.
|
|
33
|
+
"@constructive-io/bucket-provisioner": "^0.25.1",
|
|
34
34
|
"@constructive-io/graphql-env": "^3.31.0",
|
|
35
35
|
"@constructive-io/graphql-types": "^3.30.0",
|
|
36
36
|
"@constructive-io/s3-streamer": "^2.42.0",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"express": "^5.2.1",
|
|
47
47
|
"grafast": "1.1.2",
|
|
48
48
|
"grafserv": "1.0.1",
|
|
49
|
-
"graphile-bucket-provisioner-plugin": "1.14.
|
|
49
|
+
"graphile-bucket-provisioner-plugin": "1.14.1",
|
|
50
50
|
"graphile-build": "5.1.1",
|
|
51
51
|
"graphile-build-pg": "5.1.3",
|
|
52
52
|
"graphile-bulk-mutations": "^1.14.0",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"graphile-i18n": "^2.14.0",
|
|
57
57
|
"graphile-llm": "^1.15.0",
|
|
58
58
|
"graphile-ltree": "^2.14.0",
|
|
59
|
-
"graphile-meta": "^1.9.
|
|
59
|
+
"graphile-meta": "^1.9.1",
|
|
60
60
|
"graphile-pg-aggregates": "^2.14.0",
|
|
61
61
|
"graphile-postgis": "^3.15.0",
|
|
62
|
-
"graphile-presigned-url-plugin": "^1.
|
|
62
|
+
"graphile-presigned-url-plugin": "^1.16.1",
|
|
63
63
|
"graphile-realtime-subscriptions": "^1.11.0",
|
|
64
64
|
"graphile-search": "^2.14.0",
|
|
65
65
|
"graphile-upload-plugin": "^2.26.0",
|
|
@@ -92,5 +92,5 @@
|
|
|
92
92
|
"constructive",
|
|
93
93
|
"graphql"
|
|
94
94
|
],
|
|
95
|
-
"gitHead": "
|
|
95
|
+
"gitHead": "7e6cc68f5659d12ec852835a7d8fcca6b05a8f50"
|
|
96
96
|
}
|
|
@@ -149,16 +149,7 @@ function createConstructivePreset(options) {
|
|
|
149
149
|
}), (0, graphile_bucket_provisioner_plugin_1.BucketProvisionerPreset)({
|
|
150
150
|
connection: bucket_provisioner_resolver_1.getBucketProvisionerConnection,
|
|
151
151
|
allowedOrigins: (0, presigned_url_resolver_1.getAllowedOrigins)(),
|
|
152
|
-
|
|
153
|
-
// eager provisionBucket mutation mints the identical physical name
|
|
154
|
-
// (`{prefix}-{bucketKey}-{databaseId}`) instead of falling back to the
|
|
155
|
-
// bare logical bucket key.
|
|
156
|
-
resolveBucketName: (0, presigned_url_resolver_1.createProvisionerBucketNameResolver)(),
|
|
157
|
-
// S3 buckets are provisioned lazily (on first upload) or explicitly via
|
|
158
|
-
// the provisionBucket mutation. Disable the auto-provision-on-create
|
|
159
|
-
// hook so a createBucket GraphQL mutation records the row without
|
|
160
|
-
// eagerly minting an S3 bucket that may never receive an upload.
|
|
161
|
-
autoProvision: false
|
|
152
|
+
resolveBucketName: (0, presigned_url_resolver_1.createBucketNameResolver)()
|
|
162
153
|
}));
|
|
163
154
|
}
|
|
164
155
|
if (opts.enableAggregates) {
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Follows the same lazy-init pattern as upload-resolver.ts.
|
|
12
12
|
*/
|
|
13
|
-
import type { BucketNameResolver as ProvisionerBucketNameResolver } from 'graphile-bucket-provisioner-plugin';
|
|
14
13
|
import type { BucketNameResolver, EnsureBucketProvisioned, S3Config } from 'graphile-presigned-url-plugin';
|
|
15
14
|
/**
|
|
16
15
|
* Lazily initialize and return the S3Config for the presigned URL plugin.
|
|
@@ -36,16 +35,6 @@ export declare function getPresignedUrlS3Config(): S3Config;
|
|
|
36
35
|
* S3 buckets per logical bucket key.
|
|
37
36
|
*/
|
|
38
37
|
export declare function createBucketNameResolver(): BucketNameResolver;
|
|
39
|
-
/**
|
|
40
|
-
* Create the bucket name resolver for the bucket provisioner plugin
|
|
41
|
-
* (argument order: `(bucketKey, databaseId)`).
|
|
42
|
-
*
|
|
43
|
-
* Produces the exact same physical name as createBucketNameResolver()
|
|
44
|
-
* (`{prefix}-{bucketKey}-{digest}`) so the eager `provisionBucket`
|
|
45
|
-
* mutation mints the identical tenant-aware name that the lazy first-upload
|
|
46
|
-
* path would. Throws on a missing prefix — no default bucket name.
|
|
47
|
-
*/
|
|
48
|
-
export declare function createProvisionerBucketNameResolver(): ProvisionerBucketNameResolver;
|
|
49
38
|
/**
|
|
50
39
|
* Resolve CORS allowed origins from the env/config system.
|
|
51
40
|
*
|
|
@@ -14,14 +14,12 @@
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.getPresignedUrlS3Config = getPresignedUrlS3Config;
|
|
16
16
|
exports.createBucketNameResolver = createBucketNameResolver;
|
|
17
|
-
exports.createProvisionerBucketNameResolver = createProvisionerBucketNameResolver;
|
|
18
17
|
exports.getAllowedOrigins = getAllowedOrigins;
|
|
19
18
|
exports.createEnsureBucketProvisioned = createEnsureBucketProvisioned;
|
|
20
19
|
const bucket_provisioner_1 = require("@constructive-io/bucket-provisioner");
|
|
21
20
|
const graphql_env_1 = require("@constructive-io/graphql-env");
|
|
22
21
|
const s3_utils_1 = require("@constructive-io/s3-utils");
|
|
23
22
|
const logger_1 = require("@pgpmjs/logger");
|
|
24
|
-
const crypto_1 = require("crypto");
|
|
25
23
|
const bucket_provisioner_resolver_1 = require("./bucket-provisioner-resolver");
|
|
26
24
|
const log = new logger_1.Logger('presigned-url-resolver');
|
|
27
25
|
let s3Config = null;
|
|
@@ -87,59 +85,6 @@ function getBucketNamePrefix() {
|
|
|
87
85
|
}
|
|
88
86
|
return prefix;
|
|
89
87
|
}
|
|
90
|
-
/** S3's hard ceiling on a bucket name. */
|
|
91
|
-
const MAX_BUCKET_NAME_LENGTH = 63;
|
|
92
|
-
/** S3's floor, which a degenerate prefix/key could otherwise fall under. */
|
|
93
|
-
const MIN_BUCKET_NAME_LENGTH = 3;
|
|
94
|
-
/** Hex characters of the identity digest kept as the uniqueness tail. */
|
|
95
|
-
const IDENTITY_DIGEST_LENGTH = 12;
|
|
96
|
-
/** Readable budget: how much of the name the prefix and key may each occupy. */
|
|
97
|
-
const PREFIX_BUDGET = 20;
|
|
98
|
-
const BUCKET_KEY_BUDGET = 63 - IDENTITY_DIGEST_LENGTH - PREFIX_BUDGET - 3;
|
|
99
|
-
/**
|
|
100
|
-
* Reduce a component to the S3 bucket-name alphabet: lowercase, `[a-z0-9-]`,
|
|
101
|
-
* with runs of separators collapsed and no leading or trailing hyphen.
|
|
102
|
-
*
|
|
103
|
-
* Dots are legal in a bucket name but deliberately dropped — a dotted name
|
|
104
|
-
* cannot be used with virtual-hosted-style HTTPS, because the wildcard
|
|
105
|
-
* certificate does not match a further label.
|
|
106
|
-
*/
|
|
107
|
-
function sanitizeBucketNameComponent(value) {
|
|
108
|
-
return value
|
|
109
|
-
.toLowerCase()
|
|
110
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
111
|
-
.replace(/^-+|-+$/g, '');
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* The single physical-bucket naming policy:
|
|
115
|
-
* `{prefix}-{bucketKey}-{digest}` (e.g. `myapp-public-3f9c1a2b7e04`).
|
|
116
|
-
*
|
|
117
|
-
* Both the presigned-upload (lazy) path and the bucket-provisioner (eager) path
|
|
118
|
-
* derive names from this one function, so a bucket's physical name is identical
|
|
119
|
-
* regardless of which path mints it.
|
|
120
|
-
*
|
|
121
|
-
* The name is bounded and S3-legal by construction: the prefix and key are
|
|
122
|
-
* sanitized to `[a-z0-9-]` and truncated to a readable budget, and the tail is a
|
|
123
|
-
* digest of the *untruncated* identity — so two buckets whose keys agree only
|
|
124
|
-
* past the truncation point, or the same key in two databases, still get distinct
|
|
125
|
-
* names. Names remain stable for a given (prefix, databaseId, bucketKey) because
|
|
126
|
-
* nothing here reads the clock or a counter; and an already-provisioned bucket
|
|
127
|
-
* never consults this function at all, since `platform_buckets.physical_name` is
|
|
128
|
-
* authoritative once recorded.
|
|
129
|
-
*/
|
|
130
|
-
function mintPhysicalBucketName(prefix, databaseId, bucketKey) {
|
|
131
|
-
const identity = `${prefix}/${databaseId}/${bucketKey}`;
|
|
132
|
-
const digest = (0, crypto_1.createHash)('sha256').update(identity).digest('hex').slice(0, IDENTITY_DIGEST_LENGTH);
|
|
133
|
-
const safePrefix = sanitizeBucketNameComponent(prefix).slice(0, PREFIX_BUDGET).replace(/-+$/, '');
|
|
134
|
-
const safeKey = sanitizeBucketNameComponent(bucketKey).slice(0, BUCKET_KEY_BUDGET).replace(/-+$/, '');
|
|
135
|
-
const name = [safePrefix, safeKey, digest].filter((part) => part.length > 0).join('-');
|
|
136
|
-
// The digest alone already satisfies both bounds, so this is only reachable
|
|
137
|
-
// when both readable components sanitize away to nothing.
|
|
138
|
-
if (name.length < MIN_BUCKET_NAME_LENGTH || name.length > MAX_BUCKET_NAME_LENGTH) {
|
|
139
|
-
throw new Error(`[presigned-url-resolver] Cannot mint a legal S3 bucket name for key "${bucketKey}": got "${name}"`);
|
|
140
|
-
}
|
|
141
|
-
return name;
|
|
142
|
-
}
|
|
143
88
|
/**
|
|
144
89
|
* Create a per-(database, bucketKey) bucket name resolver for the presigned
|
|
145
90
|
* URL plugin (argument order: `(databaseId, bucketKey)`).
|
|
@@ -152,20 +97,7 @@ function mintPhysicalBucketName(prefix, databaseId, bucketKey) {
|
|
|
152
97
|
*/
|
|
153
98
|
function createBucketNameResolver() {
|
|
154
99
|
const prefix = getBucketNamePrefix();
|
|
155
|
-
return (databaseId, bucketKey) => mintPhysicalBucketName(prefix, databaseId, bucketKey);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Create the bucket name resolver for the bucket provisioner plugin
|
|
159
|
-
* (argument order: `(bucketKey, databaseId)`).
|
|
160
|
-
*
|
|
161
|
-
* Produces the exact same physical name as createBucketNameResolver()
|
|
162
|
-
* (`{prefix}-{bucketKey}-{digest}`) so the eager `provisionBucket`
|
|
163
|
-
* mutation mints the identical tenant-aware name that the lazy first-upload
|
|
164
|
-
* path would. Throws on a missing prefix — no default bucket name.
|
|
165
|
-
*/
|
|
166
|
-
function createProvisionerBucketNameResolver() {
|
|
167
|
-
const prefix = getBucketNamePrefix();
|
|
168
|
-
return (bucketKey, databaseId) => mintPhysicalBucketName(prefix, databaseId, bucketKey);
|
|
100
|
+
return (databaseId, bucketKey) => (0, bucket_provisioner_1.mintPhysicalBucketName)(prefix, databaseId, bucketKey);
|
|
169
101
|
}
|
|
170
102
|
/**
|
|
171
103
|
* Resolve CORS allowed origins from the env/config system.
|