@zintrust/cloudflare-containers-proxy 0.1.43 → 0.1.51

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/dist/index.d.ts CHANGED
@@ -16,6 +16,25 @@ export type ZintrustContainersProxyEnv = {
16
16
  APP_NAME?: string;
17
17
  APP_KEY?: string;
18
18
  CSRF_SKIP_PATHS?: string;
19
+ HOST?: string;
20
+ DB_HOST?: string;
21
+ DB_PORT?: string;
22
+ DB_DATABASE?: string;
23
+ DB_USERNAME?: string;
24
+ DB_PASSWORD?: string;
25
+ DB_PORT_POSTGRESQL?: string;
26
+ DB_DATABASE_POSTGRESQL?: string;
27
+ DB_USERNAME_POSTGRESQL?: string;
28
+ DB_PASSWORD_POSTGRESQL?: string;
29
+ DB_HOST_MSSQL?: string;
30
+ DB_PORT_MSSQL?: string;
31
+ DB_DATABASE_MSSQL?: string;
32
+ DB_USERNAME_MSSQL?: string;
33
+ DB_PASSWORD_MSSQL?: string;
34
+ REDIS_HOST?: string;
35
+ REDIS_PORT?: string;
36
+ REDIS_PASSWORD?: string;
37
+ REDIS_DB?: string;
19
38
  ZT_ZIN_BIN?: string;
20
39
  MYSQL_PROXY_KEY_ID?: string;
21
40
  MYSQL_PROXY_SECRET?: string;
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Container } from '@cloudflare/containers';
2
+ import { ErrorFactory, Logger } from '@zintrust/core';
2
3
  const getContainerEnv = (container) => {
3
4
  return container.env;
4
5
  };
@@ -29,6 +30,13 @@ const createProxyEntrypoint = (env, command, port) => {
29
30
  return ['node', resolveZinBin(env), command, '--host', '0.0.0.0', '--port', String(port)];
30
31
  };
