@wenbin_wb/dsh-bridge 2.7.1 → 2.8.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.
@@ -0,0 +1,93 @@
1
+ // 滑动窗口请求速率限制器 (Rate Limiter)
2
+ // 防范暴力破解、高频遍历与 DoS 攻击
3
+
4
+ export class RateLimiter {
5
+ /**
6
+ * @param {object} options
7
+ * @param {number} options.maxRequests 窗口期内允许的最大请求数(默认 30)
8
+ * @param {number} options.windowMs 时间窗口毫秒数(默认 60000ms = 1分钟)
9
+ */
10
+ constructor({ maxRequests = 30, windowMs = 60000 } = {}) {
11
+ this.maxRequests = maxRequests;
12
+ this.windowMs = windowMs;
13
+ /** @type {Map<string, number[]>} key -> timestamp[] */
14
+ this.records = new Map();
15
+
16
+ // 定期清理过期记录(每 5 分钟清理一次)
17
+ this.cleanupTimer = setInterval(() => this.cleanup(), 5 * 60 * 1000);
18
+ if (this.cleanupTimer.unref) this.cleanupTimer.unref();
19
+ }
20
+
21
+ /**
22
+ * 检查指定 Key(如 IP / 用户 / Session)是否允许本次请求
23
+ * @param {string} key 限制标识
24
+ * @param {number} [customMax] 单次自定义最大请求数
25
+ * @returns {{ allowed: boolean, remaining: number, retryAfterSec: number }}
26
+ */
27
+ check(key = 'default', customMax) {
28
+ const limit = customMax || this.maxRequests;
29
+ const now = Date.now();
30
+ const windowStart = now - this.windowMs;
31
+
32
+ let timestamps = this.records.get(key);
33
+ if (!timestamps) {
34
+ timestamps = [];
35
+ this.records.set(key, timestamps);
36
+ }
37
+
38
+ // 过滤掉当前窗口期之前的记录
39
+ const valid = timestamps.filter(t => t > windowStart);
40
+ this.records.set(key, valid);
41
+
42
+ if (valid.length >= limit) {
43
+ const oldestInWindow = valid[0];
44
+ const resetTime = oldestInWindow + this.windowMs;
45
+ const retryAfterSec = Math.max(1, Math.ceil((resetTime - now) / 1000));
46
+ return {
47
+ allowed: false,
48
+ remaining: 0,
49
+ retryAfterSec,
50
+ };
51
+ }
52
+
53
+ valid.push(now);
54
+ return {
55
+ allowed: true,
56
+ remaining: limit - valid.length,
57
+ retryAfterSec: 0,
58
+ };
59
+ }
60
+
61
+ /**
62
+ * 重置指定 key 或全部记录
63
+ * @param {string} [key]
64
+ */
65
+ reset(key) {
66
+ if (key) this.records.delete(key);
67
+ else this.records.clear();
68
+ }
69
+
70
+ /**
71
+ * 清理所有过期记录
72
+ */
73
+ cleanup() {
74
+ const now = Date.now();
75
+ const windowStart = now - this.windowMs;
76
+ for (const [key, list] of this.records.entries()) {
77
+ const valid = list.filter(t => t > windowStart);
78
+ if (valid.length === 0) {
79
+ this.records.delete(key);
80
+ } else {
81
+ this.records.set(key, valid);
82
+ }
83
+ }
84
+ }
85
+
86
+ dispose() {
87
+ if (this.cleanupTimer) {
88
+ clearInterval(this.cleanupTimer);
89
+ this.cleanupTimer = null;
90
+ }
91
+ this.records.clear();
92
+ }
93
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@wenbin_wb/dsh-bridge",
3
- "version": "2.7.1",
3
+ "version": "2.8.0",
4
4
  "description": "手机扫码即可在移动端/公网继续用 DeepSeek Harness,人不在电脑前也能接着干。一键局域网二维码、Cloudflare 公网隧道、自建隧道与微信 / QQ / 飞书 / Telegram Bot(多工作区/会话持久化/媒体/卡片审批/流式输出),无需自己搭公网服务器。",
5
- "releaseNotes": "【v2.7.1 Windows 一键升级与自建协议修复】\n• 🛠️ 修复 Windows 平台下一键升级触发 `spawn EINVAL` 的问题\n• 🌐 修复自建隧道 WebSocket (ws/wss) 协议诊断报错的问题\n• 🛡️ 强化 Gateway 启动并发锁与会话恢复防抖",
5
+ "releaseNotes": "【v2.8.0 移动端体验革新、远程工作区管理与全面安全加固】\n• 🗂️ 远程工作区网页选择器:移动端/远程点击添加工作区自动呼出网页端目录浏览器,本机电脑智能无感分流原生系统选择器\n• 📱 移动端视觉与布局体验深度优化:顶部导航栏居中动态标题、第二行徽标与极简下载图标两端排布、底部工具栏防重叠自适应\n• 🔐 全方位安全加固:RPC 端点鉴权、路径穿越与系统核心目录防御、滑动窗口限流与 IM 权限拦截",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",
8
8
  "exports": {