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.
- package/package.json +1 -1
- package/src/next.js +55 -19
package/package.json
CHANGED
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
|
-
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
await
|
|
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
|
|
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
|
-
|
|
167
|
-
if (
|
|
168
|
-
await
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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 &&
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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:
|
|
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
|
|
272
|
-
|
|
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 });
|