@nicnocquee/dataqueue 1.21.0 → 1.22.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/src/db-util.ts CHANGED
@@ -1,31 +1,102 @@
1
1
  import { Pool } from 'pg';
2
2
  import { JobQueueConfig } from './types.js';
3
3
  import { parse } from 'pg-connection-string';
4
+ import fs from 'fs';
4
5
 
5
- // Create a database connection pool
6
+ /**
7
+ * Helper to load a PEM string or file. Only values starting with 'file://' are loaded from file.
8
+ */
9
+ function loadPemOrFile(value?: string): string | undefined {
10
+ if (!value) return undefined;
11
+ if (value.startsWith('file://')) {
12
+ const filePath = value.slice(7);
13
+ return fs.readFileSync(filePath, 'utf8');
14
+ }
15
+ return value;
16
+ }
17
+
18
+ /**
19
+ * Create a database connection pool with flexible SSL certificate loading.
20
+ *
21
+ * SSL config example (for local file paths):
22
+ * ssl: {
23
+ * ca: process.env.PGSSLROOTCERT, // PEM string or 'file://...'
24
+ * cert: process.env.PGSSLCERT, // optional, PEM string or 'file://...'
25
+ * key: process.env.PGSSLKEY, // optional, PEM string or 'file://...'
26
+ * rejectUnauthorized: true
27
+ * }
28
+ */
6
29
  export const createPool = (config: JobQueueConfig['databaseConfig']): Pool => {
7
30
  let searchPath: string | undefined;
31
+ let ssl: any = undefined;
32
+ let customCA: string | undefined;
33
+ let sslmode: string | undefined;
34
+
8
35
  if (config.connectionString) {
9
- // Parse the connection string to extract search_path from query params
10
36
  try {
11
37
  const url = new URL(config.connectionString);
12
38
  searchPath = url.searchParams.get('search_path') || undefined;
39
+ sslmode = url.searchParams.get('sslmode') || undefined;
40
+ if (sslmode === 'no-verify') {
41
+ ssl = { rejectUnauthorized: false };
42
+ }
13
43
  } catch (e) {
14
- // fallback: try pg-connection-string parse (for non-standard URLs)
15
44
  const parsed = parse(config.connectionString);
16
45
  if (parsed.options) {
17
- // options might look like '-c search_path=myschema'
18
46
  const match = parsed.options.match(/search_path=([^\s]+)/);
19
47
  if (match) {
20
48
  searchPath = match[1];
21
49
  }
22
50
  }
51
+ sslmode = typeof parsed.sslmode === 'string' ? parsed.sslmode : undefined;
52
+ if (sslmode === 'no-verify') {
53
+ ssl = { rejectUnauthorized: false };
54
+ }
23
55
  }
24
56
  }
25
57
 
26
- const pool = new Pool(config);
58
+ // Flexible SSL loading: only support file:// for file loading
59
+ if (config.ssl) {
60
+ if (typeof config.ssl.ca === 'string') {
61
+ customCA = config.ssl.ca;
62
+ } else if (typeof process.env.PGSSLROOTCERT === 'string') {
63
+ customCA = process.env.PGSSLROOTCERT;
64
+ } else {
65
+ customCA = undefined;
66
+ }
67
+ const caValue =
68
+ typeof customCA === 'string' ? loadPemOrFile(customCA) : undefined;
69
+ ssl = {
70
+ ...ssl,
71
+ ...(caValue ? { ca: caValue } : {}),
72
+ cert: loadPemOrFile(
73
+ typeof config.ssl.cert === 'string'
74
+ ? config.ssl.cert
75
+ : process.env.PGSSLCERT,
76
+ ),
77
+ key: loadPemOrFile(
78
+ typeof config.ssl.key === 'string'
79
+ ? config.ssl.key
80
+ : process.env.PGSSLKEY,
81
+ ),
82
+ rejectUnauthorized:
83
+ config.ssl.rejectUnauthorized !== undefined
84
+ ? config.ssl.rejectUnauthorized
85
+ : true,
86
+ };
87
+ }
88
+
89
+ // Warn if both sslmode (any value) and a custom CA are set
90
+ if (sslmode && customCA) {
91
+ const warning = `\n\n\x1b[33m**************************************************\n\u26A0\uFE0F WARNING: SSL CONFIGURATION ISSUE\n**************************************************\nBoth sslmode ('${sslmode}') is set in the connection string\nand a custom CA is provided (via config.ssl.ca or PGSSLROOTCERT).\nThis combination may cause connection failures or unexpected behavior.\n\nRecommended: Remove sslmode from the connection string when using a custom CA.\n**************************************************\x1b[0m\n`;
92
+ console.warn(warning);
93
+ }
94
+
95
+ const pool = new Pool({
96
+ ...config,
97
+ ...(ssl ? { ssl } : {}),
98
+ });
27
99
 
28
- // If search_path is specified, set it for every new connection
29
100
  if (searchPath) {
30
101
  pool.on('connect', (client) => {
31
102
  client.query(`SET search_path TO ${searchPath}`);
package/src/types.ts CHANGED
@@ -164,6 +164,25 @@ export interface Processor {
164
164
  start: () => Promise<number>;
165
165
  }
166
166
 
167
+ export interface DatabaseSSLConfig {
168
+ /**
169
+ * CA certificate as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.
170
+ */
171
+ ca?: string;
172
+ /**
173
+ * Client certificate as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.
174
+ */
175
+ cert?: string;
176
+ /**
177
+ * Client private key as PEM string or file path. If the value starts with 'file://', it will be loaded from file, otherwise treated as PEM string.
178
+ */
179
+ key?: string;
180
+ /**
181
+ * Whether to reject unauthorized certificates (default: true)
182
+ */
183
+ rejectUnauthorized?: boolean;
184
+ }
185
+
167
186
  export interface JobQueueConfig {
168
187
  databaseConfig: {
169
188
  connectionString?: string;
@@ -172,7 +191,7 @@ export interface JobQueueConfig {
172
191
  database?: string;
173
192
  user?: string;
174
193
  password?: string;
175
- ssl?: any;
194
+ ssl?: DatabaseSSLConfig;
176
195
  };
177
196
  verbose?: boolean;
178
197
  }