bro-framework 2.4.4 → 2.4.5

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/next.js +55 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bro-framework",
3
- "version": "2.4.4",
3
+ "version": "2.4.5",
4
4
  "description": "The No-BS Backend Framework for Node.js",
5
5
  "repository": {
6
6
  "type": "git",
package/src/next.js CHANGED
@@ -10,8 +10,12 @@ export function createBro(globalConfig = {}) {
10
10
  let initPromise = null;
11
11
 
12
12
  let globalDb = null;
13
- let globalRedis = null;
14
13
  let globalLocale = null;
14
+
15
+ const globalStore = globalThis;
16
+ globalStore.__broRedis = globalStore.__broRedis || null;
17
+ globalStore.__broMemoryCache = globalStore.__broMemoryCache || new Map();
18
+ globalStore.__broMemoryRateLimit = globalStore.__broMemoryRateLimit || new Map();
15
19
 
16
20
  async function ensureInitialized() {
17
21
  if (isInitialized) return;
@@ -38,10 +42,10 @@ export function createBro(globalConfig = {}) {
38
42
  }
39
43
 
40
44
  if (globalConfig.redisUrl) {
41
- globalRedis = createClient({ url: globalConfig.redisUrl });
42
- globalRedis.on('error', (err) => console.error('[bro.js/next] Redis Error:', err));
43
- if (globalRedis.status === 'wait' || !globalRedis.status) {
44
- await globalRedis.connect().catch(err => {
45
+ globalStore.__broRedis = createClient({ url: globalConfig.redisUrl });
46
+ globalStore.__broRedis.on('error', (err) => console.error('[bro.js/next] Redis Error:', err));
47
+ if (!globalStore.__broRedis.isOpen) {
48
+ await globalStore.__broRedis.connect().catch(err => {
45
49
  if (!err.message.includes('already connecting') && !err.message.includes('already connected')) {
46
50
  throw err;
47
51
  }
@@ -159,29 +163,57 @@ export function createBro(globalConfig = {}) {
159
163
 
160
164
  // Rate Limiting
161
165
  const activeRateLimit = config.rateLimit === false ? null : (config.rateLimit || globalConfig.rateLimit);
162
- if (activeRateLimit && globalRedis) {
166
+ if (activeRateLimit) {
163
167
  const ip = req.headers.get('x-forwarded-for') || 'ip';
164
168
  const urlObj = new URL(req.url);
165
169
  const rlKey = `rate-limit:${urlObj.pathname}:${ip}`;
166
- const currentCount = await globalRedis.incr(rlKey);
167
- if (currentCount === 1) {
168
- await globalRedis.expire(rlKey, Math.ceil(activeRateLimit.windowMs / 1000));
169
- }
170
- if (currentCount > activeRateLimit.max) {
171
- return NextResponse.json({ error: 'Too Many Requests' }, { status: 429 });
170
+
171
+ if (globalStore.__broRedis) {
172
+ const currentCount = await globalStore.__broRedis.incr(rlKey);
173
+ if (currentCount === 1) {
174
+ await globalStore.__broRedis.expire(rlKey, Math.ceil(activeRateLimit.windowMs / 1000));
175
+ }
176
+ if (currentCount > activeRateLimit.max) {
177
+ return NextResponse.json({ error: 'Too Many Requests' }, { status: 429 });
178
+ }
179
+ } else {
180
+ const now = Date.now();
181
+ let record = globalStore.__broMemoryRateLimit.get(rlKey);
182
+
183
+ if (!record || now > record.expires) {
184
+ record = { count: 0, expires: now + activeRateLimit.windowMs };
185
+ }
186
+
187
+ record.count++;
188
+ globalStore.__broMemoryRateLimit.set(rlKey, record);
189
+
190
+ if (record.count > activeRateLimit.max) {
191
+ return NextResponse.json({ error: 'Too Many Requests' }, { status: 429 });
192
+ }
172
193
  }
173
194
  }
174
195
 
175
196
  // Caching
176
197
  let cacheKey = null;
177
- if (config.cache && globalRedis && req.method === 'GET') {
198
+ if (config.cache && req.method === 'GET') {
178
199
  const urlObj = new URL(req.url);
179
200
  const identity = user ? (user.id || user.role || 'user') : (apiKeyUsed || 'anon');
180
201
  cacheKey = `cache:${urlObj.pathname}${urlObj.search}:${resolvedLocale}:${identity}`;
181
202
 
182
- const cachedData = await globalRedis.get(cacheKey);
183
- if (cachedData) {
184
- return NextResponse.json(JSON.parse(cachedData), { status: 200 });
203
+ if (globalStore.__broRedis) {
204
+ const cachedData = await globalStore.__broRedis.get(cacheKey);
205
+ if (cachedData) {
206
+ return NextResponse.json(JSON.parse(cachedData), { status: 200 });
207
+ }
208
+ } else {
209
+ const cached = globalStore.__broMemoryCache.get(cacheKey);
210
+ if (cached) {
211
+ if (Date.now() < cached.expires) {
212
+ return NextResponse.json(cached.data, { status: 200 });
213
+ } else {
214
+ globalStore.__broMemoryCache.delete(cacheKey);
215
+ }
216
+ }
185
217
  }
186
218
  }
187
219
 
@@ -250,7 +282,7 @@ export function createBro(globalConfig = {}) {
250
282
  req,
251
283
  env: process.env,
252
284
  db: globalDb,
253
- redis: globalRedis,
285
+ redis: globalStore.__broRedis,
254
286
  io: { emit: () => console.warn('[bro.js/next] WebSockets require standard bro.js server.') },
255
287
  body,
256
288
  params,
@@ -268,8 +300,12 @@ export function createBro(globalConfig = {}) {
268
300
 
269
301
  const result = await config.handler(ctx);
270
302
 
271
- if (cacheKey && globalRedis) {
272
- await globalRedis.set(cacheKey, JSON.stringify(result), { EX: config.cache });
303
+ if (cacheKey) {
304
+ if (globalStore.__broRedis) {
305
+ await globalStore.__broRedis.set(cacheKey, JSON.stringify(result), { EX: config.cache });
306
+ } else {
307
+ globalStore.__broMemoryCache.set(cacheKey, { data: result, expires: Date.now() + (config.cache * 1000) });
308
+ }
273
309
  }
274
310
 
275
311
  return NextResponse.json(result, { status: 200 });