@sleep2agi/commhub-server 0.5.0-preview.19 → 0.5.0-preview.20
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/README.md +14 -3
- package/package.json +1 -1
- package/src/auth.ts +9 -0
- package/src/index.ts +16 -1
package/README.md
CHANGED
|
@@ -89,9 +89,20 @@ delivered/acked/running → reassign → delivered (新agent)
|
|
|
89
89
|
|
|
90
90
|
## 鉴权
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
两种认证方式:
|
|
93
|
+
1. **V3 用户系统** (推荐): `POST /api/auth/register` → 获取 `atok_xxx` token
|
|
94
|
+
2. **全局 token** (传统): `COMMHUB_AUTH_TOKEN` 环境变量
|
|
95
|
+
|
|
96
|
+
Header: `Authorization: Bearer <token>` 或 Query: `?token=<token>`
|
|
97
|
+
|
|
98
|
+
## V3 功能
|
|
99
|
+
|
|
100
|
+
- **用户系统**: 注册/登录/Token 认证
|
|
101
|
+
- **多网络**: 每个用户可创建多个独立网络
|
|
102
|
+
- **网络隔离**: 不同网络的数据完全隔离
|
|
103
|
+
- **试用授权**: 14 天免费试用, 到期需授权码
|
|
104
|
+
- **审计日志**: 所有操作记录
|
|
105
|
+
- **限流**: 注册 30/min, 登录 10/min (per IP)
|
|
95
106
|
- `/health` 不需要 auth
|
|
96
107
|
|
|
97
108
|
## License
|
package/package.json
CHANGED
package/src/auth.ts
CHANGED
|
@@ -132,3 +132,12 @@ export function createNetwork(userId: string, name: string, description?: string
|
|
|
132
132
|
);
|
|
133
133
|
return { ok: true, network_id: networkId, network_name: name };
|
|
134
134
|
}
|
|
135
|
+
|
|
136
|
+
export function changePassword(userId: string, oldPassword: string, newPassword: string): { ok: boolean; error?: string } {
|
|
137
|
+
if (!newPassword || newPassword.length < 6) return { ok: false, error: "new password must be at least 6 characters" };
|
|
138
|
+
const user = db.query<any, [string]>("SELECT password_hash FROM users WHERE user_id = ?1").get(userId);
|
|
139
|
+
if (!user) return { ok: false, error: "user not found" };
|
|
140
|
+
if (user.password_hash !== hashPassword(oldPassword)) return { ok: false, error: "incorrect current password" };
|
|
141
|
+
db.run("UPDATE users SET password_hash = ?1, updated_at = datetime('now') WHERE user_id = ?2", [hashPassword(newPassword), userId]);
|
|
142
|
+
return { ok: true };
|
|
143
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { z } from "zod/v4";
|
|
|
4
4
|
import { registerTools } from "./tools.js";
|
|
5
5
|
import { db, logTaskEvent, logAudit } from "./db.js";
|
|
6
6
|
import { createSSEStream, pushEvent, pushBroadcast, getSSEStats } from "./push.js";
|
|
7
|
-
import { register, login, resolveToken, getUserNetworks, createNetwork, type AuthUser } from "./auth.js";
|
|
7
|
+
import { register, login, resolveToken, getUserNetworks, createNetwork, changePassword, type AuthUser } from "./auth.js";
|
|
8
8
|
|
|
9
9
|
const PORT = Number(process.env.PORT) || 9200;
|
|
10
10
|
const AUTH_TOKEN = process.env.COMMHUB_AUTH_TOKEN;
|
|
@@ -290,6 +290,21 @@ Bun.serve({
|
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
if (url.pathname === "/api/auth/password" && req.method === "POST") {
|
|
294
|
+
const token = req.headers.get("Authorization")?.replace("Bearer ", "");
|
|
295
|
+
if (!token) return withCors(req, Response.json({ ok: false, error: "token required" }, { status: 401 }));
|
|
296
|
+
const resolved = resolveToken(token);
|
|
297
|
+
if (!resolved) return withCors(req, Response.json({ ok: false, error: "invalid token" }, { status: 401 }));
|
|
298
|
+
try {
|
|
299
|
+
const body = await req.json() as any;
|
|
300
|
+
const result = changePassword(resolved.user.user_id, body.old_password, body.new_password);
|
|
301
|
+
if (result.ok) logAudit(resolved.user.user_id, resolved.user.username, "password_changed", "user", resolved.user.user_id);
|
|
302
|
+
return withCors(req, Response.json(result, { status: result.ok ? 200 : 400 }));
|
|
303
|
+
} catch (e: any) {
|
|
304
|
+
return withCors(req, Response.json({ ok: false, error: e.message }, { status: 400 }));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
293
308
|
// ── V3: Network management ──
|
|
294
309
|
if (url.pathname === "/api/networks" && req.method === "GET") {
|
|
295
310
|
const token = req.headers.get("Authorization")?.replace("Bearer ", "") || url.searchParams.get("token");
|