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.
@@ -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
- writerPubkey = this.themeAuthor;
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, ...opts: any[]): Promise<void>;
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
- recs = await this.pullThreadDeal(id);
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
- break;
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$$$indexkey2: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
- * 获取指定键的值列表,包括所有用户写入的值,可以用在类似排名这些需要多人数据汇总的场景,key为场景名称,各个用户写入的值为各自在该场景下的内容
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
- limit: number,
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
- limit: number,
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
- const res = await kvdb.set(key, value, indexs, vaccount);
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
- * 获取指定键的值列表,包括所有用户写入的值,可以用在类似排名这些需要多人数据汇总的场景,key为场景名称,各个用户写入的值为各自在该场景下的内容
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
- limit: number,
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, vaccount);
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
- limit: number,
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, indexValue, limit,seekKey, direction,offset, vaccount);
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-dc-api",
3
- "version": "0.0.64",
3
+ "version": "0.0.65",
4
4
  "description": "web相关的dcapi",
5
5
  "type": "module",
6
6
  "browser": "dist/dc.min.js",