web-dc-api 0.0.64 → 0.0.65
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/dist/dc.min.js +2 -2
- package/dist/index.cjs.js +2 -2
- package/dist/index.d.ts +36 -10
- package/dist/index.esm.js +2 -2
- package/lib/common/dc-key/keyManager.ts +6 -6
- package/lib/implements/account/manager.ts +1 -1
- package/lib/implements/keyvalue/manager.ts +26 -2
- package/lib/implements/threaddb/core/core.ts +1 -1
- package/lib/implements/threaddb/dbmanager.ts +1 -1
- package/lib/implements/threaddb/net/net.ts +15 -14
- package/lib/interfaces/keyvalue-interface.ts +6 -11
- package/lib/modules/keyvalue-module.ts +37 -14
- package/lib/util/utils.ts +15 -2
- package/package.json +1 -1
|
@@ -348,13 +348,13 @@ export async function ed25519PublicKeyToCryptoKey(
|
|
|
348
348
|
.replace(pemHeader, '')
|
|
349
349
|
.replace(pemFooter, '')
|
|
350
350
|
.replace(/\s+/g, '');
|
|
351
|
-
keyData = Uint8Array.from(atob(pemContents), c => c.charCodeAt(0));
|
|
351
|
+
keyData = Uint8Array.from(atob(pemContents), c => c.charCodeAt(0)) as any;
|
|
352
352
|
} else {
|
|
353
353
|
// 处理 Base64/Hex 等编码的 RAW 格式
|
|
354
|
-
keyData = Uint8Array.from(atob(publicKey), c => c.charCodeAt(0));
|
|
354
|
+
keyData = Uint8Array.from(atob(publicKey), c => c.charCodeAt(0)) as any;
|
|
355
355
|
}
|
|
356
356
|
} else {
|
|
357
|
-
keyData = publicKey;
|
|
357
|
+
keyData = publicKey as any;
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
// 2. 检测密钥格式
|
|
@@ -396,13 +396,13 @@ export async function ed25519PrivateKeyToCryptoKey(
|
|
|
396
396
|
.replace(pemHeader, '')
|
|
397
397
|
.replace(pemFooter, '')
|
|
398
398
|
.replace(/\s+/g, '');
|
|
399
|
-
keyData = Uint8Array.from(atob(pemContents), c => c.charCodeAt(0));
|
|
399
|
+
keyData = Uint8Array.from(atob(pemContents), c => c.charCodeAt(0)) as any;
|
|
400
400
|
} else {
|
|
401
401
|
// 处理 Base64/Hex 编码的 RAW 格式
|
|
402
|
-
keyData = Uint8Array.from(atob(privateKey), c => c.charCodeAt(0));
|
|
402
|
+
keyData = Uint8Array.from(atob(privateKey), c => c.charCodeAt(0)) as any;
|
|
403
403
|
}
|
|
404
404
|
} else {
|
|
405
|
-
keyData = privateKey;
|
|
405
|
+
keyData = privateKey as any;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
// 2. 检测密钥格式
|
|
@@ -336,7 +336,7 @@ export class AccountManager {
|
|
|
336
336
|
// 准备签名数据
|
|
337
337
|
const serverPidBytes = new TextEncoder().encode(serverPid.toString());
|
|
338
338
|
const prikeyencryptHash = new Uint8Array(
|
|
339
|
-
await crypto.subtle.digest("SHA-256", prikeyencrypt)
|
|
339
|
+
await crypto.subtle.digest("SHA-256", prikeyencrypt as any)
|
|
340
340
|
);
|
|
341
341
|
|
|
342
342
|
// 区块高度转为小端字节序
|
|
@@ -16,6 +16,7 @@ import { Client } from "../../common/dcapi";
|
|
|
16
16
|
import { CommentType, Direction } from "../../common/define";
|
|
17
17
|
import { DCContext } from "../../../lib/interfaces/DCContext";
|
|
18
18
|
import { AnyExtensionField } from "protobufjs";
|
|
19
|
+
import { isArray } from "util";
|
|
19
20
|
|
|
20
21
|
//定义Key-Value存储的数据类型
|
|
21
22
|
export enum KeyValueStoreType { //存储主题类型 1:鉴权主题(读写都需要鉴权) 2:公共主题(默认所有用户可读,写需要鉴权)
|
|
@@ -75,8 +76,31 @@ export class KeyValueDB {
|
|
|
75
76
|
writerPubkey?: string,
|
|
76
77
|
vaccount?: string
|
|
77
78
|
): Promise<[string | null, Error | null]> {
|
|
78
|
-
if (!writerPubkey) {
|
|
79
|
-
|
|
79
|
+
if (!writerPubkey) {//没有指定写入者,则获取该key的最新值
|
|
80
|
+
const [values, err] = await this.getWithIndex("dc_timestamp_index", "",1, "", Direction.Reverse, 0,);
|
|
81
|
+
if (err) {
|
|
82
|
+
return [null, err];
|
|
83
|
+
}
|
|
84
|
+
if (!values) {
|
|
85
|
+
return [null, null];
|
|
86
|
+
}
|
|
87
|
+
const jsonValues = JSON.parse(values);
|
|
88
|
+
if (!Array.isArray(jsonValues)) {
|
|
89
|
+
return [values, null];
|
|
90
|
+
}
|
|
91
|
+
if (jsonValues.length == 0) {
|
|
92
|
+
return [null, null];
|
|
93
|
+
}
|
|
94
|
+
let keyValue ="";
|
|
95
|
+
try{
|
|
96
|
+
keyValue = JSON.stringify(jsonValues[0]);
|
|
97
|
+
}catch(err){
|
|
98
|
+
return [null, err instanceof Error ? err : new Error(String(err))];
|
|
99
|
+
}
|
|
100
|
+
if (!keyValue || keyValue.length == 0) {
|
|
101
|
+
keyValue = jsonValues[0];
|
|
102
|
+
}
|
|
103
|
+
return [keyValue, null];
|
|
80
104
|
}
|
|
81
105
|
return this.manager.getValueWithKey(
|
|
82
106
|
this.appId,
|
|
@@ -46,7 +46,7 @@ export interface INet {
|
|
|
46
46
|
getThread( id: ThreadID, ...opts: any[]): Promise<ThreadInfo>;
|
|
47
47
|
getThreadFromPeer( id: ThreadID, peer: PeerId, options: { token?: ThreadToken }): Promise<ThreadInfo>;
|
|
48
48
|
deleteThread( id: ThreadID, ...opts: any[]): Promise<void>;
|
|
49
|
-
pullThread( id: ThreadID,timeout: number,
|
|
49
|
+
pullThread( id: ThreadID,timeout: number, options: { token?: ThreadToken | undefined; multiPeersFlag?: boolean | undefined;} ): Promise<void>;
|
|
50
50
|
getPbLogs( id: ThreadID): Promise<[net_pb.pb.ILog[], IThreadInfo]>;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -1308,7 +1308,7 @@ async newDB(
|
|
|
1308
1308
|
async refreshDBFromDC(threadId:string): Promise<Error | null> {
|
|
1309
1309
|
try {
|
|
1310
1310
|
const tId = await this.decodeThreadId(threadId);
|
|
1311
|
-
await this.network.pullThread( tId,600);
|
|
1311
|
+
await this.network.pullThread( tId,600, { multiPeersFlag: true });
|
|
1312
1312
|
return null;
|
|
1313
1313
|
} catch (error) {
|
|
1314
1314
|
return error as Error;
|
|
@@ -571,13 +571,17 @@ export class Network implements Net {
|
|
|
571
571
|
/**
|
|
572
572
|
* Pull thread updates from peers
|
|
573
573
|
*/
|
|
574
|
-
async pullThread(id: ThreadID): Promise<void> {
|
|
574
|
+
async pullThread(id: ThreadID, timeout: number, options: { token?: ThreadToken | undefined; multiPeersFlag?: boolean | undefined;}): Promise<void> {
|
|
575
575
|
try {
|
|
576
576
|
let recs: Record<string, PeerRecords> = {};
|
|
577
577
|
const mutex = this.getMutexForThread(id.toString());
|
|
578
578
|
await mutex.acquire();
|
|
579
579
|
try {
|
|
580
|
-
|
|
580
|
+
if (options.multiPeersFlag) {
|
|
581
|
+
recs = await this.pullThreadDeal(id, options.multiPeersFlag );
|
|
582
|
+
}else{
|
|
583
|
+
recs = await this.pullThreadDeal(id, false );
|
|
584
|
+
}
|
|
581
585
|
} finally {
|
|
582
586
|
mutex.release();
|
|
583
587
|
}
|
|
@@ -701,7 +705,7 @@ export class Network implements Net {
|
|
|
701
705
|
/**
|
|
702
706
|
* Pull thread records from peers
|
|
703
707
|
*/
|
|
704
|
-
async pullThreadDeal(tid: ThreadID): Promise<Record<string, PeerRecords>> {
|
|
708
|
+
async pullThreadDeal(tid: ThreadID,multiPeersFlag: boolean=false): Promise<Record<string, PeerRecords>> {
|
|
705
709
|
try {
|
|
706
710
|
let [offsets, peers] = await this.threadOffsets(tid);
|
|
707
711
|
try {
|
|
@@ -725,7 +729,8 @@ export class Network implements Net {
|
|
|
725
729
|
peers,
|
|
726
730
|
tid,
|
|
727
731
|
offsets,
|
|
728
|
-
netPullingLimit
|
|
732
|
+
netPullingLimit,
|
|
733
|
+
multiPeersFlag
|
|
729
734
|
);
|
|
730
735
|
let continueFlag = false;
|
|
731
736
|
for (const [lidStr, rs] of Object.entries(recs)) {
|
|
@@ -1319,7 +1324,8 @@ export class Network implements Net {
|
|
|
1319
1324
|
peers: PeerId[],
|
|
1320
1325
|
tid: ThreadID,
|
|
1321
1326
|
offsets: Record<string, { id?: CID, counter: number }>,
|
|
1322
|
-
limit: number
|
|
1327
|
+
limit: number,
|
|
1328
|
+
multiPeersFlag: boolean = false
|
|
1323
1329
|
): Promise<Record<string, PeerRecords>> {
|
|
1324
1330
|
try {
|
|
1325
1331
|
// 构建请求
|
|
@@ -1343,23 +1349,18 @@ export class Network implements Net {
|
|
|
1343
1349
|
}
|
|
1344
1350
|
const dbClient = new DBClient(client,this.dc,this,this.logstore);
|
|
1345
1351
|
const records = await dbClient.getRecordsFromPeer( req, serviceKey);
|
|
1346
|
-
|
|
1347
|
-
// // 更新收集器
|
|
1348
|
-
// Object.entries(records).forEach(([logId, rs]) => {
|
|
1349
|
-
// recordCollector.updateHeadCounter(logId, rs.counter);
|
|
1350
|
-
// rs.records.forEach(record => {
|
|
1351
|
-
// recordCollector.store(logId, record);
|
|
1352
|
-
// });
|
|
1353
|
-
// });
|
|
1354
1352
|
for (const [logId, rs] of Object.entries(records)) {
|
|
1355
1353
|
await recordCollector.batchUpdate(logId, rs);
|
|
1356
1354
|
}
|
|
1357
1355
|
clearTimeout(timeout);
|
|
1358
|
-
|
|
1356
|
+
if(!multiPeersFlag){
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
1359
|
} catch (err) {
|
|
1360
1360
|
continue;
|
|
1361
1361
|
}
|
|
1362
1362
|
}
|
|
1363
|
+
|
|
1363
1364
|
return recordCollector.list();
|
|
1364
1365
|
} catch (err) {
|
|
1365
1366
|
console.error("getRecords error:", err);
|
|
@@ -60,7 +60,7 @@ export interface IKeyValueOperations {
|
|
|
60
60
|
* @param kvdb: KeyValueDB,
|
|
61
61
|
* @param key 键名
|
|
62
62
|
* @param value 值内容
|
|
63
|
-
* @param indexs 索引列表,格式为"indexkey1:value
|
|
63
|
+
* @param indexs 索引列表,格式为json字符串:[{key:"indexkey1",type:"string",value:"value"},{key:"indexkey2",type:"number", value:12}],设置索引后,后续查询可以通过索引快速定位
|
|
64
64
|
* @param vaccount 可选的虚拟账户
|
|
65
65
|
* @returns [是否设置成功, 错误信息]
|
|
66
66
|
*/
|
|
@@ -76,7 +76,7 @@ export interface IKeyValueOperations {
|
|
|
76
76
|
* 获取指定键的值
|
|
77
77
|
* @param kvdb: KeyValueDB,
|
|
78
78
|
* @param key 键名
|
|
79
|
-
* @param writerPubkey
|
|
79
|
+
* @param writerPubkey 写入者的公钥,如果不指定,则获取所有用户写入的该key的最新值
|
|
80
80
|
* @param vaccount 可选的虚拟账户
|
|
81
81
|
* @returns [值内容, 错误信息]
|
|
82
82
|
*/
|
|
@@ -89,7 +89,7 @@ export interface IKeyValueOperations {
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
*
|
|
92
|
+
* 获取指定键的值列表,按key的字典序排序
|
|
93
93
|
* @param kvdb: KeyValueDB,
|
|
94
94
|
* @param key 键名
|
|
95
95
|
* @param limit 返回结果数量限制
|
|
@@ -102,10 +102,7 @@ export interface IKeyValueOperations {
|
|
|
102
102
|
getValues(
|
|
103
103
|
kvdb: KeyValueDB,
|
|
104
104
|
key: string,
|
|
105
|
-
|
|
106
|
-
seekKey:string,
|
|
107
|
-
direction: Direction,
|
|
108
|
-
offset: number,
|
|
105
|
+
options: { limit?: number; seekKey?: string; direction?: Direction; offset?: number } ,
|
|
109
106
|
vaccount?: string
|
|
110
107
|
): Promise<[string | null, Error | null]>;
|
|
111
108
|
|
|
@@ -130,6 +127,7 @@ export interface IKeyValueOperations {
|
|
|
130
127
|
* @param kvdb: KeyValueDB,
|
|
131
128
|
* @param indexKey 索引键名
|
|
132
129
|
* @param indexValue 索引值
|
|
130
|
+
* @param type 索引值类型
|
|
133
131
|
* @param seekKey 查询起始键
|
|
134
132
|
* @param offset 结果偏移量
|
|
135
133
|
* @param direction 查询方向 (Forward/Backward)
|
|
@@ -141,10 +139,7 @@ export interface IKeyValueOperations {
|
|
|
141
139
|
kvdb: KeyValueDB,
|
|
142
140
|
indexKey: string,
|
|
143
141
|
indexValue: string,
|
|
144
|
-
|
|
145
|
-
seekKey: string,
|
|
146
|
-
offset: number,
|
|
147
|
-
direction: Direction,
|
|
142
|
+
options: { type?:string; limit?: number; seekKey?: string; direction?: Direction; offset?: number } ,
|
|
148
143
|
vaccount?: string
|
|
149
144
|
): Promise<[string | null, Error | null]>;
|
|
150
145
|
}
|
|
@@ -9,6 +9,7 @@ import { createLogger } from "../util/logger";
|
|
|
9
9
|
import { ThemeAuthInfo, ThemeComment } from "../common/types/types";
|
|
10
10
|
import { Direction } from "../common/define";
|
|
11
11
|
import { ThemePermission } from "../common/constants";
|
|
12
|
+
import {padPositiveInt30} from "../util/utils";
|
|
12
13
|
const logger = createLogger('KeyValueModule');
|
|
13
14
|
const indexkey_dckv = "indexkey_dckv"; //索引键名,keyvalue设置过程中key本身的索引键
|
|
14
15
|
/**
|
|
@@ -145,12 +146,13 @@ export class KeyValueModule implements DCModule, IKeyValueOperations {
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
|
|
148
150
|
|
|
149
151
|
async set(
|
|
150
152
|
kvdb: KeyValueDB,
|
|
151
153
|
key: string,
|
|
152
154
|
value: string,
|
|
153
|
-
indexs: string, //索引列表,格式为key1:value1$$$key2:value2
|
|
155
|
+
indexs: string, //索引列表,格式为json字符串:[{key:"indexkey1",type:"string",value:"value"},{key:"indexkey2",type:"number", value:12}],这里统一转换格式为key1:value1$$$key2:value2
|
|
154
156
|
vaccount?: string
|
|
155
157
|
): Promise<[boolean | null, Error | null]> {
|
|
156
158
|
const err = this.assertInitialized();
|
|
@@ -159,7 +161,23 @@ export class KeyValueModule implements DCModule, IKeyValueOperations {
|
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
try {
|
|
162
|
-
|
|
164
|
+
//进行格式转换
|
|
165
|
+
let strIndexs = "";
|
|
166
|
+
const indexArray = JSON.parse(indexs);
|
|
167
|
+
for (const index of indexArray) {
|
|
168
|
+
let indexValue = "";
|
|
169
|
+
if( index.type === "number" ){ //
|
|
170
|
+
indexValue = padPositiveInt30(index.value);
|
|
171
|
+
}else{
|
|
172
|
+
indexValue = index.value;
|
|
173
|
+
}
|
|
174
|
+
strIndexs += `${index.key}:${indexValue}$$$`;
|
|
175
|
+
}
|
|
176
|
+
//追加时间戳索引,保证每次写入的唯一性
|
|
177
|
+
const timestamp = Date.now();
|
|
178
|
+
const timestampStr = padPositiveInt30(timestamp);
|
|
179
|
+
strIndexs += `dc_timestamp_index:${timestampStr}`;
|
|
180
|
+
const res = await kvdb.set(key, value, strIndexs, vaccount);
|
|
163
181
|
return res;
|
|
164
182
|
} catch (error) {
|
|
165
183
|
logger.error(`设置set-value失败:`, error);
|
|
@@ -190,7 +208,7 @@ export class KeyValueModule implements DCModule, IKeyValueOperations {
|
|
|
190
208
|
|
|
191
209
|
|
|
192
210
|
/**
|
|
193
|
-
*
|
|
211
|
+
* 获取指定键的值列表
|
|
194
212
|
* @param kvdb: KeyValueDB,
|
|
195
213
|
* @param key 键名
|
|
196
214
|
* @param limit 返回结果数量限制
|
|
@@ -202,19 +220,19 @@ export class KeyValueModule implements DCModule, IKeyValueOperations {
|
|
|
202
220
|
async getValues(
|
|
203
221
|
kvdb: KeyValueDB,
|
|
204
222
|
key: string,
|
|
205
|
-
|
|
206
|
-
seekKey:string,
|
|
207
|
-
direction: Direction = Direction.Forward,
|
|
208
|
-
offset: number,
|
|
223
|
+
options: { limit?: number; seekKey?: string; direction?: Direction; offset?: number } ,
|
|
209
224
|
vaccount?: string
|
|
210
225
|
): Promise<[string | null, Error | null]> {
|
|
211
226
|
const err = this.assertInitialized();
|
|
212
227
|
if (err) {
|
|
213
228
|
return [null, err];
|
|
214
229
|
}
|
|
215
|
-
|
|
230
|
+
const limit = options.limit? options.limit: 10;
|
|
231
|
+
const seekKey = options.seekKey? options.seekKey: "";
|
|
232
|
+
const direction = options.direction? options.direction: Direction.Forward;
|
|
233
|
+
const offset = options.offset? options.offset: 0;
|
|
216
234
|
try {
|
|
217
|
-
const res = await kvdb.getWithIndex(indexkey_dckv, key, limit,seekKey,direction, offset,
|
|
235
|
+
const res = await kvdb.getWithIndex(indexkey_dckv, key, limit, seekKey, direction, offset, vaccount);
|
|
218
236
|
return res;
|
|
219
237
|
} catch (error) {
|
|
220
238
|
logger.error(`getValues失败:`, error);
|
|
@@ -246,18 +264,23 @@ export class KeyValueModule implements DCModule, IKeyValueOperations {
|
|
|
246
264
|
kvdb: KeyValueDB,
|
|
247
265
|
indexKey:string,
|
|
248
266
|
indexValue:string,
|
|
249
|
-
|
|
250
|
-
seekKey:string,
|
|
251
|
-
offset: number,
|
|
252
|
-
direction: Direction = Direction.Forward,
|
|
267
|
+
options: {type?:string; limit?: number; seekKey?: string; direction?: Direction; offset?: number } ,
|
|
253
268
|
vaccount?: string
|
|
254
269
|
): Promise<[string | null, Error | null]> {
|
|
255
270
|
const err = this.assertInitialized();
|
|
256
271
|
if (err) {
|
|
257
272
|
return [null, err];
|
|
258
273
|
}
|
|
274
|
+
const limit = options.limit? options.limit: 10;
|
|
275
|
+
const seekKey = options.seekKey? options.seekKey: "";
|
|
276
|
+
const direction = options.direction? options.direction: Direction.Forward;
|
|
277
|
+
const offset = options.offset? options.offset: 0;
|
|
278
|
+
let indexValueStr = "";
|
|
279
|
+
if( options.type === "number" ){ //
|
|
280
|
+
indexValueStr = padPositiveInt30(indexValue);
|
|
281
|
+
}
|
|
259
282
|
try {
|
|
260
|
-
const res = await kvdb.getWithIndex(indexKey,
|
|
283
|
+
const res = await kvdb.getWithIndex(indexKey, indexValueStr, limit,seekKey, direction,offset, vaccount);
|
|
261
284
|
return res;
|
|
262
285
|
} catch (error) {
|
|
263
286
|
logger.error(`getWithIndex失败:`, error);
|
package/lib/util/utils.ts
CHANGED
|
@@ -13,7 +13,7 @@ const TagBytes = 16;
|
|
|
13
13
|
|
|
14
14
|
// SHA-256 哈希计算
|
|
15
15
|
async function sha256(data: Uint8Array): Promise<Uint8Array> {
|
|
16
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
16
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data as any);
|
|
17
17
|
return new Uint8Array(hashBuffer);
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -232,6 +232,18 @@ function jsonStringify(value: any): string {
|
|
|
232
232
|
);
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
|
|
236
|
+
// 30 位零填充,不做数值运算,避免精度问题
|
|
237
|
+
function padPositiveInt30(v: string | number): string {
|
|
238
|
+
let s = String(v).trim();
|
|
239
|
+
if (!/^\d+$/.test(s)) throw new Error("只接受正整数数字串");
|
|
240
|
+
// 去掉前导 0(如果要允许 0,把正则改为 /^\d+$/ 并允许 s === '0')
|
|
241
|
+
s = s.replace(/^0+/, "") || "0";
|
|
242
|
+
if (s === "0") return "0".padStart(30, "0"); // 如需排除 0 可去掉这行并在上面报错
|
|
243
|
+
if (s.length > 30) throw new Error("超过 30 位宽度");
|
|
244
|
+
return s.padStart(30, "0");
|
|
245
|
+
}
|
|
246
|
+
|
|
235
247
|
export {
|
|
236
248
|
sha256,
|
|
237
249
|
getRandomBytes,
|
|
@@ -252,5 +264,6 @@ export {
|
|
|
252
264
|
loadKeyPair,
|
|
253
265
|
parseUint32,
|
|
254
266
|
hexToAscii,
|
|
255
|
-
jsonStringify
|
|
267
|
+
jsonStringify,
|
|
268
|
+
padPositiveInt30
|
|
256
269
|
};
|