graphile-bucket-provisioner-plugin 1.14.2 → 1.15.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/README.md CHANGED
@@ -1,27 +1,12 @@
1
1
  # graphile-bucket-provisioner-plugin
2
2
 
3
- <p align="center" width="100%">
4
- <img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
5
- </p>
3
+ PostGraphile v5 plugin that exposes a `provisionBucket` mutation for manually
4
+ queueing storage reconciliation.
6
5
 
7
- <p align="center" width="100%">
8
- <a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
9
- <img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
10
- </a>
11
- <a href="https://github.com/constructive-io/constructive/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
12
- <a href="https://www.npmjs.com/package/graphile-bucket-provisioner-plugin"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=graphile%2Fgraphile-bucket-provisioner-plugin%2Fpackage.json"/></a>
13
- </p>
14
-
15
- PostGraphile v5 plugin that explicitly provisions S3-compatible buckets through a GraphQL mutation using [`@constructive-io/bucket-provisioner`](../packages/bucket-provisioner).
16
-
17
- ## Features
18
-
19
- - **Explicit `provisionBucket` mutation** — GraphQL mutation for manual/retry provisioning of any bucket
20
- - **3-tier CORS resolution** — Bucket-level `allowed_origins` → storage module-level `allowed_origins` → plugin config `allowedOrigins`
21
- - **Wildcard CORS** — Set `allowed_origins = ['*']` on a bucket for fully open CDN/public deployments
22
- - **Per-database overrides** — Reads `endpoint`, `provider`, `public_url_prefix`, and `allowed_origins` from the `storage_module` table for multi-tenant setups
23
- - **Lazy S3 config** — Connection config can be a function (evaluated once, cached) to avoid eager env-var reads at import time
24
- - **Deployment-controlled naming** — Requires a `resolveBucketName` policy for tenant-aware physical bucket names
6
+ The constructive-db `storage:provision_bucket` job is the only component that
7
+ mints physical S3 bucket names, provisions S3, and records `physical_name`.
8
+ This plugin resolves the logical bucket under RLS and enqueues that job; it
9
+ does not call S3 or derive a physical name.
25
10
 
26
11
  ## Installation
27
12
 
@@ -29,140 +14,46 @@ PostGraphile v5 plugin that explicitly provisions S3-compatible buckets through
29
14
  pnpm add graphile-bucket-provisioner-plugin
30
15
  ```
31
16
 
32
- ## Quick Start
33
-
34
- ```typescript
35
- import { createBucketProvisionerPlugin } from 'graphile-bucket-provisioner-plugin';
36
-
37
- const BucketProvisionerPlugin = createBucketProvisionerPlugin({
38
- connection: {
39
- provider: 'minio',
40
- region: 'us-east-1',
41
- endpoint: 'http://minio:9000',
42
- accessKeyId: process.env.S3_ACCESS_KEY_ID!,
43
- secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
44
- },
45
- allowedOrigins: ['https://app.example.com'],
46
- });
47
-
48
- // Add to your PostGraphile preset
49
- const preset: GraphileConfig.Preset = {
50
- plugins: [BucketProvisionerPlugin],
51
- };
52
- ```
53
-
54
- Or use the convenience preset:
17
+ ## Usage
55
18
 
56
19
  ```typescript
57
20
  import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
58
21
 
