|
16 | 16 | import type { GraphileConfig } from 'graphile-config'; |
17 | 17 | import { Logger } from '@pgpmjs/logger'; |
18 | 18 |
|
19 | | -import type { PresignedUrlPluginOptions, S3Config } from './types'; |
| 19 | +import type { PresignedUrlPluginOptions, S3Config, StorageModuleConfig } from './types'; |
20 | 20 | import { generatePresignedGetUrl } from './s3-signer'; |
21 | 21 | import { getStorageModuleConfig } from './storage-module-cache'; |
22 | 22 |
|
@@ -44,6 +44,32 @@ function resolveS3(options: PresignedUrlPluginOptions): S3Config { |
44 | 44 | return options.s3; |
45 | 45 | } |
46 | 46 |
|
| 47 | +/** |
| 48 | + * Build a per-database S3Config by overlaying storage_module overrides |
| 49 | + * onto the global S3Config. Same logic as plugin.ts resolveS3ForDatabase. |
| 50 | + */ |
| 51 | +function resolveS3ForDatabase( |
| 52 | + options: PresignedUrlPluginOptions, |
| 53 | + storageConfig: StorageModuleConfig, |
| 54 | + databaseId: string, |
| 55 | +): S3Config { |
| 56 | + const globalS3 = resolveS3(options); |
| 57 | + const bucket = options.resolveBucketName |
| 58 | + ? options.resolveBucketName(databaseId) |
| 59 | + : globalS3.bucket; |
| 60 | + const publicUrlPrefix = storageConfig.publicUrlPrefix ?? globalS3.publicUrlPrefix; |
| 61 | + |
| 62 | + if (bucket === globalS3.bucket && publicUrlPrefix === globalS3.publicUrlPrefix) { |
| 63 | + return globalS3; |
| 64 | + } |
| 65 | + |
| 66 | + return { |
| 67 | + ...globalS3, |
| 68 | + bucket, |
| 69 | + ...(publicUrlPrefix != null ? { publicUrlPrefix } : {}), |
| 70 | + }; |
| 71 | +} |
| 72 | + |
47 | 73 | export function createDownloadUrlPlugin( |
48 | 74 | options: PresignedUrlPluginOptions, |
49 | 75 | ): GraphileConfig.Plugin { |
@@ -100,39 +126,41 @@ export function createDownloadUrlPlugin( |
100 | 126 | return null; |
101 | 127 | } |
102 | 128 |
|
103 | | - const s3 = resolveS3(options); |
104 | | - |
105 | | - if (isPublic && s3.publicUrlPrefix) { |
106 | | - // Public file: return direct URL |
107 | | - return `${s3.publicUrlPrefix}/${key}`; |
108 | | - } |
109 | | - |
110 | | - // Resolve download URL expiry from storage module config (per-database) |
| 129 | + // Resolve per-database config (bucket, publicUrlPrefix, expiry) |
| 130 | + let s3ForDb = resolveS3(options); // fallback to global |
111 | 131 | let downloadUrlExpirySeconds = 3600; // fallback default |
112 | 132 | try { |
113 | 133 | const withPgClient = context.pgSettings |
114 | 134 | ? context.withPgClient |
115 | 135 | : null; |
116 | 136 | if (withPgClient) { |
117 | | - const config = await withPgClient(null, async (pgClient: any) => { |
| 137 | + const resolved = await withPgClient(null, async (pgClient: any) => { |
118 | 138 | const dbResult = await pgClient.query( |
119 | 139 | `SELECT jwt_private.current_database_id() AS id`, |
120 | 140 | ); |
121 | 141 | const databaseId = dbResult.rows[0]?.id; |
122 | 142 | if (!databaseId) return null; |
123 | | - return getStorageModuleConfig(pgClient, databaseId); |
| 143 | + const config = await getStorageModuleConfig(pgClient, databaseId); |
| 144 | + if (!config) return null; |
| 145 | + return { config, databaseId }; |
124 | 146 | }); |
125 | | - if (config) { |
126 | | - downloadUrlExpirySeconds = config.downloadUrlExpirySeconds; |
| 147 | + if (resolved) { |
| 148 | + downloadUrlExpirySeconds = resolved.config.downloadUrlExpirySeconds; |
| 149 | + s3ForDb = resolveS3ForDatabase(options, resolved.config, resolved.databaseId); |
127 | 150 | } |
128 | 151 | } |
129 | 152 | } catch { |
130 | | - // Fall back to default if config lookup fails |
| 153 | + // Fall back to global config if lookup fails |
| 154 | + } |
| 155 | + |
| 156 | + if (isPublic && s3ForDb.publicUrlPrefix) { |
| 157 | + // Public file: return direct CDN URL (per-database prefix) |
| 158 | + return `${s3ForDb.publicUrlPrefix}/${key}`; |
131 | 159 | } |
132 | 160 |
|
133 | | - // Private file: generate presigned GET URL |
| 161 | + // Private file: generate presigned GET URL (per-database bucket) |
134 | 162 | return generatePresignedGetUrl( |
135 | | - resolveS3(options), |
| 163 | + s3ForDb, |
136 | 164 | key, |
137 | 165 | downloadUrlExpirySeconds, |
138 | 166 | filename || undefined, |
|
0 commit comments