unified-keys 0.7.0 → 0.9.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.
package/README.md CHANGED
@@ -55,7 +55,7 @@ const v = await client.verifyKey({ key: userKey, context: { ip: "1.2.3.4" } });
55
55
 
56
56
  创建 key(自动绑定 client.appId)。返回 `{ keys: [{ key, keyId }] }`,`key` 是完整明文。`length` 默认 8;**长期付费 key 传 16**。
57
57
 
58
- `autoDelete`:`true` 立即软删;`"3m"`/`"2h"`/`"3d"` 延迟软删(set deleteAt,Cron 清理,保留记录供 admin 查一段时间)。
58
+ `autoDelete`:`true` 立即软删;`"3m"`/`"2h"`/`"3d"` 延迟软删(set deleteAt,Cron 每 5min 清理,保留记录供 admin 查一段时间)。`"now"`/`"0"` 等价 `true`(0.9.0 收窄,向后兼容)。`KeyRecord.deleteAt` 返回软删时间点,前端可展示「将于 XX 软删」。
59
59
 
60
60
  ### `checkKey({ key, context? })` / `client.checkKey(...)`
61
61
 
@@ -89,6 +89,13 @@ const v = await client.verifyKey({ key: userKey, context: { ip: "1.2.3.4" } });
89
89
  - **rootKey 必传**:verify 扣减也需 rootKey(0.6.0 已加鉴权)
90
90
  - **baseUrl 内置**:不再在文档/示例暴露服务地址
91
91
 
92
+ ## 0.8.0 → 0.9.0
93
+
94
+ - **autoDelete 值域收窄**:`true`(立即软删) / `"3m"`/`"2h"`/`"3d"`(延迟) / 不设(不删);`"now"`/`"0"` 等价 `true`(向后兼容,worker 收到归一)
95
+ - **`KeyRecord.deleteAt`**:新增返回软删时间点(延迟模式 + verify 用尽后写入),前端可展示「将于 XX 软删」
96
+ - **Cron 10min → 5min**:延迟软删精度提升(deleteAt 与实际软删偏差 ≤5min)
97
+ - 非 breaking(additive + 兼容归一)
98
+
92
99
  ## License
93
100
 
94
101
  MIT
