diff --git a/README.md b/README.md
index c0aa514..6d62459 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,10 @@ The GrowthBook Proxy repository is a mono-repo containing the following packages
### What's new
+**Version 1.1.2**
+- Fix max payload size bug
+- Deprecate `CLUSTER_ROOT_NODES` in favor of `CLUSTER_ROOT_NODES_JSON`
+
**Version 1.1.1**
- Multi organization support
- Support paginated SDK Connection polling
@@ -110,9 +114,8 @@ Redis-specific options for cluster mode:
_(Note that CACHE_CONNECTION_URL is ignored when using cluster mode)_
- `USE_CLUSTER` - "true" or "1" to enable (default: `false`)
-- `CLUSTER_ROOT_NODES` - simple: comma-separated URLs to your cluster seed nodes
-- `CLUSTER_ROOT_NODES_JSON` - advanced: JSON array of ClusterNode objects (ioredis)
-- `CLUSTER_OPTIONS_JSON` - advanced: JSON object of ClusterOptions (ioredis)
+- `CLUSTER_ROOT_NODES_JSON` - JSON array of ClusterNode objects (ioredis)
+- `CLUSTER_OPTIONS_JSON` - JSON object of ClusterOptions (ioredis)
#### MongoDB
diff --git a/packages/apps/proxy/package.json b/packages/apps/proxy/package.json
index 815d819..9e078a4 100644
--- a/packages/apps/proxy/package.json
+++ b/packages/apps/proxy/package.json
@@ -4,7 +4,7 @@
"node": ">=18"
},
"description": "GrowthBook proxy server for caching, realtime updates, telemetry, etc",
- "version": "1.1.1",
+ "version": "1.1.2",
"main": "dist/app.js",
"license": "MIT",
"repository": {
diff --git a/packages/apps/proxy/src/controllers/adminController.ts b/packages/apps/proxy/src/controllers/adminController.ts
index 6ce127f..7e2f4f8 100644
--- a/packages/apps/proxy/src/controllers/adminController.ts
+++ b/packages/apps/proxy/src/controllers/adminController.ts
@@ -2,6 +2,7 @@ import express, { Request, Response } from "express";
import { registrar } from "../services/registrar";
import { adminMiddleware } from "../middleware/adminMiddleware";
import logger from "../services/logger";
+import { MAX_PAYLOAD_SIZE } from "../init";
const postConnection = (req: Request, res: Response) => {
const apiKey = req.body.apiKey;
@@ -57,7 +58,7 @@ adminRouter.post(
"/connection",
adminMiddleware,
express.json({
- limit: process.env.MAX_PAYLOAD_SIZE ?? "2mb",
+ limit: process.env.MAX_PAYLOAD_SIZE ?? MAX_PAYLOAD_SIZE,
}),
postConnection,
);
diff --git a/packages/apps/proxy/src/controllers/featuresController.ts b/packages/apps/proxy/src/controllers/featuresController.ts
index 871017a..db81f05 100644
--- a/packages/apps/proxy/src/controllers/featuresController.ts
+++ b/packages/apps/proxy/src/controllers/featuresController.ts
@@ -11,6 +11,7 @@ import { sseSupportMiddleware } from "../middleware/sseSupportMiddleware";
import logger from "../services/logger";
import { fetchFeatures } from "../services/features";
import { Context } from "../types";
+import { MAX_PAYLOAD_SIZE } from "../init";
const getFeatures = async (req: Request, res: Response, next: NextFunction) => {
if (!registrar?.growthbookApiHost) {
@@ -181,7 +182,9 @@ export const featuresRouter = (ctx: Context) => {
router.post(
"/api/eval/*",
apiKeyMiddleware,
- express.json(),
+ express.json({
+ limit: process.env.MAX_PAYLOAD_SIZE ?? MAX_PAYLOAD_SIZE,
+ }),
sseSupportMiddleware,
getEvaluatedFeatures,
);
@@ -192,6 +195,7 @@ export const featuresRouter = (ctx: Context) => {
"/proxy/features",
apiKeyMiddleware,
express.json({
+ limit: process.env.MAX_PAYLOAD_SIZE ?? MAX_PAYLOAD_SIZE,
verify: (req: Request, res: Response, buf: Buffer) =>
(res.locals.rawBody = buf),
}),
diff --git a/packages/apps/proxy/src/init.ts b/packages/apps/proxy/src/init.ts
index 5902677..c409557 100644
--- a/packages/apps/proxy/src/init.ts
+++ b/packages/apps/proxy/src/init.ts
@@ -4,6 +4,8 @@ import dotenv from "dotenv";
import { CacheEngine, Context } from "./types";
dotenv.config({ path: "./.env.local" });
+export const MAX_PAYLOAD_SIZE = "2mb";
+
export default async () => {
const context: Partial = {
growthbookApiHost: process.env.GROWTHBOOK_API_HOST,
@@ -15,7 +17,7 @@ export default async () => {
verboseDebugging: ["true", "1"].includes(
process.env.VERBOSE_DEBUGGING ?? "0",
),
- maxPayloadSize: process.env.MAX_PAYLOAD_SIZE ?? "2mb",
+ maxPayloadSize: process.env.MAX_PAYLOAD_SIZE ?? MAX_PAYLOAD_SIZE,
// SDK Connections settings:
createConnectionsFromEnv: ["true", "1"].includes(process.env.CREATE_CONNECTIONS_FROM_ENV ?? "1"),
pollForConnections: ["true", "1"].includes(process.env.POLL_FOR_CONNECTIONS ?? "1"),
@@ -37,9 +39,6 @@ export default async () => {
),
// Redis only - cluster:
useCluster: ["true", "1"].includes(process.env.USE_CLUSTER ?? "0"),
- clusterRootNodes: process.env.CLUSTER_ROOT_NODES
- ? process.env.CLUSTER_ROOT_NODES.replace(" ", "").split(",")
- : undefined,
clusterRootNodesJSON: process.env.CLUSTER_ROOT_NODES_JSON
? JSON.parse(process.env.CLUSTER_ROOT_NODES_JSON)
: undefined,
diff --git a/packages/apps/proxy/src/services/cache/RedisCache.ts b/packages/apps/proxy/src/services/cache/RedisCache.ts
index 3643f77..ff51a90 100644
--- a/packages/apps/proxy/src/services/cache/RedisCache.ts
+++ b/packages/apps/proxy/src/services/cache/RedisCache.ts
@@ -22,7 +22,7 @@ export class RedisCache {
public readonly allowStale: boolean;
private readonly useCluster: boolean;
- private readonly clusterRootNodes?: ClusterNode[];
+ private readonly clusterRootNodesJSON?: ClusterNode[];
private readonly clusterOptions?: ClusterOptions;
private readonly appContext?: Context;
@@ -36,7 +36,6 @@ export class RedisCache {
useAdditionalMemoryCache,
publishPayloadToChannel = false,
useCluster = false,
- clusterRootNodes,
clusterRootNodesJSON,
clusterOptionsJSON,
}: CacheSettings = {},
@@ -48,8 +47,7 @@ export class RedisCache {
this.allowStale = allowStale;
this.publishPayloadToChannel = publishPayloadToChannel;
this.useCluster = useCluster;
- this.clusterRootNodes =
- clusterRootNodesJSON ?? this.transformRootNodes(clusterRootNodes);
+ this.clusterRootNodesJSON = clusterRootNodesJSON;
this.clusterOptions = clusterOptionsJSON;
this.appContext = appContext;
@@ -69,9 +67,9 @@ export class RedisCache {
? new Redis(this.connectionUrl)
: new Redis();
} else {
- if (this.clusterRootNodes) {
+ if (this.clusterRootNodesJSON) {
this.client = new Redis.Cluster(
- this.clusterRootNodes,
+ this.clusterRootNodesJSON,
this.clusterOptions,
);
} else {
@@ -268,21 +266,4 @@ export class RedisCache {
public getsubscriberClient() {
return this.subscriberClient;
}
-
- private transformRootNodes(rootNodes?: string[]): ClusterNode[] | undefined {
- if (!rootNodes) return undefined;
- return rootNodes
- .map((node) => {
- try {
- const url = new URL(node);
- const host = url.protocol + "//" + url.hostname + url.pathname;
- const port = parseInt(url.port);
- return { host, port };
- } catch (e) {
- logger.error(e, "Error parsing Redis cluster node");
- return undefined;
- }
- })
- .filter(Boolean) as ClusterNode[];
- }
}
diff --git a/packages/apps/proxy/src/services/cache/index.ts b/packages/apps/proxy/src/services/cache/index.ts
index aa30d7b..1ec9408 100644
--- a/packages/apps/proxy/src/services/cache/index.ts
+++ b/packages/apps/proxy/src/services/cache/index.ts
@@ -22,7 +22,6 @@ export interface CacheSettings {
useAdditionalMemoryCache?: boolean;
publishPayloadToChannel?: boolean; // for RedisCache pub/sub
useCluster?: boolean; // for RedisCache
- clusterRootNodes?: string[]; // for RedisCache
clusterRootNodesJSON?: ClusterNode[]; // for RedisCache
clusterOptionsJSON?: ClusterOptions; // for RedisCache
}