31
32
  const createMySqlProxyEnvVars = (env) => {
33
+ const dbHost = toStringEnv(env.MYSQL_DB_HOST, '') ||
34
+ toStringEnv(env.DB_HOST, '') ||
35
+ toStringEnv(env.HOST, 'host.docker.internal');
36
+ const dbPort = toStringEnv(env.MYSQL_DB_PORT, '') || toStringEnv(env.DB_PORT, '3306');
37
+ const dbName = toStringEnv(env.MYSQL_DB_DATABASE, '') || toStringEnv(env.DB_DATABASE, 'zintrust');
38
+ const dbUser = toStringEnv(env.MYSQL_DB_USERNAME, '') || toStringEnv(env.DB_USERNAME, 'root');
39
+ const dbPass = toStringEnv(env.MYSQL_DB_PASSWORD, '') || toStringEnv(env.DB_PASSWORD, '');
32
40
  return {
33
41
  ...createCommonProxyEnvVars(env),
34
42
  MYSQL_PROXY_REQUIRE_SIGNING: 'true',
@@ -36,19 +44,28 @@ const createMySqlProxyEnvVars = (env) => {
36
44
  MYSQL_PROXY_SECRET: toStringEnv(env.MYSQL_PROXY_SECRET, ''),
37
45
  MYSQL_PROXY_SIGNING_WINDOW_MS: toStringEnv(env.MYSQL_PROXY_SIGNING_WINDOW_MS, '60000'),
38
46
  MYSQL_PROXY_POOL_LIMIT: toStringEnv(env.MYSQL_PROXY_POOL_LIMIT, '100'),
39
- MYSQL_DB_HOST: toStringEnv(env.MYSQL_DB_HOST, ''),
40
- MYSQL_DB_PORT: toStringEnv(env.MYSQL_DB_PORT, '3306'),
41
- MYSQL_DB_DATABASE: toStringEnv(env.MYSQL_DB_DATABASE, 'zintrust'),
42
- MYSQL_DB_USERNAME: toStringEnv(env.MYSQL_DB_USERNAME, 'zintrust'),
43
- MYSQL_DB_PASSWORD: toStringEnv(env.MYSQL_DB_PASSWORD, ''),
44
- DB_HOST: toStringEnv(env.MYSQL_DB_HOST, ''),
45
- DB_PORT: toStringEnv(env.MYSQL_DB_PORT, '3306'),
46
- DB_DATABASE: toStringEnv(env.MYSQL_DB_DATABASE, 'zintrust'),
47
- DB_USERNAME: toStringEnv(env.MYSQL_DB_USERNAME, 'zintrust'),
48
- DB_PASSWORD: toStringEnv(env.MYSQL_DB_PASSWORD, ''),
47
+ MYSQL_DB_HOST: dbHost,
48
+ MYSQL_DB_PORT: dbPort,
49
+ MYSQL_DB_DATABASE: dbName,
50
+ MYSQL_DB_USERNAME: dbUser,
51
+ MYSQL_DB_PASSWORD: dbPass,
52
+ DB_HOST: dbHost,
53
+ DB_PORT: dbPort,
54
+ DB_DATABASE: dbName,
55
+ DB_USERNAME: dbUser,
56
+ DB_PASSWORD: dbPass,
49
57
  };
50
58
  };
51
59
  const createPostgresProxyEnvVars = (env) => {
60
+ const dbHost = toStringEnv(env.POSTGRES_DB_HOST, '') ||
61
+ toStringEnv(env.DB_HOST, '') ||
62
+ toStringEnv(env.HOST, 'host.docker.internal');
63
+ const dbPort = toStringEnv(env.POSTGRES_DB_PORT, '') || toStringEnv(env.DB_PORT_POSTGRESQL, '5432');
64
+ const dbName = toStringEnv(env.POSTGRES_DB_DATABASE, '') ||
65
+ toStringEnv(env.DB_DATABASE_POSTGRESQL, 'postgres');
66
+ const dbUser = toStringEnv(env.POSTGRES_DB_USERNAME, '') ||
67
+ toStringEnv(env.DB_USERNAME_POSTGRESQL, 'postgres');
68
+ const dbPass = toStringEnv(env.POSTGRES_DB_PASSWORD, '') || toStringEnv(env.DB_PASSWORD_POSTGRESQL, '');
52
69
  return {
53
70
  ...createCommonProxyEnvVars(env),
54
71
  POSTGRES_PROXY_REQUIRE_SIGNING: 'true',
@@ -56,28 +73,34 @@ const createPostgresProxyEnvVars = (env) => {
56
73
  POSTGRES_PROXY_SECRET: toStringEnv(env.POSTGRES_PROXY_SECRET, ''),
57
74
  POSTGRES_PROXY_SIGNING_WINDOW_MS: toStringEnv(env.POSTGRES_PROXY_SIGNING_WINDOW_MS, '60000'),
58
75
  POSTGRES_PROXY_POOL_LIMIT: toStringEnv(env.POSTGRES_PROXY_POOL_LIMIT, '100'),
59
- DB_HOST: toStringEnv(env.POSTGRES_DB_HOST, ''),
60
- DB_PORT_POSTGRESQL: toStringEnv(env.POSTGRES_DB_PORT, '5432'),
61
- DB_DATABASE_POSTGRESQL: toStringEnv(env.POSTGRES_DB_DATABASE, 'postgres'),
62
- DB_USERNAME_POSTGRESQL: toStringEnv(env.POSTGRES_DB_USERNAME, 'postgres'),
63
- DB_PASSWORD_POSTGRESQL: toStringEnv(env.POSTGRES_DB_PASSWORD, ''),
76
+ DB_HOST: dbHost,
77
+ DB_PORT_POSTGRESQL: dbPort,
78
+ DB_DATABASE_POSTGRESQL: dbName,
79
+ DB_USERNAME_POSTGRESQL: dbUser,
80
+ DB_PASSWORD_POSTGRESQL: dbPass,
64
81
  };
65
82
  };
66
83
  const createRedisProxyEnvVars = (env) => {
84
+ const targetHost = toStringEnv(env.REDIS_PROXY_TARGET_HOST, '') ||
85
+ toStringEnv(env.REDIS_HOST, '') ||
86
+ toStringEnv(env.HOST, 'host.docker.internal');
87
+ const targetPort = toStringEnv(env.REDIS_PROXY_TARGET_PORT, '') || toStringEnv(env.REDIS_PORT, '6379');
88
+ const targetPassword = toStringEnv(env.REDIS_PROXY_TARGET_PASSWORD, '') || toStringEnv(env.REDIS_PASSWORD, '');
89
+ const targetDb = toStringEnv(env.REDIS_PROXY_TARGET_DB, '') || toStringEnv(env.REDIS_DB, '0');
67
90
  return {
68
91
  ...createCommonProxyEnvVars(env),
69
92
  REDIS_PROXY_REQUIRE_SIGNING: 'true',
70
93
  REDIS_PROXY_KEY_ID: toStringEnv(env.REDIS_PROXY_KEY_ID, ''),
71
94
  REDIS_PROXY_SECRET: toStringEnv(env.REDIS_PROXY_SECRET, ''),
72
95
  REDIS_PROXY_SIGNING_WINDOW_MS: toStringEnv(env.REDIS_PROXY_SIGNING_WINDOW_MS, '60000'),
73
- REDIS_PROXY_TARGET_HOST: toStringEnv(env.REDIS_PROXY_TARGET_HOST, ''),
74
- REDIS_PROXY_TARGET_PORT: toStringEnv(env.REDIS_PROXY_TARGET_PORT, '6379'),
75
- REDIS_PROXY_TARGET_PASSWORD: toStringEnv(env.REDIS_PROXY_TARGET_PASSWORD, ''),
76
- REDIS_PROXY_TARGET_DB: toStringEnv(env.REDIS_PROXY_TARGET_DB, '0'),
77
- REDIS_HOST: toStringEnv(env.REDIS_PROXY_TARGET_HOST, ''),
78
- REDIS_PORT: toStringEnv(env.REDIS_PROXY_TARGET_PORT, '6379'),
79
- REDIS_PASSWORD: toStringEnv(env.REDIS_PROXY_TARGET_PASSWORD, ''),
80
- REDIS_DB: toStringEnv(env.REDIS_PROXY_TARGET_DB, '0'),
96
+ REDIS_PROXY_TARGET_HOST: targetHost,
97
+ REDIS_PROXY_TARGET_PORT: targetPort,
98
+ REDIS_PROXY_TARGET_PASSWORD: targetPassword,
99
+ REDIS_PROXY_TARGET_DB: targetDb,
100
+ REDIS_HOST: targetHost,
101
+ REDIS_PORT: targetPort,
102
+ REDIS_PASSWORD: targetPassword,
103
+ REDIS_DB: targetDb,
81
104
  };
82
105
  };
83
106
  const createMongoDbProxyEnvVars = (env) => {
@@ -87,11 +110,19 @@ const createMongoDbProxyEnvVars = (env) => {
87
110
  MONGODB_PROXY_KEY_ID: toStringEnv(env.MONGODB_PROXY_KEY_ID, ''),
88
111
  MONGODB_PROXY_SECRET: toStringEnv(env.MONGODB_PROXY_SECRET, ''),
89
112
  MONGODB_PROXY_SIGNING_WINDOW_MS: toStringEnv(env.MONGODB_PROXY_SIGNING_WINDOW_MS, '60000'),
90
- MONGO_URI: toStringEnv(env.MONGODB_PROXY_TARGET_URI, ''),
113
+ MONGO_URI: toStringEnv(env.MONGODB_PROXY_TARGET_URI, 'mongodb://host.docker.internal:27017'),
91
114
  MONGO_DB: toStringEnv(env.MONGODB_PROXY_TARGET_DB, 'zintrust'),
92
115
  };
93
116
  };
94
117
  const createSqlServerProxyEnvVars = (env) => {
118
+ const dbHost = toStringEnv(env.SQLSERVER_DB_HOST, '') ||
119
+ toStringEnv(env.DB_HOST_MSSQL, '') ||
120
+ toStringEnv(env.DB_HOST, '') ||
121
+ toStringEnv(env.HOST, 'host.docker.internal');
122
+ const dbPort = toStringEnv(env.SQLSERVER_DB_PORT, '') || toStringEnv(env.DB_PORT_MSSQL, '1433');
123
+ const dbName = toStringEnv(env.SQLSERVER_DB_DATABASE, '') || toStringEnv(env.DB_DATABASE_MSSQL, 'zintrust');
124
+ const dbUser = toStringEnv(env.SQLSERVER_DB_USERNAME, '') || toStringEnv(env.DB_USERNAME_MSSQL, 'sa');
125
+ const dbPass = toStringEnv(env.SQLSERVER_DB_PASSWORD, '') || toStringEnv(env.DB_PASSWORD_MSSQL, '');
95
126
  return {
96
127
  ...createCommonProxyEnvVars(env),
97
128
  SQLSERVER_PROXY_REQUIRE_SIGNING: 'true',
@@ -99,11 +130,11 @@ const createSqlServerProxyEnvVars = (env) => {
99
130
  SQLSERVER_PROXY_SECRET: toStringEnv(env.SQLSERVER_PROXY_SECRET, ''),
100
131
  SQLSERVER_PROXY_SIGNING_WINDOW_MS: toStringEnv(env.SQLSERVER_PROXY_SIGNING_WINDOW_MS, '60000'),
101
132
  SQLSERVER_PROXY_POOL_LIMIT: toStringEnv(env.SQLSERVER_PROXY_POOL_LIMIT, '100'),
102
- DB_HOST_MSSQL: toStringEnv(env.SQLSERVER_DB_HOST, ''),
103
- DB_PORT_MSSQL: toStringEnv(env.SQLSERVER_DB_PORT, '1433'),
104
- DB_DATABASE_MSSQL: toStringEnv(env.SQLSERVER_DB_DATABASE, 'zintrust'),
105
- DB_USERNAME_MSSQL: toStringEnv(env.SQLSERVER_DB_USERNAME, 'sa'),
106
- DB_PASSWORD_MSSQL: toStringEnv(env.SQLSERVER_DB_PASSWORD, ''),
133
+ DB_HOST_MSSQL: dbHost,
134
+ DB_PORT_MSSQL: dbPort,
135
+ DB_DATABASE_MSSQL: dbName,
136
+ DB_USERNAME_MSSQL: dbUser,
137
+ DB_PASSWORD_MSSQL: dbPass,
107
138
  };
108
139
  };
109
140
  const createSmtpProxyEnvVars = (env) => {
@@ -140,7 +171,9 @@ const ensureContainerStarted = async (container, port, start) => {
140
171
  export class ZintrustMySqlProxyContainer extends Container {
141
172
  defaultPort = 8789;
142
173
  sleepAfter = '10m';
143
- pingEndpoint = '/health';
174
+ // Keep this lightweight: the proxy root path responds quickly (401 without
175
+ // signing headers) and does not depend on DB connectivity like /health.
176
+ pingEndpoint = 'containerstarthealthcheck';
144
177
  async fetch(request) {
145
178
  const env = getContainerEnv(this);
146
179
  await ensureContainerStarted(this, 8789, {
@@ -153,7 +186,7 @@ export class ZintrustMySqlProxyContainer extends Container {
153
186
  export class ZintrustPostgresProxyContainer extends Container {
154
187
  defaultPort = 8790;
155
188
  sleepAfter = '10m';
156
- pingEndpoint = '/health';
189
+ pingEndpoint = 'containerstarthealthcheck';
157
190
  async fetch(request) {
158
191
  const env = getContainerEnv(this);
159
192
  await ensureContainerStarted(this, 8790, {
@@ -166,7 +199,7 @@ export class ZintrustPostgresProxyContainer extends Container {
166
199
  export class ZintrustRedisProxyContainer extends Container {
167
200
  defaultPort = 8791;
168
201
  sleepAfter = '10m';
169
- pingEndpoint = '/health';
202
+ pingEndpoint = 'containerstarthealthcheck';
170
203
  async fetch(request) {
171
204
  const env = getContainerEnv(this);
172
205
  await ensureContainerStarted(this, 8791, {
@@ -179,7 +212,7 @@ export class ZintrustRedisProxyContainer extends Container {
179
212
  export class ZintrustMongoDbProxyContainer extends Container {
180
213
  defaultPort = 8792;
181
214
  sleepAfter = '10m';
182
- pingEndpoint = '/health';
215
+ pingEndpoint = 'containerstarthealthcheck';
183
216
  async fetch(request) {
184
217
  const env = getContainerEnv(this);
185
218
  await ensureContainerStarted(this, 8792, {
@@ -192,7 +225,7 @@ export class ZintrustMongoDbProxyContainer extends Container {
192
225
  export class ZintrustSqlServerProxyContainer extends Container {
193
226
  defaultPort = 8793;
194
227
  sleepAfter = '10m';
195
- pingEndpoint = '/health';
228
+ pingEndpoint = 'containerstarthealthcheck';
196
229
  async fetch(request) {
197
230
  const env = getContainerEnv(this);
198
231
  await ensureContainerStarted(this, 8793, {
@@ -205,7 +238,7 @@ export class ZintrustSqlServerProxyContainer extends Container {
205
238
  export class ZintrustSmtpProxyContainer extends Container {
206
239
  defaultPort = 8794;
207
240
  sleepAfter = '10m';
208
- pingEndpoint = '/health';
241
+ pingEndpoint = 'containerstarthealthcheck';
209
242
  async fetch(request) {
210
243
  const env = getContainerEnv(this);
211
244
  await ensureContainerStarted(this, 8794, {
@@ -238,6 +271,79 @@ const rewritePrefix = (request, prefix) => {
238
271
  return new Request(url.toString(), request);
239
272
  };
240
273
  const CONTAINER_INSTANCE_NAME = 'cf-singleton-container';
274
+ const CONTAINER_RETRY_ATTEMPTS = 20;
275
+ const CONTAINER_RETRY_DELAY_MS = 500;
276
+ const isContainerNotReadyMessage = (value) => {
277
+ return (value.includes('Monitor failed to find container') ||
278
+ value.includes('container port not found') ||
279
+ value.includes('Connection refused'));
280
+ };
281
+ const responseIndicatesContainerNotReady = async (response) => {
282
+ if (!response)
283
+ return false;
284
+ if (response.status !== 500)
285
+ return false;
286
+ try {
287
+ const text = await response.clone().text();
288
+ return isContainerNotReadyMessage(text);
289
+ }
290
+ catch {
291
+ return false;
292
+ }
293
+ };
294
+ const errorIndicatesContainerNotReady = (error) => {
295
+ const msg = error instanceof Error ? error.message : String(error);
296
+ return isContainerNotReadyMessage(msg);
297
+ };
298
+ const sleepMs = async (ms) => {
299
+ if (typeof AbortSignal === 'undefined' || typeof AbortSignal.timeout !== 'function') {
300
+ throw ErrorFactory.createValidationError('Container retry sleep requires AbortSignal.timeout() support in this runtime');
301
+ }
302
+ const signal = AbortSignal.timeout(ms);
303
+ await new Promise((resolve) => {
304
+ if (signal.aborted) {
305
+ resolve();
306
+ return;
307
+ }
308
+ signal.addEventListener('abort', () => resolve(), { once: true });
309
+ });
310
+ };
311
+ const createContainerNotReadyResponse = (_message) => {
312
+ return createJson({
313
+ error: 'container_not_ready',
314
+ service: 'zintrust-containers-proxy',
315
+ message: 'Container is starting. Retry shortly.',
316
+ }, { status: 503 });
317
+ };
318
+ const fetchWithContainerRetry = async (stub, request, attempt = 1) => {
319
+ try {
320
+ const requestClone = request.clone();
321
+ const response = await stub.fetch(requestClone);
322
+ const notReady = await responseIndicatesContainerNotReady(response);
323
+ if (!notReady)
324
+ return response;
325
+ if (attempt >= CONTAINER_RETRY_ATTEMPTS) {
326
+ return createContainerNotReadyResponse('Container monitor not ready (max retries reached).');
327
+ }
328
+ Logger.warn('Container not ready; retrying', { attempt, max: CONTAINER_RETRY_ATTEMPTS });
329
+ await sleepMs(CONTAINER_RETRY_DELAY_MS);
330
+ return fetchWithContainerRetry(stub, request, attempt + 1);
331
+ }
332
+ catch (error) {
333
+ if (!errorIndicatesContainerNotReady(error))
334
+ throw error;
335
+ if (attempt >= CONTAINER_RETRY_ATTEMPTS) {
336
+ return createContainerNotReadyResponse(String(error));
337
+ }
338
+ Logger.warn('Container connection error; retrying', {
339
+ attempt,
340
+ max: CONTAINER_RETRY_ATTEMPTS,
341
+ error: String(error),
342
+ });
343
+ await sleepMs(CONTAINER_RETRY_DELAY_MS);
344
+ return fetchWithContainerRetry(stub, request, attempt + 1);
345
+ }
346
+ };
241
347
  const ROUTES = Object.freeze({
242
348
  mysql: { binding: 'ZT_PROXY_MYSQL', prefix: '/mysql' },
243
349
  postgres: { binding: 'ZT_PROXY_POSTGRES', prefix: '/postgres' },
@@ -266,7 +372,16 @@ export default {
266
372
  return createJson({ error: 'not_found', message: 'Unknown proxy route' }, { status: 404 });
267
373
  }
268
374
  const namespace = env[def.binding];
375
+ if (!namespace || typeof namespace.getByName !== 'function') {
376
+ return createJson({
377
+ error: 'missing_binding',
378
+ service: 'zintrust-containers-proxy',
379
+ binding: def.binding,
380
+ message: 'Durable Object binding is missing. Ensure your Wrangler config defines durable_objects bindings for ZT_PROXY_* (and containers/migrations) for the selected --env.',
381
+ }, { status: 500 });
382
+ }
269
383
  const stub = namespace.getByName(CONTAINER_INSTANCE_NAME);
270
- return stub.fetch(rewritePrefix(request, def.prefix));
384
+ const nextRequest = rewritePrefix(request, def.prefix);
385
+ return fetchWithContainerRetry(stub, nextRequest);
271
386
  },
272
387
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/cloudflare-containers-proxy",
3
- "version": "0.1.43",
3
+ "version": "0.1.51",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -19,7 +19,7 @@
19
19
  "access": "public"
20
20
  },
21
21
  "peerDependencies": {
22
- "@zintrust/core": "^0.1.43"
22
+ "@zintrust/core": "^0.1.51"
23
23
  },
24
24
  "dependencies": {
25
25
  "@cloudflare/containers": "^0.1.0",
@@ -1,36 +0,0 @@
1
- {
2
- "name": "@zintrust/cloudflare-containers-proxy",
3
- "version": "0.1.43",
4
- "buildDate": "2026-02-19T18:46:20.595Z",
5
- "buildEnvironment": {
6
- "node": "v20.20.0",
7
- "platform": "linux",
8
- "arch": "x64"
9
- },
10
- "git": {
11
- "commit": "d42fc0e",
12
- "branch": "master"
13
- },
14
- "package": {
15
- "engines": {
16
- "node": ">=20.0.0"
17
- },
18
- "dependencies": [
19
- "@cloudflare/containers",
20
- "@cloudflare/workers-types"
21
- ],
22
- "peerDependencies": [
23
- "@zintrust/core"
24
- ]
25
- },
26
- "files": {
27
- "index.d.ts": {
28
- "size": 3701,
29
- "sha256": "0f7335c84e3f52ee46fa680b635f383e607e64af93338701934105234be3ce18"
30
- },
31
- "index.js": {
32
- "size": 12198,
33
- "sha256": "00696df76d78232e337eb112ff58b791daf52ecd8d01b62b25020691cd79d772"
34
- }
35
- }
36
- }