59
- const preset: GraphileConfig.Preset = {
60
- extends: [
61
- BucketProvisionerPreset({
62
- connection: () => ({
63
- provider: 'minio',
64
- region: 'us-east-1',
65
- endpoint: process.env.S3_ENDPOINT!,
66
- accessKeyId: process.env.S3_ACCESS_KEY_ID!,
67
- secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
68
- }),
69
- allowedOrigins: ['https://app.example.com'],
70
- }),
71
- ],
22
+ const preset = {
23
+ extends: [BucketProvisionerPreset()],
72
24
  };
73
25
  ```
74
26
 
75
- ## How It Works
76
-
77
- ### Auto-Provisioning (default)
78
-
79
- When a `createBucket` mutation runs on a table tagged with `@storageBuckets`:
80
-
81
- 1. The original resolver runs first (creates the DB row via RLS)
82
- 2. The plugin reads the bucket's `key` and `type` from the mutation input
83
- 3. It reads the `storage_module` config for per-database endpoint/provider overrides
84
- 4. It calls `BucketProvisioner.provision()` to create and configure the S3 bucket
85
- 5. If provisioning fails, the error is logged but the mutation result is returned normally
86
-
87
- ### Explicit Mutation
88
-
89
- The plugin also adds a `provisionBucket` mutation for manual provisioning or retrying failed provisions:
27
+ The mutation accepts a logical bucket key and optionally an owner entity ID:
90
28
 
91
29
  ```graphql
92
30
  mutation {
93
31
  provisionBucket(input: { bucketKey: "public" }) {
94
- success
95
- bucketName
96
- accessType
97
- provider
98
- endpoint
99
- error
32
+ bucketId
33
+ bucketKey
34
+ physicalName
35
+ jobId
100
36
  }
101
37
  }
102
38
  ```
103
39
 
104
- This mutation:
105
- 1. Reads the bucket row from the database (protected by RLS)
106
- 2. Reads the storage module config for the current database
107
- 3. Provisions the S3 bucket with the appropriate settings
108
- 4. Returns a success/error payload
109
-
110
- ## API
111
-
112
- ### `createBucketProvisionerPlugin(options)`
113
-
114
- Creates the plugin instance. Returns a `GraphileConfig.Plugin`.
115
-
116
- | Option | Type | Description |
117
- |--------|------|-------------|
118
- | `connection` | `StorageConnectionConfig \| () => StorageConnectionConfig` | S3 connection config (static or lazy getter) |
119
- | `allowedOrigins` | `string[]` | CORS allowed origins for bucket configuration |
120
- | `resolveBucketName` | `(databaseId, bucketKey) => string` | Deployment policy for deriving a tenant-aware physical bucket name |
121
- | `versioning` | `boolean?` | Enable S3 versioning on provisioned buckets (default: `false`) |
122
-
123
- ### `BucketProvisionerPreset(options)`
124
-
125
- Convenience function that wraps the plugin in a `GraphileConfig.Preset`.
126
-
127
- ### Connection Config
128
-
129
- ```typescript
130
- interface StorageConnectionConfig {
131
- provider: 's3' | 'minio' | 'r2' | 'gcs' | 'spaces';
132
- region: string;
133
- endpoint?: string;
134
- accessKeyId: string;
135
- secretAccessKey: string;
136
- }
137
- ```
138
-
139
- ### Smart Tag Detection
140
-
141
- The plugin detects tables tagged with `@storageBuckets` (set by the storage module generator in constructive-db):
142
-
143
- ```sql
144
- COMMENT ON TABLE app_public.buckets IS E'@storageBuckets\nStorage buckets table';
145
- ```
146
-
147
- The plugin wraps `create*` mutations for auto-provisioning and `update*` mutations for CORS change detection. Delete mutations are not wrapped.
148
-
149
- ## Error Handling
150
-
151
- The plugin is designed to never break mutations:
152
-
153
- - **Auto-provisioning errors** are caught and logged. The mutation result is returned normally. The admin can retry via the `provisionBucket` mutation.
154
- - **Explicit `provisionBucket` errors** return a structured payload with `success: false` and an `error` message.
155
- - **Validation errors** (`INVALID_BUCKET_KEY`, `DATABASE_NOT_FOUND`, `STORAGE_MODULE_NOT_PROVISIONED`, `BUCKET_NOT_FOUND`) are thrown as exceptions since they indicate configuration issues.
156
-
157
- ## Multi-Tenant Support
40
+ `provisionBucket` resolves the bucket row under RLS and enqueues the same
41
+ `storage:provision_bucket` job used by the database INSERT trigger. Database
42
+ scope jobs include `database_id`, use the `bucket:<uuid>` queue, and have
43
+ 25 attempts. Platform scope jobs use the platform trigger payload. Enqueue
44
+ failures are returned as GraphQL errors.
158
45
 
159
- The plugin reads per-database overrides from the `storage_module` table:
46
+ The payload fields are:
160
47
 
161
- - `endpoint` — Override the S3 endpoint for this database
162
- - `provider` — Override the storage provider for this database
163
- - `public_url_prefix` — CDN/public URL prefix for public buckets
48
+ | Field | Description |
49
+ |-------|-------------|
50
+ | `bucketId` | Logical bucket row ID |
51
+ | `bucketKey` | Logical bucket key |
52
+ | `physicalName` | Recorded physical name, or `null` while reconciliation is pending |
53
+ | `jobId` | ID of the queued reconciler job |
164
54
 
165
- This allows different tenants to use different storage backends while sharing the same plugin configuration.
55
+ There are no plugin options: S3 connection details, naming, CORS, versioning,
56
+ and physical-name recording belong to the reconciler.
166
57
 
167
58
  ---
168
59
 
package/esm/index.d.ts CHANGED
@@ -1,35 +1,9 @@
1
1
  /**
2
2
  * Bucket Provisioner Plugin for PostGraphile v5
3
3
  *
4
- * Provides an explicit `provisionBucket` mutation for PostGraphile v5.
5
- *
6
- * @example
7
- * ```typescript
8
- * import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
9
- * import { getEnvOptions } from '@constructive-io/graphql-env';
10
- *
11
- * // Use a lazy getter so env vars are read at runtime, not import time
12
- * function getConnection() {
13
- * const { cdn } = getEnvOptions();
14
- * return {
15
- * provider: cdn?.provider || 'minio',
16
- * region: cdn?.awsRegion || 'us-east-1',
17
- * endpoint: cdn?.endpoint || 'http://minio:9000',
18
- * accessKeyId: cdn?.awsAccessKey!,
19
- * secretAccessKey: cdn?.awsSecretKey!,
20
- * };
21
- * }
22
- *
23
- * const preset = {
24
- * extends: [
25
- * BucketProvisionerPreset({
26
- * connection: getConnection, // pass function ref, NOT getConnection()
27
- * allowedOrigins: ['https://app.example.com'],
28
- * }),
29
- * ],
30
- * };
31
- * ```
4
+ * Provides an explicit `provisionBucket` reconciliation enqueue mutation for
5
+ * PostGraphile v5.
32
6
  */
33
7
  export { BucketProvisionerPlugin, createBucketProvisionerPlugin } from './plugin';
34
8
  export { BucketProvisionerPreset } from './preset';
35
- export type { BucketAccessType, BucketNameResolver, BucketProvisionerPluginOptions, ConnectionConfigOrGetter, ProvisionBucketInput, ProvisionBucketPayload, ProvisionResult, StorageConnectionConfig, StorageProvider, } from './types';
9
+ export type { ProvisionBucketInput, ProvisionBucketPayload, } from './types';
package/esm/index.js CHANGED
@@ -1,34 +1,8 @@
1
1
  /**
2
2
  * Bucket Provisioner Plugin for PostGraphile v5
3
3
  *
4
- * Provides an explicit `provisionBucket` mutation for PostGraphile v5.
5
- *
6
- * @example
7
- * ```typescript
8
- * import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
9
- * import { getEnvOptions } from '@constructive-io/graphql-env';
10
- *
11
- * // Use a lazy getter so env vars are read at runtime, not import time
12
- * function getConnection() {
13
- * const { cdn } = getEnvOptions();
14
- * return {
15
- * provider: cdn?.provider || 'minio',
16
- * region: cdn?.awsRegion || 'us-east-1',
17
- * endpoint: cdn?.endpoint || 'http://minio:9000',
18
- * accessKeyId: cdn?.awsAccessKey!,
19
- * secretAccessKey: cdn?.awsSecretKey!,
20
- * };
21
- * }
22
- *
23
- * const preset = {
24
- * extends: [
25
- * BucketProvisionerPreset({
26
- * connection: getConnection, // pass function ref, NOT getConnection()
27
- * allowedOrigins: ['https://app.example.com'],
28
- * }),
29
- * ],
30
- * };
31
- * ```
4
+ * Provides an explicit `provisionBucket` reconciliation enqueue mutation for
5
+ * PostGraphile v5.
32
6
  */
33
7
  export { BucketProvisionerPlugin, createBucketProvisionerPlugin } from './plugin';
34
8
  export { BucketProvisionerPreset } from './preset';
package/esm/plugin.d.ts CHANGED
@@ -3,13 +3,9 @@
3
3
  *
4
4
  * Adds S3 bucket provisioning support to PostGraphile v5:
5
5
  *
6
- * 1. `provisionBucket` mutation — explicitly provision an S3 bucket for a
6
+ * 1. `provisionBucket` mutation — explicitly enqueue reconciliation for a
7
7
  * logical bucket row in the database. Reads the bucket config via RLS,
8
- * then calls BucketProvisioner to create and configure the S3 bucket.
9
- *
10
- * This plugin uses `@constructive-io/bucket-provisioner` for the actual
11
- * S3 operations (bucket creation, Block Public Access, CORS, policies,
12
- * versioning, lifecycle rules).
8
+ * then queues the same storage reconciler job used by the INSERT trigger.
13
9
  *
14
10
  * Detection: Uses the `@storageBuckets` smart tag on the codec (table).
15
11
  * The storage module generator in constructive-db sets this tag on the
@@ -17,18 +13,16 @@
17
13
  * COMMENT ON TABLE buckets IS E'@storageBuckets\nStorage buckets table';
18
14
  */
19
15
  import type { GraphileConfig } from 'graphile-config';
20
- import type { BucketProvisionerPluginOptions } from './types';
21
16
  /**
22
17
  * Creates the bucket provisioner plugin.
23
18
  *
24
- * This plugin provides one provisioning pathway:
19
+ * This plugin provides one reconciliation pathway:
25
20
  *
26
21
  * 1. **Explicit `provisionBucket` mutation** — Call this mutation with a
27
- * bucket key to provision (or re-provision) the S3 bucket. Protected
28
- * by RLS on the buckets table.
22
+ * bucket key to enqueue reconciliation (or re-reconciliation) for the
23
+ * bucket. Protected by RLS on the buckets table.
29
24
  *
30
- * @param options - Plugin configuration (S3 credentials, CORS origins, naming)
31
25
  */
32
- export declare function createBucketProvisionerPlugin(options: BucketProvisionerPluginOptions): GraphileConfig.Plugin;
26
+ export declare function createBucketProvisionerPlugin(): GraphileConfig.Plugin;
33
27
  export declare const BucketProvisionerPlugin: typeof createBucketProvisionerPlugin;
34
28
  export default BucketProvisionerPlugin;