@maiyunnet/kebab 2.0.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.
Files changed (114) hide show
  1. package/.VSCodeCounter/2025-02-14_14-46-44/details.md +82 -0
  2. package/.VSCodeCounter/2025-02-14_14-46-44/diff-details.md +15 -0
  3. package/.VSCodeCounter/2025-02-14_14-46-44/diff.csv +2 -0
  4. package/.VSCodeCounter/2025-02-14_14-46-44/diff.md +19 -0
  5. package/.VSCodeCounter/2025-02-14_14-46-44/diff.txt +22 -0
  6. package/.VSCodeCounter/2025-02-14_14-46-44/results.csv +69 -0
  7. package/.VSCodeCounter/2025-02-14_14-46-44/results.json +1 -0
  8. package/.VSCodeCounter/2025-02-14_14-46-44/results.md +48 -0
  9. package/.VSCodeCounter/2025-02-14_14-46-44/results.txt +118 -0
  10. package/.vscode/tasks.json +15 -0
  11. package/LICENSE +201 -0
  12. package/README.md +201 -0
  13. package/bin/kebab.js +2 -0
  14. package/eslint.config.js +22 -0
  15. package/index.js +19 -0
  16. package/index.ts +33 -0
  17. package/lib/buffer.js +108 -0
  18. package/lib/buffer.ts +152 -0
  19. package/lib/captcha/zcool-addict-italic.ttf +0 -0
  20. package/lib/captcha.js +71 -0
  21. package/lib/captcha.ts +63 -0
  22. package/lib/consistent.js +171 -0
  23. package/lib/consistent.ts +219 -0
  24. package/lib/core.js +663 -0
  25. package/lib/core.ts +880 -0
  26. package/lib/crypto.js +256 -0
  27. package/lib/crypto.ts +384 -0
  28. package/lib/db.js +521 -0
  29. package/lib/db.ts +719 -0
  30. package/lib/dns.js +321 -0
  31. package/lib/dns.ts +405 -0
  32. package/lib/fs.js +405 -0
  33. package/lib/fs.ts +527 -0
  34. package/lib/jwt.js +223 -0
  35. package/lib/jwt.ts +276 -0
  36. package/lib/kv.js +1004 -0
  37. package/lib/kv.ts +1489 -0
  38. package/lib/lan.js +99 -0
  39. package/lib/lan.ts +87 -0
  40. package/lib/net/cacert.pem +3480 -0
  41. package/lib/net/formdata.js +137 -0
  42. package/lib/net/formdata.ts +166 -0
  43. package/lib/net/request.js +102 -0
  44. package/lib/net/request.ts +150 -0
  45. package/lib/net/response.js +28 -0
  46. package/lib/net/response.ts +59 -0
  47. package/lib/net.js +462 -0
  48. package/lib/net.ts +662 -0
  49. package/lib/s3.js +180 -0
  50. package/lib/s3.ts +235 -0
  51. package/lib/scan.js +276 -0
  52. package/lib/scan.ts +364 -0
  53. package/lib/session.js +177 -0
  54. package/lib/session.ts +230 -0
  55. package/lib/sql.js +818 -0
  56. package/lib/sql.ts +1151 -0
  57. package/lib/ssh/sftp.js +373 -0
  58. package/lib/ssh/sftp.ts +508 -0
  59. package/lib/ssh/shell.js +109 -0
  60. package/lib/ssh/shell.ts +123 -0
  61. package/lib/ssh.js +171 -0
  62. package/lib/ssh.ts +191 -0
  63. package/lib/text/tld.json +1 -0
  64. package/lib/text.js +452 -0
  65. package/lib/text.ts +607 -0
  66. package/lib/time.js +216 -0
  67. package/lib/time.ts +254 -0
  68. package/lib/ws.js +373 -0
  69. package/lib/ws.ts +523 -0
  70. package/lib/zip.js +381 -0
  71. package/lib/zip.ts +447 -0
  72. package/lib/zlib.js +289 -0
  73. package/lib/zlib.ts +350 -0
  74. package/main.js +51 -0
  75. package/main.ts +27 -0
  76. package/package.json +37 -0
  77. package/sys/child.js +585 -0
  78. package/sys/child.ts +678 -0
  79. package/sys/cmd.js +226 -0
  80. package/sys/cmd.ts +225 -0
  81. package/sys/ctr.js +608 -0
  82. package/sys/ctr.ts +904 -0
  83. package/sys/master.js +314 -0
  84. package/sys/master.ts +355 -0
  85. package/sys/mod.js +1273 -0
  86. package/sys/mod.ts +1871 -0
  87. package/sys/route.js +922 -0
  88. package/sys/route.ts +1113 -0
  89. package/types/index.d.ts +283 -0
  90. package/www/example/ctr/main.js +42 -0
  91. package/www/example/ctr/main.ts +9 -0
  92. package/www/example/ctr/middle.js +57 -0
  93. package/www/example/ctr/middle.ts +26 -0
  94. package/www/example/ctr/test.js +2818 -0
  95. package/www/example/ctr/test.ts +3218 -0
  96. package/www/example/data/locale/en.test.json +8 -0
  97. package/www/example/data/locale/index.html +1 -0
  98. package/www/example/data/locale/ja.test.json +8 -0
  99. package/www/example/data/locale/sc.test.json +8 -0
  100. package/www/example/data/locale/tc.test.json +8 -0
  101. package/www/example/data/test.zip +0 -0
  102. package/www/example/kebab.json +24 -0
  103. package/www/example/mod/test.js +49 -0
  104. package/www/example/mod/test.ts +47 -0
  105. package/www/example/mod/testdata.js +11 -0
  106. package/www/example/mod/testdata.ts +30 -0
  107. package/www/example/route.json +6 -0
  108. package/www/example/view/test.ejs +11 -0
  109. package/www/example/ws/mproxy.js +49 -0
  110. package/www/example/ws/mproxy.ts +16 -0
  111. package/www/example/ws/rproxy.js +47 -0
  112. package/www/example/ws/rproxy.ts +14 -0
  113. package/www/example/ws/test.js +68 -0
  114. package/www/example/ws/test.ts +36 -0