package/SKILL.md CHANGED
@@ -11,7 +11,7 @@ description: 当项目需要管理 API key(创建、只读校验、扣减验
11
11
 
12
12
  - **appId 是 client 的责任**:`createClient({ appId, rootKey })` 必传 appId,createKey/listKeys 自动绑定,不再每次传
13
13
  - **check/verify 端点拆分**(0.6.0 安全修复):`POST /v1/keys/check` 公开只读(不扣不删);`POST /v1/keys/verify` 需 `rootKeyAuth`(扣减是写操作)
14
- - **autoDelete 在 createKey 预设**(不再由 verify 调用方传——堵「任何人拿 key 明文删别人 key」漏洞)
14
+ - **autoDelete 在 createKey 预设**(不再由 verify 调用方传——堵「任何人拿 key 明文删别人 key」漏洞);值域 `true`(立即软删) / `"3d"` 等时长(延迟软删) / 不设(不删),`"now"`/`"0"` 等价 `true`(0.9.0 收窄,老值 worker 归一兼容)
15
15
  - **baseUrl 内置**:服务端点已内置到 SDK(`DEFAULT_BASE_URL`),文档/示例不暴露
16
16
 
17
17
  ## 何时用
@@ -49,7 +49,7 @@ const { keys } = await client.createKey({
49
49
  externalId: "user-123", // 应用层关联业务实体(可选)
50
50
  meta: { plan: "pro" }, // 任意元数据(可选)
51
51
  rateLimit: { max: 60, windowSec: 60 }, // 限流(max 不传 = 不限速)
52
- autoBan: { threshold: 10, windowSec: 600, maxViolations: 3, durationSec: 3600 },
52
+ autoBan: { pause: { threshold: 10, windowSec: 600, durationSec: 3600 }, banAfter: 3 },
53
53
  });
54
54
 
55
55
  // 公开只读校验(无需 rootKey,不扣减不删)
@@ -87,7 +87,7 @@ if (!isValidKeyFormat(inputKey)) return invalid();
87
87
  | event | `check` | `verify`(无限 key 写 `check`) |
88
88
  | 场景 | 探活 / 前端校验 / 只验证不扣减 | 真正消费额度 |
89
89
 
90
- **`autoDelete`(createKey 预设,verify 执行)**:`true` 立即 soft delete;`"3m"`/`"2h"`/`"3d"` 延迟(set deleteAt,Cron 每 10min 清理);不传 = 不删(无限/长期 key)。
90
+ **`autoDelete`(用尽后软删策略,createKey 预设,verify 执行)**:`true` 立即 soft delete;`"3m"`/`"2h"`/`"3d"` 延迟(set deleteAt,Cron 每 5min 清理);不传 = 不删(无限/长期 key)。`KeyRecord.deleteAt` 返回软删时间点(延迟模式 + 用尽后写入),前端可展示「将于 XX 软删」。
91
91
 
92
92
  **返回 `code`**(valid=false 时):`RATE_LIMITED`(429) / `KEY_PAUSED`、`KEY_BANNED`(423) / `KEY_DISABLED` / `KEY_EXPIRED` / `KEY_EXHAUSTED`。
93
93
 
@@ -108,20 +108,27 @@ if (!r.valid) return res.status(403).json({ error: "Key 无效" });
108
108
  | check/verify 拆分 | ✅ 公开只读 vs 管理扣减 | ❌ 单端点 |
109
109
  | 用尽自动删 | ✅ autoDelete(createKey 预设) | ❌ |
110
110
  | 永久痕迹 | ✅ soft delete 反查 | ❌ 真删丢痕迹 |
111
- | 限流 / 自动封禁 | ✅ DO 滑动窗口 / 渐进式·直接 | ❌ |
111
+ | 限流 / 自动封禁 | ✅ DO SQLite 滑动窗口 / pause·ban·渐进 | ❌ |
112
112
  | 前置格式校验 | ✅ SDK isValidKeyFormat | ❌ |
113
113
 
114
- ## 自动封禁(autoBan)
114
+ ## 自动处罚(autoBan)
115
115
 
116
- **渐进式**(默认):违规累计达 maxViolations 永久 ban,前 N-1 次 pause。
116
+ `pause` / `ban` 互斥,`banAfter` 渐进式升级。三种模式:
117
+
118
+ **纯暂停**:窗口内 rate_limited 达 `pause.threshold` → 暂停(`pauseCount++`)。
119
+ ```jsonc
120
+ { "pause": { "threshold": 10, "windowSec": 600, "durationSec": 3600 } }
121
+ ```
122
+ **直接封禁**:窗口内 rate_limited 达 `ban.threshold` → 永久 ban。
117
123
  ```jsonc
118
- { "threshold": 10, "windowSec": 600, "maxViolations": 3, "durationSec": 3600 }
124
+ { "ban": { "threshold": 15, "windowSec": 600 } }
119
125
  ```
120
- **直接**(`mode: "direct"`):单次达阈值即 action。
126
+ **渐进**(先 pause,累计 N 次升级 ban):`pause` + `banAfter`。
121
127
  ```jsonc
122
- { "mode": "direct", "action": "ban", "threshold": 10, "windowSec": 600 }
128
+ { "pause": { "threshold": 10, "windowSec": 600, "durationSec": 3600 }, "banAfter": 3 }
123
129
  ```
124
- `unban` 重置违规计数;`unpause` 不重置。
130
+
131
+ 校验:`pause` 与 `ban` 互斥;`banAfter` 仅配 `pause` 时允许。`unban` 重置 `pauseCount`;`unpause` 不重置。
125
132
 
126
133
  ## event 类型(usage_log)
127
134
 
@@ -165,7 +172,7 @@ const { found, key, recentLogs } = await client.lookupKey({ plaintext: "sk_xxx"
165
172
  ## 实现位置(项目内)
166
173
 
167
174
  - 服务端:`apps/key-service`(Hono Worker;D1 binding `KEY_SERVICE_DB` + Durable Object `RATE_LIMITER`)
168
- - SDK:`packages/keys`(npm `unified-keys@0.7.0`,monorepo workspace 直接用 src;服务端点 `DEFAULT_BASE_URL` 在 `src/client.ts`)
175
+ - SDK:`packages/keys`(npm `unified-keys@0.9.0`,monorepo workspace 直接用 src;服务端点 `DEFAULT_BASE_URL` 在 `src/client.ts`)
169
176
  - schema:`packages/db/src/schemas/key-service.ts`(`key_apps` / `key_records`(含 `auto_delete` 列)/ `key_usage_logs`)
170
- - migrations:`packages/infra/migrations/keys/0001` ~ `0005`(alchemy deploy 自动跑)
177
+ - migrations:`packages/infra/migrations/keys/0001` ~ `0007`(alchemy deploy 自动跑)
171
178
  - rootKey:`packages/infra/.env` 的 `KEYS_ROOT_KEY`(admin-api + 4 应用共用,alchemy `secret("KEYS_ROOT_KEY")` 同步)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unified-keys",
3
- "version": "0.7.0",
4
- "description": "自建通用 API key 管理 SDK(明文存储 + 明文返回 + 完整使用记录),调用 keys.932324.xyz 服务",
3
+ "version": "0.9.0",
4
+ "description": "通用 API key 管理 SDK(明文存储 + 明文返回 + 完整使用记录 + per-key 限流/自动处罚)",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
7
7
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export type {
3
3
  AppRecord,
4
4
  AutoBanConfig,
5
5
  BanInput,
6
+ BanRule,
6
7
  BatchDeleteResult,
7
8
  CheckInput,
8
9
  CheckResult,
@@ -24,6 +25,7 @@ export type {
24
25
  LookupResult,
25
26
  PauseInput,
26
27
  PauseResult,
28
+ PauseRule,
27
29
  RateLimitConfig,
28
30
  UpdateCreditsInput,
29
31
  UpdateCreditsResult,
package/src/types.ts CHANGED
@@ -35,28 +35,49 @@ export interface RateLimitConfig {
35
35
  windowSec?: number;
36
36
  }
37
37
 
38
- /** 自动封禁配置(meta.autoBan)*/
39
- export interface AutoBanConfig {
40
- /** direct: 单次触发动作(pause/ban,默认 pause)*/
41
- action?: "pause" | "ban";
42
- /** pause 持续秒数,默认 3600(1h)*/
38
+ /** 暂停触发规则(与 ban 互斥):窗口内 rate_limited 达 threshold → 暂停 */
39
+ export interface PauseRule {
40
+ /** 暂停时长秒数,默认 3600(1h) */
43
41
  durationSec?: number;
44
- /** progressive: 违规达此数永久 ban(默认 3);前 N-1 次 pause */
45
- maxViolations?: number;
46
- /** 模式:progressive(渐进式,默认)/ direct(直接,单次达阈值即 action)*/
47
- mode?: "progressive" | "direct";
48
- /** 触发阈值(窗口内 rate_limited 次数 = 一次违规)*/
42
+ /** 统计窗口内 rate_limited 达此数 触发一次 pause(pause_count +1 */
43
+ threshold: number;
44
+ /** 统计窗口秒数,默认 600(10min) */
45
+ windowSec?: number;
46
+ }
47
+
48
+ /** 封禁触发规则(与 pause 互斥):窗口内 rate_limited 达 threshold → 永久 ban */
49
+ export interface BanRule {
50
+ /** 统计窗口内 rate_limited 达此数 → 永久 ban */
49
51
  threshold: number;
50
- /** 统计窗口秒数,默认 600(10min)*/
52
+ /** 统计窗口秒数,默认 600(10min) */
51
53
  windowSec?: number;
52
54
  }
53
55
 
56
+ /**
57
+ * 自动处罚策略(存 meta.autoBan)。三种模式:
58
+ * - 纯暂停:`{ pause: { threshold, durationSec } }`
59
+ * - 直接封禁:`{ ban: { threshold } }`
60
+ * - 渐进(先 pause,达 banAfter 后 ban):`{ pause: {...}, banAfter: N }`
61
+ */
62
+ export interface AutoBanConfig {
63
+ /** 封禁规则。与 pause 互斥。 */
64
+ ban?: BanRule;
65
+ /**
66
+ * 渐进式升级(仅配 pause 时有效):累计 pause 次数达此数 → 升级永久 ban。
67
+ * 配 ban 时不能配;没配 pause 时不能配。
68
+ */
69
+ banAfter?: number;
70
+ /** 暂停规则。与 ban 互斥。 */
71
+ pause?: PauseRule;
72
+ }
73
+
54
74
  export interface CreateKeyInput {
55
- /** 自动封禁配置(存 meta.autoBan)*/
75
+ /** 自动处罚策略(存 meta.autoBan):pause/ban 互斥,banAfter 渐进式 */
56
76
  autoBan?: AutoBanConfig;
57
77
  /**
58
- * autoDeletecreateKey 时预设为 key 属性,verify 扣减到 0 时执行;verify 端点不再接受调用方传参):
59
- * true / "now" → 立即 soft delete;"3m" / "2h" / "3d" → set deleteAt,Cron 软删(保留记录供 admin 查一段时间)
78
+ * autoDelete(用尽后软删策略:createKey 预设,verify 扣到 0 时执行;verify 端点不再接受调用方传参):
79
+ * true → 立即 soft delete;"3m"/"2h"/"3d" → set deleteAt,Cron 软删(保留记录供 admin 查一段时间);不设 = 不删
80
+ * ("now"/"0" 老值等价 true,向后兼容)
60
81
  */
61
82
  autoDelete?: boolean | string;
62
83
  /** 批量创建数量,默认 1 */
@@ -95,12 +116,14 @@ export interface CreateKeyResult {
95
116
 
96
117
  export interface KeyRecord {
97
118
  appId: string;
98
- /** autoDelete 策略(createKey 预设,verify 只读):null=不删 | "true"=用尽立即软删 | "3m"/"2h"/"3d"=延迟软删 */
119
+ /** autoDelete 策略(createKey 预设,verify 只读):null=不删 | "true"=用尽立即软删 | "Nm"/"Nh"/"Nd"=延迟软删 */
99
120
  autoDelete: string | null;
100
121
  banReason: string | null;
101
122
  banStatus: "active" | "paused" | "banned";
102
123
  createdAt: string;
103
124
  creditsRemaining: number;
125
+ /** 软删时间点(ISO):autoDelete 延迟模式 + verify 扣到 0 时写入;Cron 每 5min 扫描软删;null=未安排 */
126
+ deleteAt: string | null;
104
127
  /** soft delete 标志(true=已软删,verify/list 过滤但 admin 可查)*/
105
128
  deleted: boolean;
106
129
  enabled: boolean;