package/lib/kv.ts ADDED
@@ -0,0 +1,1489 @@
1
+ /**
2
+ * Project: Kebab, User: JianSuoQiYue
3
+ * Date: 2019-5-30 19:25:22
4
+ * Last: 2020-3-28 18:54:04, 2022-09-12 23:24:45, 2022-09-22 01:06:22, 2024-2-21 13:32:56, 2024-8-21 16:59:57
5
+ */
6
+
7
+ // --- Pool 是使用时必须要一个用户创建一份的,Connection 是池子里获取的 ---
8
+
9
+ // --- 为啥 Pool 要独立,因为有些配置项目不能存在 Connection 是用户单独使用的,例如 pre ---
10
+
11
+ // --- 第三方 ---
12
+ import * as redis from '@litert/redis';
13
+ // --- 库和定义 ---
14
+ import * as time from '~/lib/time';
15
+ import * as text from '~/lib/text';
16
+ import * as ctr from '~/sys/ctr';
17
+ import * as types from '~/types';
18
+
19
+ /** --- 连接信息 --- */
20
+ export interface IConnectionInfo {
21
+ 'id': number;
22
+ 'last': number;
23
+ 'host': string;
24
+ 'port': number;
25
+ 'index': number;
26
+
27
+ 'lost': boolean;
28
+ }
29
+
30
+ /** --- 连接列表(同一个 host、port、index、auth 只有一个连接) --- */
31
+ const connections: Connection[] = [];
32
+
33
+ /**
34
+ * --- 计划任务 30 秒一次,关闭超过 3 分钟不活动的连接 ---
35
+ */
36
+ async function checkConnection(): Promise<void> {
37
+ const now: number = time.stamp();
38
+ for (let i = 0; i < connections.length; ++i) {
39
+ const connection = connections[i];
40
+ if (connection.isLost()) {
41
+ // --- 连接已经丢失,移除 ---
42
+ await connection.end();
43
+ connections.splice(i, 1);
44
+ --i;
45
+ continue;
46
+ }
47
+ if (connection.isUsing()) {
48
+ // --- 连接正在被使用,看看是否使用超过 3 分钟,超过则不是正常状态 ---
49
+ if (connection.getLast() <= now - 180) {
50
+ // --- 10 分钟之前开始的 ---
51
+ console.log(`[kv] [error] There is a transactional connection[${i}] that is not closed.`);
52
+ await connection.end();
53
+ connections.splice(i, 1);
54
+ --i;
55
+ }
56
+ continue;
57
+ }
58
+ // --- 检测 3 分钟内是否使用过 ---
59
+ if (connection.getLast() > now - 180) {
60
+ // --- 3 分钟内使用过,不管 ---
61
+ continue;
62
+ }
63
+ // --- 超 3 分钟未被使用,则关闭 ---
64
+ await connection.end();
65
+ connections.splice(i, 1);
66
+ --i;
67
+ continue;
68
+ }
69
+ setTimeout(function() {
70
+ checkConnection().catch(e => { console.log('[KV]', e); });
71
+ }, 30000);
72
+ }
73
+ setTimeout(function() {
74
+ checkConnection().catch(e => { console.log('[KV]', e); });
75
+ }, 30000);
76
+
77
+ export class Pool {
78
+
79
+ /** --- 当前 Pool 对象的 kv 连接信息 --- */
80
+ private readonly _etc: types.IConfigKv;
81
+
82
+ public constructor(ctr: ctr.Ctr, etc?: types.IConfigKv) {
83
+ if (etc) {
84
+ this._etc = etc;
85
+ }
86
+ else {
87
+ this._etc = ctr.getPrototype('_config').kv;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * --- 设定一个值 ---
93
+ * @param key
94
+ * @param val
95
+ * @param ttl 秒,0 为不限制
96
+ * @param mod 设置模式: 空,nx(key不存在才建立),xx(key存在才修改)
97
+ */
98
+ public async set(key: string, val: object | string | number, ttl: number = 0, mod: '' | 'nx' | 'xx' = ''): Promise<boolean> {
99
+ const conn = await this._getConnection();
100
+ if (!conn) {
101
+ return false;
102
+ }
103
+ const r = await conn.set(key, val, ttl, mod, this._etc);
104
+ conn.used();
105
+ return r;
106
+ }
107
+
108
+ /**
109
+ * --- 添加一个值,存在则不变 ---
110
+ * @param key
111
+ * @param val
112
+ * @param ttl 秒,0 为不限制
113
+ */
114
+ public async add(key: string, val: object | string | number, ttl: number = 0): Promise<boolean> {
115
+ const conn = await this._getConnection();
116
+ if (!conn) {
117
+ return false;
118
+ }
119
+ const r = await conn.add(key, val, ttl, this._etc);
120
+ conn.used();
121
+ return r;
122
+ }
123
+
124
+ /**
125
+ * --- 替换一个存在的值 ---
126
+ * @param key
127
+ * @param val
128
+ * @param ttl 秒,0 为不限制
129
+ */
130
+ public async replace(key: string, val: object | string | number, ttl: number = 0): Promise<boolean> {
131
+ const conn = await this._getConnection();
132
+ if (!conn) {
133
+ return false;
134
+ }
135
+ const r = await conn.replace(key, val, ttl, this._etc);
136
+ conn.used();
137
+ return r;
138
+ }
139
+
140
+ /**
141
+ * --- 向已存在的值后追加数据 ---
142
+ * @param key
143
+ * @param val
144
+ */
145
+ public async append(key: string, val: string): Promise<boolean> {
146
+ const conn = await this._getConnection();
147
+ if (!conn) {
148
+ return false;
149
+ }
150
+ const r = await conn.append(key, val, this._etc);
151
+ conn.used();
152
+ return r;
153
+ }
154
+
155
+ /**
156
+ * --- 向已存在的值之前追加数据 ---
157
+ * @param key
158
+ * @param val
159
+ */
160
+ public async prepend(key: string, val: string): Promise<boolean> {
161
+ const conn = await this._getConnection();
162
+ if (!conn) {
163
+ return false;
164
+ }
165
+ const r = await conn.prepend(key, val, this._etc);
166
+ conn.used();
167
+ return r;
168
+ }
169
+
170
+ /**
171
+ * --- 检测 key 是否存在 ---
172
+ * @param keys
173
+ */
174
+ public async exists(keys: string | string[]): Promise<number> {
175
+ const conn = await this._getConnection();
176
+ if (!conn) {
177
+ return 0;
178
+ }
179
+ const r = await conn.exists(keys, this._etc);
180
+ conn.used();
181
+ return r;
182
+ }
183
+
184
+ /**
185
+ * --- 获取字符串 ---
186
+ * @param key
187
+ */
188
+ public async get(key: string): Promise<string | null> {
189
+ const conn = await this._getConnection();
190
+ if (!conn) {
191
+ return null;
192
+ }
193
+ const r = await conn.get(key, this._etc);
194
+ conn.used();
195
+ return r;
196
+ }
197
+
198
+ /**
199
+ * --- 获取相应的剩余有效期秒数 ---
200
+ * @param key
201
+ */
202
+ public async ttl(key: string): Promise<number | null> {
203
+ const conn = await this._getConnection();
204
+ if (!conn) {
205
+ return null;
206
+ }
207
+ const r = await conn.ttl(key, this._etc);
208
+ conn.used();
209
+ return r;
210
+ }
211
+
212
+ /**
213
+ * --- 获取相应的剩余有效期毫秒数 ---
214
+ * @param key
215
+ */
216
+ public async pttl(key: string): Promise<number | null> {
217
+ const conn = await this._getConnection();
218
+ if (!conn) {
219
+ return null;
220
+ }
221
+ const r = await conn.pttl(key, this._etc);
222
+ conn.used();
223
+ return r;
224
+ }
225
+
226
+ /**
227
+ * --- 批量获取值 ---
228
+ * @param keys key 序列
229
+ */
230
+ public async mSet(rows: Record<string, string | Buffer>): Promise<boolean> {
231
+ const conn = await this._getConnection();
232
+ if (!conn) {
233
+ return false;
234
+ }
235
+ const r = await conn.mSet(rows, this._etc);
236
+ conn.used();
237
+ return r;
238
+ }
239
+
240
+ /**
241
+ * --- 批量获取值 ---
242
+ * @param keys key 序列
243
+ */
244
+ public async mGet(keys: string[]): Promise<Record<string, string | null>> {
245
+ const conn = await this._getConnection();
246
+ if (conn) {
247
+ const r = await conn.mGet(keys, this._etc);
248
+ conn.used();
249
+ return r;
250
+ }
251
+ else {
252
+ const rtn: Record<string, string | null> = {};
253
+ for (const key of keys) {
254
+ rtn[key] = null;
255
+ }
256
+ return rtn;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * --- 获取 json 对象 ---
262
+ * @param key
263
+ */
264
+ public async getJson(key: string): Promise<any | null> {
265
+ const conn = await this._getConnection();
266
+ if (!conn) {
267
+ return null;
268
+ }
269
+ const r = await conn.getJson(key, this._etc);
270
+ conn.used();
271
+ return r;
272
+ }
273
+
274
+ /**
275
+ * --- 删除已存在的值 ---
276
+ * @param keys
277
+ */
278
+ public async del(keys: string | string[]): Promise<boolean> {
279
+ const conn = await this._getConnection();
280
+ if (!conn) {
281
+ return false;
282
+ }
283
+ const r = await conn.del(keys, this._etc);
284
+ conn.used();
285
+ return r;
286
+ }
287
+
288
+ /**
289
+ * --- 自增 ---
290
+ * @param key
291
+ * @param num 整数或浮点正数
292
+ */
293
+ public async incr(key: string, num: number = 1): Promise<number | false> {
294
+ const conn = await this._getConnection();
295
+ if (!conn) {
296
+ return false;
297
+ }
298
+ const r = await conn.incr(key, num, this._etc);
299
+ conn.used();
300
+ return r;
301
+ }
302
+
303
+ /**
304
+ * --- 自减 ---
305
+ * @param key
306
+ * @param num 整数或浮点正数
307
+ */
308
+ public async decr(key: string, num: number = 1): Promise<number | false> {
309
+ const conn = await this._getConnection();
310
+ if (!conn) {
311
+ return false;
312
+ }
313
+ const r = await conn.decr(key, num, this._etc);
314
+ conn.used();
315
+ return r;
316
+ }
317
+
318
+ /**
319
+ * --- 仅修改过期时间不修改值 ---
320
+ * @param key
321
+ * @param ttl
322
+ */
323
+ public async expire(key: string, ttl: number): Promise<boolean> {
324
+ const conn = await this._getConnection();
325
+ if (!conn) {
326
+ return false;
327
+ }
328
+ const r = await conn.expire(key, ttl, this._etc);
329
+ conn.used();
330
+ return r;
331
+ }
332
+
333
+ /**
334
+ * --- 获取服务器上的所有 key 列表 ---
335
+ * @param pattern
336
+ */
337
+ public async keys(pattern: string): Promise<string[] | false> {
338
+ const conn = await this._getConnection();
339
+ if (!conn) {
340
+ return false;
341
+ }
342
+ const r = await conn.keys(pattern, this._etc);
343
+ conn.used();
344
+ return r;
345
+ }
346
+
347
+ /**
348
+ * --- 根据条件获取服务器上的 keys ---
349
+ * @param cursor
350
+ * @param pattern 例如 *
351
+ * @param count 获取的条数
352
+ */
353
+ public async scan(cursor: number = 0, pattern: string = '*', count: number = 10): Promise<redis.IScanResult<string> | false> {
354
+ const conn = await this._getConnection();
355
+ if (!conn) {
356
+ return false;
357
+ }
358
+ const r = await conn.scan(cursor, pattern, count, this._etc);
359
+ conn.used();
360
+ return r;
361
+ }
362
+
363
+ /**
364
+ * --- 清除当前所选数据库的所有内容 ---
365
+ */
366
+ public async flushDb(): Promise<boolean> {
367
+ const conn = await this._getConnection();
368
+ if (!conn) {
369
+ return false;
370
+ }
371
+ const r = await conn.flushDb();
372
+ conn.used();
373
+ return r;
374
+ }
375
+
376
+ /**
377
+ * --- 发送 ping ---
378
+ */
379
+ public async ping(): Promise<string | false> {
380
+ const conn = await this._getConnection();
381
+ if (!conn) {
382
+ return false;
383
+ }
384
+ const r = await conn.ping();
385
+ conn.used();
386
+ return r;
387
+ }
388
+
389
+ /**
390
+ * --- 设置哈希表值 ---
391
+ * @param key key 名
392
+ * @param field 字段名
393
+ * @param val 值
394
+ * @param mod 空,nx(key不存在才建立)
395
+ */
396
+ public async hSet(key: string, field: string, val: object | string | number, mod: '' | 'nx' = ''): Promise<boolean> {
397
+ const conn = await this._getConnection();
398
+ if (!conn) {
399
+ return false;
400
+ }
401
+ const r = await conn.hSet(key, field, val, mod, this._etc);
402
+ conn.used();
403
+ return r;
404
+ }
405
+
406
+ /**
407
+ * --- 批量设置哈希值 ---
408
+ * @param key key 名
409
+ * @param rows key / val 数组
410
+ */
411
+ public async hMSet(key: string, rows: Record<string, object | string | number>): Promise<boolean> {
412
+ const conn = await this._getConnection();
413
+ if (!conn) {
414
+ return false;
415
+ }
416
+ const r = await conn.hMSet(key, rows, this._etc);
417
+ conn.used();
418
+ return r;
419
+ }
420
+
421
+ /**
422
+ * --- 获取哈希值 ---
423
+ * @param key
424
+ * @param field
425
+ */
426
+ public async hGet(key: string, field: string): Promise<string | null> {
427
+ const conn = await this._getConnection();
428
+ if (!conn) {
429
+ return null;
430
+ }
431
+ const r = await conn.hGet(key, field, this._etc);
432
+ conn.used();
433
+ return r;
434
+ }
435
+
436
+ /**
437
+ * --- 获取哈希 json 对象 ---
438
+ * @param key
439
+ * @param field
440
+ */
441
+ public async hGetJson(key: string, field: string): Promise<any | null> {
442
+ const conn = await this._getConnection();
443
+ if (!conn) {
444
+ return null;
445
+ }
446
+ const r = await conn.hGetJson(key, field, this._etc);
447
+ conn.used();
448
+ return r;
449
+ }
450
+
451
+ /**
452
+ * --- 批量获取哈希值 ---
453
+ * @param key
454
+ * @param fields
455
+ */
456
+ public async hMGet(key: string, fields: string[]): Promise<Record<string, string | null>> {
457
+ const conn = await this._getConnection();
458
+ if (conn) {
459
+ const r = await conn.hMGet(key, fields, this._etc);
460
+ conn.used();
461
+ return r;
462
+ }
463
+ else {
464
+ const rtn: Record<string, string | null> = {};
465
+ for (const field of fields) {
466
+ rtn[field] = null;
467
+ }
468
+ return rtn;
469
+ }
470
+ }
471
+
472
+ /**
473
+ * --- 批量获取哈希键值对 ---
474
+ * @param key
475
+ */
476
+ public async hGetAll(key: string): Promise<Record<string, string | null> | null> {
477
+ const conn = await this._getConnection();
478
+ if (!conn) {
479
+ return null;
480
+ }
481
+ const r = await conn.hGetAll(key, this._etc);
482
+ conn.used();
483
+ return r;
484
+ }
485
+
486
+ /**
487
+ * --- 删除哈希键 ---
488
+ * @param key
489
+ * @param fields 值序列
490
+ */
491
+ public async hDel(key: string, fields: string | string[]): Promise<number> {
492
+ const conn = await this._getConnection();
493
+ if (!conn) {
494
+ return 0;
495
+ }
496
+ const r = await conn.hDel(key, fields, this._etc);
497
+ conn.used();
498
+ return r;
499
+ }
500
+
501
+ /**
502
+ * --- 判断哈希字段是否存在 ---
503
+ * @param key
504
+ * @param field
505
+ */
506
+ public async hExists(key: string, field: string): Promise<boolean> {
507
+ const conn = await this._getConnection();
508
+ if (!conn) {
509
+ return false;
510
+ }
511
+ const r = await conn.hExists(key, field, this._etc);
512
+ conn.used();
513
+ return r;
514
+ }
515
+
516
+ /**
517
+ * --- 设置哈希自增自减 ---
518
+ * @param key key
519
+ * @param field 字段
520
+ * @param increment 正数或负数,整数或浮点
521
+ */
522
+ public async hIncr(key: string, field: string, increment: number): Promise<number> {
523
+ const conn = await this._getConnection();
524
+ if (!conn) {
525
+ return 0;
526
+ }
527
+ const r = await conn.hIncr(key, field, increment, this._etc);
528
+ conn.used();
529
+ return r;
530
+ }
531
+
532
+ /**
533
+ * --- 获取哈希所有字段 ---
534
+ * @param key
535
+ */
536
+ public async hKeys(key: string): Promise<string[]> {
537
+ const conn = await this._getConnection();
538
+ if (!conn) {
539
+ return [];
540
+ }
541
+ const r = await conn.hKeys(key, this._etc);
542
+ conn.used();
543
+ return r;
544
+ }
545
+
546
+ public async lPush(key: string, values: Array<string | Buffer>): Promise<number> {
547
+ const conn = await this._getConnection();
548
+ if (!conn) {
549
+ return 0;
550
+ }
551
+ const r = await conn.lPush(key, values, this._etc);
552
+ conn.used();
553
+ return r;
554
+ }
555
+
556
+ public async rPush(key: string, values: Array<string | Buffer>): Promise<number> {
557
+ const conn = await this._getConnection();
558
+ if (!conn) {
559
+ return 0;
560
+ }
561
+ const r = await conn.rPush(key, values, this._etc);
562
+ conn.used();
563
+ return r;
564
+ }
565
+
566
+ public async bLMove(sourceKey: string, destKey: string, soo: 'LEFT' | 'RIGHT', deo: 'LEFT' | 'RIGHT', timeout: number): Promise<string | null> {
567
+ const conn = await this._getConnection();
568
+ if (!conn) {
569
+ return null;
570
+ }
571
+ const r = await conn.bLMove(sourceKey, destKey, soo, deo, timeout, this._etc);
572
+ conn.used();
573
+ return r;
574
+ }
575
+
576
+ public async lPop(key: string): Promise<string | null> {
577
+ const conn = await this._getConnection();
578
+ if (!conn) {
579
+ return null;
580
+ }
581
+ const r = await conn.lPop(key, this._etc);
582
+ conn.used();
583
+ return r;
584
+ }
585
+
586
+ public async rPop(key: string): Promise<string | null> {
587
+ const conn = await this._getConnection();
588
+ if (!conn) {
589
+ return null;
590
+ }
591
+ const r = await conn.rPop(key, this._etc);
592
+ conn.used();
593
+ return r;
594
+ }
595
+
596
+ public async bRPop(key: string | string[], timeout: number): Promise<Record<string, string>> {
597
+ const conn = await this._getConnection();
598
+ if (!conn) {
599
+ return {};
600
+ }
601
+ const r = await conn.bRPop(key, timeout, this._etc);
602
+ conn.used();
603
+ return r;
604
+ }
605
+
606
+ public async lRange(key: string, start: number, stop: number): Promise<string[]> {
607
+ const conn = await this._getConnection();
608
+ if (!conn) {
609
+ return [];
610
+ }
611
+ const r = await conn.lRange(key, start, stop, this._etc);
612
+ conn.used();
613
+ return r;
614
+ }
615
+
616
+ public async lLen(key: string): Promise<number> {
617
+ const conn = await this._getConnection();
618
+ if (!conn) {
619
+ return 0;
620
+ }
621
+ const r = await conn.lLen(key, this._etc);
622
+ conn.used();
623
+ return r;
624
+ }
625
+
626
+ /**
627
+ * --- 从连接池获取一个连接,会被自动设置为使用中,需要在执行命令后解除占用 ---
628
+ */
629
+ private async _getConnection(): Promise<Connection | null> {
630
+ let conn!: Connection;
631
+ for (const connection of connections) {
632
+ const etc = connection.getEtc();
633
+ if (
634
+ connection.isLost() ||
635
+ connection.isUsing() ||
636
+ (etc.host !== this._etc.host) ||
637
+ (etc.port !== this._etc.port) ||
638
+ (etc.index !== this._etc.index)
639
+ ) {
640
+ // --- 配置项连接项不匹配 ---
641
+ continue;
642
+ }
643
+ connection.refreshLast();
644
+ connection.setUsing();
645
+ conn = connection;
646
+ break;
647
+ }
648
+ if (!conn) {
649
+ // --- 没有找到合适的连接,创建一个 ---
650
+ const link = redis.createCommandClient({
651
+ 'host': this._etc.host,
652
+ 'port': this._etc.port
653
+ });
654
+ // --- 开始连接 ---
655
+ try {
656
+ await link.connect();
657
+ }
658
+ catch {
659
+ return null;
660
+ }
661
+ // --- 认证 ---
662
+ if (this._etc.user || this._etc.pwd) {
663
+ try {
664
+ await link.auth(this._etc.user + this._etc.pwd);
665
+ }
666
+ catch {
667
+ return null;
668
+ }
669
+ }
670
+ await link.select(this._etc.index);
671
+ conn = new Connection(this._etc, link);
672
+ conn.refreshLast();
673
+ conn.setUsing();
674
+ link.on('error', function(err): void {
675
+ conn.setLost();
676
+ // console.log(`--- redis [${conn._etc.host}:${conn._etc.port}] error ---`);
677
+ console.log('[KV] [ERROR]', err);
678
+ }).on('close', () => {
679
+ conn.setLost();
680
+ });
681
+ connections.push(conn);
682
+ }
683
+ return conn;
684
+ }
685
+
686
+ }
687
+
688
+ export class Connection {
689
+ /** --- 本连接最后一次使用时间 --- */
690
+ private _last: number = 0;
691
+
692
+ /** --- kv 缓存连接对象 --- */
693
+ private readonly _link: redis.ICommandClient;
694
+
695
+ /** --- 当前连接是否正在被独占使用 --- */
696
+ private _using: boolean = false;
697
+
698
+ /** --- 当发生断开,则需从连接池移除连接 --- */
699
+ private _lost: boolean = false;
700
+
701
+ /** --- 当前的连接配置信息 --- */
702
+ private readonly _etc: types.IConfigKv;
703
+
704
+ public constructor(etc: types.IConfigKv, link: redis.ICommandClient) {
705
+ this._etc = etc;
706
+ this._link = link;
707
+ this.refreshLast();
708
+ }
709
+
710
+ /**
711
+ * --- 获取连接 etc 信息 ---
712
+ */
713
+ public getEtc(): types.IConfigKv {
714
+ return this._etc;
715
+ }
716
+
717
+ /**
718
+ * --- 获取最后一次获取连接的时间 ---
719
+ */
720
+ public getLast(): number {
721
+ return this._last;
722
+ }
723
+
724
+ /**
725
+ * --- 将本条连接设置为不可用 ---
726
+ */
727
+ public setLost(): void {
728
+ this._lost = true;
729
+ }
730
+
731
+ /**
732
+ * --- 是否已经丢失 ---
733
+ */
734
+ public isLost(): boolean {
735
+ return this._lost;
736
+ }
737
+
738
+ /**
739
+ * --- 将本条连接设置占用中 ---
740
+ */
741
+ public setUsing(): void {
742
+ this._using = true;
743
+ }
744
+
745
+ /**
746
+ * --- 获取当前状态是否正在被使用中 ---
747
+ */
748
+ public isUsing(): boolean {
749
+ return this._using;
750
+ }
751
+
752
+ /**
753
+ * --- 取消占用 ---
754
+ */
755
+ public used(): void {
756
+ this._using = false;
757
+ }
758
+
759
+ /**
760
+ * --- 设定最后使用时间 ---
761
+ */
762
+ public refreshLast(): void {
763
+ this._last = time.stamp();
764
+ }
765
+
766
+ /**
767
+ * --- 断开此连接,一般情况下不使用 ---
768
+ */
769
+ public async end(): Promise<void> {
770
+ try {
771
+ await this._link.close();
772
+ }
773
+ catch {
774
+ return;
775
+ }
776
+ }
777
+
778
+ /**
779
+ * --- 设定一个值 ---
780
+ * @param key
781
+ * @param val
782
+ * @param ttl 秒,0 为不限制
783
+ * @param mod 设置模式: 空,nx(key不存在才建立),xx(key存在才修改)
784
+ * @param etc 配置项,主要用 etc.pre
785
+ */
786
+ public async set(key: string, val: object | string | number, ttl: number, mod: '' | 'nx' | 'xx', etc: types.IConfigKv): Promise<boolean> {
787
+ this.refreshLast();
788
+ if (typeof val !== 'string') {
789
+ val = text.stringifyJson(val);
790
+ }
791
+ try {
792
+ switch (mod) {
793
+ case '': {
794
+ return await this._link.set(etc.pre + key, val, ttl === 0 ? undefined : ttl);
795
+ }
796
+ case 'nx': {
797
+ return await this._link.setNX(etc.pre + key, val, ttl === 0 ? undefined : ttl);
798
+ }
799
+ case 'xx': {
800
+ return await this._link.replace(etc.pre + key, val, ttl === 0 ? undefined : ttl);
801
+ }
802
+ }
803
+ }
804
+ catch {
805
+ return false;
806
+ }
807
+ }
808
+
809
+ /**
810
+ * --- 添加一个值,存在则不变 ---
811
+ * @param key
812
+ * @param val
813
+ * @param ttl 秒,0 为不限制
814
+ * @param etc
815
+ */
816
+ public async add(
817
+ key: string,
818
+ val: object | string | number,
819
+ ttl: number,
820
+ etc: types.IConfigKv
821
+ ): Promise<boolean> {
822
+ return this.set(key, val, ttl, 'nx', etc);
823
+ }
824
+
825
+ /**
826
+ * --- 替换一个存在的值 ---
827
+ * @param key
828
+ * @param val
829
+ * @param ttl 秒,0 为不限制
830
+ * @param etc
831
+ */
832
+ public async replace(
833
+ key: string,
834
+ val: object | string | number,
835
+ ttl: number,
836
+ etc: types.IConfigKv
837
+ ): Promise<boolean> {
838
+ return this.set(key, val, ttl, 'xx', etc);
839
+ }
840
+
841
+ /**
842
+ * --- 向已存在的值后追加数据 ---
843
+ * @param key
844
+ * @param val
845
+ * @param etc
846
+ */
847
+ public async append(key: string, val: string, etc: types.IConfigKv): Promise<boolean> {
848
+ this.refreshLast();
849
+ try {
850
+ return await this._link.append(etc.pre + key, val) > 0 ? true : false;
851
+ }
852
+ catch {
853
+ return false;
854
+ }
855
+ }
856
+
857
+ /**
858
+ * --- 向已存在的值之前追加数据 ---
859
+ * @param key
860
+ * @param val
861
+ */
862
+ public async prepend(key: string, val: string, etc: types.IConfigKv): Promise<boolean> {
863
+ this.refreshLast();
864
+ try {
865
+ const script: string = `local val = redis.call('GET', KEYS[1])
866
+ if (val == false) then
867
+ return 0
868
+ end
869
+ local r = redis.call('SET', KEYS[1], ARGV[1]..val)
870
+ if (r) then
871
+ return 1
872
+ else
873
+ return 0
874
+ end`;
875
+ let r = await this._link.evalSHA('ea360f3f6508a243824ecda6be15db56df217873', [etc.pre + key], [val]);
876
+ r = parseInt(r);
877
+ if (r <= 0) {
878
+ await this._link.scriptLoad(script);
879
+ r = this._link.evalSHA('ea360f3f6508a243824ecda6be15db56df217873', [etc.pre + key], [val]);
880
+ r = parseInt(r);
881
+ }
882
+ return r > 0 ? true : false;
883
+ }
884
+ catch {
885
+ return false;
886
+ }
887
+ }
888
+
889
+ /**
890
+ * --- 检测 key 是否存在 ---
891
+ * @param keys 单个或序列
892
+ * @param etc
893
+ */
894
+ public async exists(keys: string | string[], etc: types.IConfigKv): Promise<number> {
895
+ this.refreshLast();
896
+ try {
897
+ if (typeof keys === 'string') {
898
+ keys = [keys];
899
+ }
900
+ for (let k = 0; k < keys.length; ++k) {
901
+ keys[k] = etc.pre + keys[k];
902
+ }
903
+ return await this._link.mExists(keys);
904
+ }
905
+ catch {
906
+ return 0;
907
+ }
908
+ }
909
+
910
+ /**
911
+ * --- 获取字符串 ---
912
+ * @param key
913
+ * @param etc
914
+ */
915
+ public async get(key: string, etc: types.IConfigKv): Promise<string | null> {
916
+ this.refreshLast();
917
+ try {
918
+ return await this._link.get(etc.pre + key);
919
+ }
920
+ catch {
921
+ return null;
922
+ }
923
+ }
924
+
925
+ /**
926
+ * --- 获取相应的剩余有效期秒数 ---
927
+ * @param key
928
+ * @param etc
929
+ */
930
+ public async ttl(key: string, etc: types.IConfigKv): Promise<number | null> {
931
+ this.refreshLast();
932
+ try {
933
+ return await this._link.ttl(etc.pre + key);
934
+ }
935
+ catch {
936
+ return null;
937
+ }
938
+ }
939
+
940
+ /**
941
+ * --- 获取相应的剩余有效期毫秒数 ---
942
+ * @param key
943
+ * @param etc
944
+ */
945
+ public async pttl(key: string, etc: types.IConfigKv): Promise<number | null> {
946
+ this.refreshLast();
947
+ try {
948
+ return await this._link.pTTL(etc.pre + key);
949
+ }
950
+ catch {
951
+ return null;
952
+ }
953
+ }
954
+
955
+ /**
956
+ * --- 批量获取值 ---
957
+ * @param keys key 序列
958
+ * @param etc 顺序数组
959
+ */
960
+ public async mGet(keys: string[], etc: types.IConfigKv): Promise<Record<string, string | null>> {
961
+ this.refreshLast();
962
+ for (let k = 0; k < keys.length; ++k) {
963
+ keys[k] = etc.pre + keys[k];
964
+ }
965
+ const rtn: Record<string, string | null> = {};
966
+ try {
967
+ const pl: number = etc.pre.length;
968
+ const r = await this._link.mGet(keys);
969
+ if (pl === 0) {
970
+ return r;
971
+ }
972
+ for (const k in r) {
973
+ rtn[k.slice(pl)] = r[k];
974
+ }
975
+ }
976
+ catch {
977
+ for (const key of keys) {
978
+ rtn[key] = null;
979
+ }
980
+ }
981
+ return rtn;
982
+ }
983
+
984
+ /**
985
+ * --- 批量设置哈希值 ---
986
+ * @param key key 名
987
+ * @param rows key / val 数组
988
+ * @param etc
989
+ */
990
+ public async mSet(
991
+ rows: Record<string, string | Buffer>,
992
+ etc: types.IConfigKv
993
+ ): Promise<boolean> {
994
+ this.refreshLast();
995
+ try {
996
+ const rtn: Record<string, string | Buffer> = {};
997
+ for (const key in rows) {
998
+ rtn[etc.pre + key] = rows[key];
999
+ }
1000
+ await this._link.mSet(rtn);
1001
+ return true;
1002
+ }
1003
+ catch {
1004
+ return false;
1005
+ }
1006
+ }
1007
+
1008
+ /**
1009
+ * --- 获取 json 对象 ---
1010
+ * @param key
1011
+ * @param etc
1012
+ */
1013
+ public async getJson(key: string, etc: types.IConfigKv): Promise<any | null> {
1014
+ const v = await this.get(key, etc);
1015
+ if (v === null) {
1016
+ return null;
1017
+ }
1018
+ const r = text.parseJson(v);
1019
+ return r === false ? null : r;
1020
+ }
1021
+
1022
+ /**
1023
+ * --- 删除已存在的值 ---
1024
+ * @param keys
1025
+ * @param etc
1026
+ */
1027
+ public async del(keys: string | string[], etc: types.IConfigKv): Promise<boolean> {
1028
+ this.refreshLast();
1029
+ if (typeof keys === 'string') {
1030
+ keys = [keys];
1031
+ }
1032
+ for (let k = 0; k < keys.length; ++k) {
1033
+ keys[k] = etc.pre + keys[k];
1034
+ }
1035
+ try {
1036
+ return await this._link.del(keys) > 0 ? true : false;
1037
+ }
1038
+ catch {
1039
+ return false;
1040
+ }
1041
+ }
1042
+
1043
+ /**
1044
+ * --- 自增 ---
1045
+ * @param key
1046
+ * @param num 整数或浮点正数
1047
+ * @param etc
1048
+ */
1049
+ public async incr(key: string, num: number, etc: types.IConfigKv): Promise<number | false> {
1050
+ this.refreshLast();
1051
+ try {
1052
+ if (Number.isInteger(num)) {
1053
+ if (num === 1) {
1054
+ return await this._link.incr(etc.pre + key);
1055
+ }
1056
+ else {
1057
+ return await this._link.incr(etc.pre + key, num);
1058
+ }
1059
+ }
1060
+ else {
1061
+ return await this._link.incrByFloat(etc.pre + key, num);
1062
+ }
1063
+ }
1064
+ catch {
1065
+ return false;
1066
+ }
1067
+ }
1068
+
1069
+ /**
1070
+ * --- 自减 ---
1071
+ * @param key
1072
+ * @param num 整数或浮点正数
1073
+ * @param etc
1074
+ */
1075
+ public async decr(key: string, num: number, etc: types.IConfigKv): Promise<number | false> {
1076
+ this.refreshLast();
1077
+ try {
1078
+ if (Number.isInteger(num)) {
1079
+ if (num === 1) {
1080
+ return await this._link.decr(etc.pre + key);
1081
+ }
1082
+ else {
1083
+ return await this._link.decr(etc.pre + key, num);
1084
+ }
1085
+ }
1086
+ else {
1087
+ return await this._link.incrByFloat(etc.pre + key, -num);
1088
+ }
1089
+ }
1090
+ catch {
1091
+ return false;
1092
+ }
1093
+ }
1094
+
1095
+ /**
1096
+ * --- 仅修改过期时间不修改值 ---
1097
+ * @param key
1098
+ * @param ttl
1099
+ * @param etc
1100
+ */
1101
+ public async expire(key: string, ttl: number, etc: types.IConfigKv): Promise<boolean> {
1102
+ this.refreshLast();
1103
+ try {
1104
+ return await this._link.expire(etc.pre + key, ttl);
1105
+ }
1106
+ catch {
1107
+ return false;
1108
+ }
1109
+ }
1110
+
1111
+ /**
1112
+ * --- 获取服务器上的所有 key 列表 ---
1113
+ * @param pattern
1114
+ */
1115
+ public async keys(pattern: string, etc: types.IConfigKv): Promise<string[] | false> {
1116
+ this.refreshLast();
1117
+ try {
1118
+ const r = await this._link.keys(etc.pre + pattern);
1119
+ const pl = etc.pre.length;
1120
+ if (pl > 0) {
1121
+ for (let k = 0; k < r.length; ++k) {
1122
+ r[k] = r[k].slice(pl);
1123
+ }
1124
+ }
1125
+ return r;
1126
+ }
1127
+ catch {
1128
+ return false;
1129
+ }
1130
+ }
1131
+
1132
+ /**
1133
+ * --- 根据条件获取服务器上的 keys ---
1134
+ * @param cursor
1135
+ * @param pattern 例如 *
1136
+ * @param count 获取的条数
1137
+ * @param etc
1138
+ */
1139
+ public async scan(
1140
+ cursor: number,
1141
+ pattern: string,
1142
+ count: number,
1143
+ etc: types.IConfigKv
1144
+ ): Promise<redis.IScanResult<string> | false> {
1145
+ this.refreshLast();
1146
+ try {
1147
+ const r = await this._link.scan(cursor, etc.pre + pattern, count);
1148
+ for (let i = 0; i < r.items.length; ++i) {
1149
+ r.items[i] = r.items[i].slice(this._etc.pre.length);
1150
+ }
1151
+ return r;
1152
+ }
1153
+ catch {
1154
+ return false;
1155
+ }
1156
+ }
1157
+
1158
+ /**
1159
+ * --- 清除当前所选数据库的所有内容 ---
1160
+ */
1161
+ public async flushDb(): Promise<boolean> {
1162
+ this.refreshLast();
1163
+ try {
1164
+ await this._link.flushDb();
1165
+ return true;
1166
+ }
1167
+ catch {
1168
+ return false;
1169
+ }
1170
+ }
1171
+
1172
+ /**
1173
+ * --- 发送 ping ---
1174
+ */
1175
+ public async ping(): Promise<false | string> {
1176
+ this.refreshLast();
1177
+ try {
1178
+ return (await this._link.ping()) === 'PONG' ? 'PONG' : false;
1179
+ }
1180
+ catch {
1181
+ return false;
1182
+ }
1183
+ }
1184
+
1185
+ /**
1186
+ * --- 设置哈希表值 ---
1187
+ * @param key key 名
1188
+ * @param field 字段名
1189
+ * @param val 值
1190
+ * @param mod 空,nx(key不存在才建立)
1191
+ * @param etc
1192
+ */
1193
+ public async hSet(key: string, field: string, val: object | string | number, mod: '' | 'nx', etc: types.IConfigKv): Promise<boolean> {
1194
+ this.refreshLast();
1195
+ try {
1196
+ if (typeof val !== 'string') {
1197
+ val = text.stringifyJson(val);
1198
+ }
1199
+ if (mod === 'nx') {
1200
+ return await this._link.hSetNX(etc.pre + key, field, val);
1201
+ }
1202
+ else {
1203
+ return await this._link.hSet(etc.pre + key, field, val);
1204
+ }
1205
+ }
1206
+ catch {
1207
+ return false;
1208
+ }
1209
+ }
1210
+
1211
+ /**
1212
+ * --- 批量设置哈希值 ---
1213
+ * @param key key 名
1214
+ * @param rows key / val 数组
1215
+ * @param etc
1216
+ */
1217
+ public async hMSet(
1218
+ key: string,
1219
+ rows: Record<string, object | string | number>,
1220
+ etc: types.IConfigKv
1221
+ ): Promise<boolean> {
1222
+ this.refreshLast();
1223
+ try {
1224
+ for (const i in rows) {
1225
+ const val = rows[i];
1226
+ if (typeof val === 'object') {
1227
+ rows[i] = text.stringifyJson(val);
1228
+ }
1229
+ }
1230
+ await this._link.hMSet(etc.pre + key, rows as Record<string, string | number>);
1231
+ return true;
1232
+ }
1233
+ catch {
1234
+ return false;
1235
+ }
1236
+ }
1237
+
1238
+ /**
1239
+ * --- 获取哈希值 ---
1240
+ * @param key
1241
+ * @param field
1242
+ * @param etc
1243
+ */
1244
+ public async hGet(key: string, field: string, etc: types.IConfigKv): Promise<string | null> {
1245
+ this.refreshLast();
1246
+ try {
1247
+ return await this._link.hGet(etc.pre + key, field);
1248
+ }
1249
+ catch {
1250
+ return null;
1251
+ }
1252
+ }
1253
+
1254
+ /**
1255
+ * --- 获取哈希 json 对象 ---
1256
+ * @param key
1257
+ * @param field
1258
+ * @param etc
1259
+ */
1260
+ public async hGetJson(key: string, field: string, etc: types.IConfigKv): Promise<any | null> {
1261
+ const v = await this.hGet(key, field, etc);
1262
+ if (v === null) {
1263
+ return null;
1264
+ }
1265
+ const r = text.parseJson(v);
1266
+ return r === false ? null : v;
1267
+ }
1268
+
1269
+ /**
1270
+ * --- 批量获取哈希值 ---
1271
+ * @param key
1272
+ * @param fields
1273
+ * @param etc
1274
+ */
1275
+ public async hMGet(key: string, fields: string[], etc: types.IConfigKv): Promise<Record<string, string | null>> {
1276
+ this.refreshLast();
1277
+ try {
1278
+ return await this._link.hMGet(etc.pre + key, fields);
1279
+ }
1280
+ catch {
1281
+ const rtn: Record<string, string | null> = {};
1282
+ for (const field of fields) {
1283
+ rtn[field] = null;
1284
+ }
1285
+ return rtn;
1286
+ }
1287
+ }
1288
+
1289
+ /**
1290
+ * --- 批量获取哈希键值对 ---
1291
+ * @param key
1292
+ * @param etc
1293
+ */
1294
+ public async hGetAll(key: string, etc: types.IConfigKv): Promise<Record<string, string | null> | null> {
1295
+ this.refreshLast();
1296
+ try {
1297
+ return await this._link.hGetAll(etc.pre + key);
1298
+ }
1299
+ catch {
1300
+ return null;
1301
+ }
1302
+ }
1303
+
1304
+ /**
1305
+ * --- 删除哈希键 ---
1306
+ * @param key
1307
+ * @param fields 值序列
1308
+ * @param etc
1309
+ */
1310
+ public async hDel(key: string, fields: string | string[], etc: types.IConfigKv): Promise<number> {
1311
+ this.refreshLast();
1312
+ try {
1313
+ return await this._link.hDel(etc.pre + key, fields);
1314
+ }
1315
+ catch {
1316
+ return 0;
1317
+ }
1318
+ }
1319
+
1320
+ /**
1321
+ * --- 判断哈希字段是否存在 ---
1322
+ * @param key
1323
+ * @param field
1324
+ * @param etc
1325
+ */
1326
+ public async hExists(key: string, field: string, etc: types.IConfigKv): Promise<boolean> {
1327
+ this.refreshLast();
1328
+ try {
1329
+ return await this._link.hExists(etc.pre + key, field);
1330
+ }
1331
+ catch {
1332
+ return false;
1333
+ }
1334
+ }
1335
+
1336
+ /**
1337
+ * --- 设置哈希自增自减 ---
1338
+ * @param key key
1339
+ * @param field 字段
1340
+ * @param increment 正数或负数,整数或浮点
1341
+ * @param etc
1342
+ */
1343
+ public async hIncr(key: string, field: string, increment: number, etc: types.IConfigKv): Promise<number> {
1344
+ this.refreshLast();
1345
+ try {
1346
+ if (Number.isInteger(increment)) {
1347
+ return await this._link.hIncr(etc.pre + key, field, increment);
1348
+ }
1349
+ else {
1350
+ return await this._link.hIncrByFloat(etc.pre + key, field, increment);
1351
+ }
1352
+ }
1353
+ catch {
1354
+ return 0;
1355
+ }
1356
+ }
1357
+
1358
+ /**
1359
+ * --- 获取哈希所有字段 ---
1360
+ * @param key
1361
+ * @param etc
1362
+ */
1363
+ public async hKeys(key: string, etc: types.IConfigKv): Promise<string[]> {
1364
+ this.refreshLast();
1365
+ try {
1366
+ return await this._link.hKeys(etc.pre + key);
1367
+ }
1368
+ catch {
1369
+ return [];
1370
+ }
1371
+ }
1372
+
1373
+ public async lPush(key: string, values: Array<string | Buffer>, etc: types.IConfigKv): Promise<number> {
1374
+ this.refreshLast();
1375
+ try {
1376
+ return await this._link.lPush(etc.pre + key, values);
1377
+ }
1378
+ catch {
1379
+ return 0;
1380
+ }
1381
+ }
1382
+
1383
+ public async rPush(key: string, values: Array<string | Buffer>, etc: types.IConfigKv): Promise<number> {
1384
+ this.refreshLast();
1385
+ try {
1386
+ return await this._link.rPush(etc.pre + key, values);
1387
+ }
1388
+ catch {
1389
+ return 0;
1390
+ }
1391
+ }
1392
+
1393
+ public async bLMove(sourceKey: string, destKey: string, soo: 'LEFT' | 'RIGHT', deo: 'LEFT' | 'RIGHT', timeout: number, etc: types.IConfigKv): Promise<string | null> {
1394
+ this.refreshLast();
1395
+ try {
1396
+ return await this._link.bLMove(etc.pre + sourceKey, etc.pre + destKey, soo, deo, timeout);
1397
+ }
1398
+ catch {
1399
+ return null;
1400
+ }
1401
+ }
1402
+
1403
+ public async lPop(key: string, etc: types.IConfigKv): Promise<string | null> {
1404
+ this.refreshLast();
1405
+ try {
1406
+ return await this._link.lPop(etc.pre + key);
1407
+ }
1408
+ catch {
1409
+ return null;
1410
+ }
1411
+ }
1412
+
1413
+ public async rPop(key: string, etc: types.IConfigKv): Promise<string | null> {
1414
+ this.refreshLast();
1415
+ try {
1416
+ return await this._link.rPop(etc.pre + key);
1417
+ }
1418
+ catch {
1419
+ return null;
1420
+ }
1421
+ }
1422
+
1423
+ public async bRPop(key: string | string[], timeout: number, etc: types.IConfigKv): Promise<Record<string, string>> {
1424
+ this.refreshLast();
1425
+ try {
1426
+ if (typeof key === 'string') {
1427
+ key = [key];
1428
+ }
1429
+ return await this._link.bRPop(key.map(item => etc.pre + item), timeout);
1430
+ }
1431
+ catch {
1432
+ return {};
1433
+ }
1434
+ }
1435
+
1436
+ public async lRange(key: string, start: number, stop: number, etc: types.IConfigKv): Promise<string[]> {
1437
+ this.refreshLast();
1438
+ try {
1439
+ return await this._link.lRange(etc.pre + key, start, stop);
1440
+ }
1441
+ catch {
1442
+ return [];
1443
+ }
1444
+ }
1445
+
1446
+ public async lLen(key: string, etc: types.IConfigKv): Promise<number> {
1447
+ this.refreshLast();
1448
+ try {
1449
+ return await this._link.lLen(etc.pre + key);
1450
+ }
1451
+ catch {
1452
+ return 0;
1453
+ }
1454
+ }
1455
+
1456
+ }
1457
+
1458
+ /**
1459
+ * --- 获取 Kv Pool 对象 ---
1460
+ * @param etc 配置信息可留空
1461
+ */
1462
+ export function get(ctr: ctr.Ctr, etc?: types.IConfigKv): Pool {
1463
+ if (!etc) {
1464
+ etc = ctr.getPrototype('_config').kv;
1465
+ }
1466
+ return new Pool(ctr, etc);
1467
+ }
1468
+
1469
+ /**
1470
+ * --- 获取当前连接池中所有连接的信息 ---
1471
+ */
1472
+ export function getConnectionList(): IConnectionInfo[] {
1473
+ const list = [];
1474
+ for (let i = 0; i < connections.length; ++i) {
1475
+ const connection = connections[i];
1476
+ const etc = connection.getEtc();
1477
+ list.push({
1478
+ 'id': i,
1479
+ 'last': connection.getLast(),
1480
+ 'host': etc.host,
1481
+ 'port': etc.port,
1482
+ 'index': etc.index,
1483
+
1484
+ 'lost': connection.isLost(),
1485
+ 'using': connection.isUsing()
1486
+ });
1487
+ }
1488
+ return list;
1489
+ }