web-dc-api 0.0.46 → 0.0.48
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 +1 -1
- package/dist/index.d.ts +24 -2
- package/dist/index.esm.js +1 -1
- package/lib/common/chain.ts +10 -10
- package/lib/common/define.ts +1 -1
- package/lib/common/service-worker.ts +19 -2
- package/lib/dc.ts +1 -1
- package/lib/implements/account/manager.ts +2 -2
- package/lib/implements/comment/client.ts +53 -1
- package/lib/implements/comment/manager.ts +19 -1
- package/lib/implements/threaddb/dbmanager.ts +28 -1
- package/lib/implements/threaddb/net/net.ts +90 -39
- package/lib/interfaces/util-interface.ts +15 -2
- package/lib/modules/auth-module.ts +16 -11
- package/lib/modules/util-module.ts +24 -1
- package/package.json +1 -1
package/lib/common/chain.ts
CHANGED
|
@@ -52,7 +52,7 @@ export class ChainUtil {
|
|
|
52
52
|
// 获取用户钱包信息
|
|
53
53
|
async getUserInfoWithAccount(account: string): Promise<User> {
|
|
54
54
|
const walletAccountStorage =
|
|
55
|
-
await this.dcchainapi?.query.dcNode.walletAccountStorage(account);
|
|
55
|
+
await (this.dcchainapi?.query as any).dcNode.walletAccountStorage(account);
|
|
56
56
|
if (!walletAccountStorage) {
|
|
57
57
|
throw new Error("walletAccountStorage is null");
|
|
58
58
|
}
|
|
@@ -67,7 +67,7 @@ export class ChainUtil {
|
|
|
67
67
|
}
|
|
68
68
|
if (userInfo?.parentAccount !== account) {
|
|
69
69
|
const parentWalletAccountStorage =
|
|
70
|
-
await this.dcchainapi?.query.dcNode.walletAccountStorage(
|
|
70
|
+
await (this.dcchainapi?.query as any).dcNode.walletAccountStorage(
|
|
71
71
|
userInfo?.parentAccount
|
|
72
72
|
);
|
|
73
73
|
if (!parentWalletAccountStorage) {
|
|
@@ -131,7 +131,7 @@ export class ChainUtil {
|
|
|
131
131
|
// 获取用户钱包信息
|
|
132
132
|
async getUserInfoWithNftHex(nftHexAccount: string): Promise<User> {
|
|
133
133
|
const walletAccount =
|
|
134
|
-
await this.dcchainapi?.query.dcNode.nftToWalletAccount(nftHexAccount);
|
|
134
|
+
await (this.dcchainapi?.query as any).dcNode.nftToWalletAccount(nftHexAccount);
|
|
135
135
|
if (!walletAccount || !walletAccount.toString()) {
|
|
136
136
|
throw new Error("walletAccount is null");
|
|
137
137
|
}
|
|
@@ -191,7 +191,7 @@ export class ChainUtil {
|
|
|
191
191
|
const accountHash = await sha256(accountBytes);
|
|
192
192
|
const nftHexAccount = "0x" + Buffer.from(accountHash).toString("hex");
|
|
193
193
|
const walletAccount =
|
|
194
|
-
await this.dcchainapi?.query.dcNode.nftToWalletAccount(nftHexAccount);
|
|
194
|
+
await (this.dcchainapi?.query as any).dcNode.nftToWalletAccount(nftHexAccount);
|
|
195
195
|
if (!walletAccount || !walletAccount.toString()) {
|
|
196
196
|
throw new Error("walletAccount is null");
|
|
197
197
|
}
|
|
@@ -200,7 +200,7 @@ export class ChainUtil {
|
|
|
200
200
|
|
|
201
201
|
// 获取所有文件存储节点
|
|
202
202
|
getObjNodes = async (cid: string): Promise<string[] | undefined> => {
|
|
203
|
-
const fileInfo = (await this.dcchainapi?.query.dcNode.files(cid)) || null;
|
|
203
|
+
const fileInfo = (await (this.dcchainapi?.query as any).dcNode.files(cid)) || null;
|
|
204
204
|
const fileInfoJSON = fileInfo?.toJSON();
|
|
205
205
|
if (
|
|
206
206
|
!fileInfoJSON ||
|
|
@@ -226,7 +226,7 @@ export class ChainUtil {
|
|
|
226
226
|
|
|
227
227
|
// 链上查询节点信息
|
|
228
228
|
// getDcNodeAddr = async (peerid: string) => {
|
|
229
|
-
// const peerInfo = await this.dcchainapi?.query.dcNode.peers(peerid);
|
|
229
|
+
// const peerInfo = await (this.dcchainapi?.query as any).dcNode.peers(peerid);
|
|
230
230
|
// const peerInfoJson = peerInfo?.toJSON();
|
|
231
231
|
// if (
|
|
232
232
|
// !peerInfoJson ||
|
|
@@ -264,7 +264,7 @@ export class ChainUtil {
|
|
|
264
264
|
// peerid: 节点的peerid
|
|
265
265
|
// 直接连接节点的地址
|
|
266
266
|
getDcNodeWebrtcDirectAddr = async (peerid: string): Promise<Multiaddr | null> => {
|
|
267
|
-
const peerInfo = await this.dcchainapi?.query.dcNode.peers(peerid);
|
|
267
|
+
const peerInfo = await (this.dcchainapi?.query as any).dcNode.peers(peerid);
|
|
268
268
|
const peerInfoJson = peerInfo?.toJSON();
|
|
269
269
|
if (
|
|
270
270
|
!peerInfoJson ||
|
|
@@ -288,7 +288,7 @@ export class ChainUtil {
|
|
|
288
288
|
|
|
289
289
|
// 链上查询节点列表
|
|
290
290
|
getDcNodeList = async (): Promise<string[]> => {
|
|
291
|
-
const peerList = await this.dcchainapi?.query.dcNode.onlineNodesAddress();
|
|
291
|
+
const peerList = await (this.dcchainapi?.query as any).dcNode.onlineNodesAddress();
|
|
292
292
|
const peerListJson = peerList?.toJSON();
|
|
293
293
|
console.log(
|
|
294
294
|
"peerListJson================================================",
|
|
@@ -319,7 +319,7 @@ export class ChainUtil {
|
|
|
319
319
|
return [null, new Error("dcchainapi is not initialized")];
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
const fileInfo = await this.dcchainapi.query.dcNode.files(cid);
|
|
322
|
+
const fileInfo = await (this.dcchainapi.query as any).dcNode.files(cid);
|
|
323
323
|
|
|
324
324
|
if (!fileInfo || fileInfo.isEmpty) {
|
|
325
325
|
return [null, new Error(`File with CID ${cid} not found`)];
|
|
@@ -426,7 +426,7 @@ export class ChainUtil {
|
|
|
426
426
|
}
|
|
427
427
|
const appIdBytes = new TextEncoder().encode(appId);
|
|
428
428
|
const appIdHex = "0x" + Buffer.from(appIdBytes).toString("hex");
|
|
429
|
-
const appInfoStr = await this.dcchainapi?.query.dcNode.appsInfo(appIdHex);
|
|
429
|
+
const appInfoStr = await (this.dcchainapi?.query as any).dcNode.appsInfo(appIdHex);
|
|
430
430
|
if (!appInfoStr || appInfoStr.isEmpty) {
|
|
431
431
|
throw new Error(`App info for ${appId} not found`);
|
|
432
432
|
}
|
package/lib/common/define.ts
CHANGED
|
@@ -28,13 +28,30 @@ export async function registerServiceWorker(fileOps?: IFileOperations, swUrl: st
|
|
|
28
28
|
: '/sw.js';
|
|
29
29
|
|
|
30
30
|
const registration = await navigator.serviceWorker.register(swPath);
|
|
31
|
-
|
|
31
|
+
// 让等待中的 SW 立即成为激活态
|
|
32
|
+
if (registration.waiting) {
|
|
33
|
+
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
|
34
|
+
}
|
|
35
|
+
// 有新 SW 安装后自动接管
|
|
36
|
+
registration.addEventListener('updatefound', () => {
|
|
37
|
+
const nw = registration.installing;
|
|
38
|
+
if (!nw) return;
|
|
39
|
+
nw.addEventListener('statechange', () => {
|
|
40
|
+
if (nw.state === 'installed' && navigator.serviceWorker.controller) {
|
|
41
|
+
nw.postMessage({ type: 'SKIP_WAITING' });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
32
45
|
// 设置消息监听器处理IPFS资源请求
|
|
33
46
|
navigator.serviceWorker.addEventListener('message', async (event) => {
|
|
34
47
|
if (event.data && event.data.type === 'ipfs-fetch') {
|
|
35
48
|
await handleIpfsRequest(event.data, event.ports[0]!, fileOps);
|
|
36
49
|
}
|
|
37
50
|
});
|
|
51
|
+
// 控制器变化(新 SW 接管)
|
|
52
|
+
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
53
|
+
location.reload();
|
|
54
|
+
});
|
|
38
55
|
|
|
39
56
|
return registration;
|
|
40
57
|
} catch (error) {
|
|
@@ -51,7 +68,7 @@ export async function registerServiceWorker(fileOps?: IFileOperations, swUrl: st
|
|
|
51
68
|
* @param port 消息端口
|
|
52
69
|
* @param fileOps 文件操作对象
|
|
53
70
|
*/
|
|
54
|
-
async function handleIpfsRequest(
|
|
71
|
+
export async function handleIpfsRequest(
|
|
55
72
|
data: { id: string, pathname: string, range?: string },
|
|
56
73
|
port: MessagePort,
|
|
57
74
|
fileOps?: IFileOperations
|
package/lib/dc.ts
CHANGED
|
@@ -82,7 +82,7 @@ export class DC implements DCContext {
|
|
|
82
82
|
this.dcChain = new ChainUtil();
|
|
83
83
|
this.dcutil = new DcUtil(this.dcChain);
|
|
84
84
|
// //todo 发布注释 remove
|
|
85
|
-
|
|
85
|
+
// this.dcutil.defaultPeerId= "12D3KooWEGzh4AcbJrfZMfQb63wncBUpscMEEyiMemSWzEnjVCPf";
|
|
86
86
|
// //todo remove end
|
|
87
87
|
this.appInfo = options.appInfo || ({} as APPInfo);
|
|
88
88
|
this.accountInfo = {} as AccountInfo;
|
|
@@ -413,7 +413,7 @@ export class AccountManager {
|
|
|
413
413
|
const accountHash = await sha256(accountBytes);
|
|
414
414
|
const nftHexAccount = "0x" + Buffer.from(accountHash).toString("hex");
|
|
415
415
|
const walletAccount =
|
|
416
|
-
await this.chainUtil.dcchainapi?.query.dcNode.nftToWalletAccount(
|
|
416
|
+
await (this.chainUtil.dcchainapi?.query as any).dcNode.nftToWalletAccount(
|
|
417
417
|
nftHexAccount
|
|
418
418
|
);
|
|
419
419
|
// 比较公钥
|
|
@@ -441,7 +441,7 @@ export class AccountManager {
|
|
|
441
441
|
const accountHash = await sha256(accountBytes);
|
|
442
442
|
const nftHexAccount = "0x" + Buffer.from(accountHash).toString("hex");
|
|
443
443
|
const walletAccount =
|
|
444
|
-
await this.chainUtil.dcchainapi?.query.dcNode.nftToWalletAccount(
|
|
444
|
+
await (this.chainUtil.dcchainapi?.query as any).dcNode.nftToWalletAccount(
|
|
445
445
|
nftHexAccount
|
|
446
446
|
);
|
|
447
447
|
if (!walletAccount.toString()) {
|
|
@@ -94,7 +94,59 @@ export class CommentClient {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
async getUserOffChainUsedInfo(
|
|
98
|
+
vaccount: string = ""
|
|
99
|
+
): Promise<dcnet.pb.GetUserOffChainUsedInfoReply> {
|
|
100
|
+
const message = new dcnet.pb.GetUserOffChainUsedInfoRequest({});
|
|
101
|
+
message.vaccount = new TextEncoder().encode(vaccount);
|
|
102
|
+
const messageBytes =
|
|
103
|
+
dcnet.pb.GetUserOffChainUsedInfoRequest.encode(message).finish();
|
|
104
|
+
try {
|
|
105
|
+
const grpcClient = new Libp2pGrpcClient(
|
|
106
|
+
this.client.p2pNode,
|
|
107
|
+
this.client.peerAddr,
|
|
108
|
+
this.client.token,
|
|
109
|
+
this.client.protocol
|
|
110
|
+
);
|
|
111
|
+
const reply = await grpcClient.unaryCall(
|
|
112
|
+
"/dcnet.pb.Service/GetUserOffChainUsedInfo",
|
|
113
|
+
messageBytes,
|
|
114
|
+
30000
|
|
115
|
+
);
|
|
116
|
+
const decoded = dcnet.pb.GetUserOffChainUsedInfoReply.decode(reply);
|
|
117
|
+
return decoded;
|
|
118
|
+
} catch (error: any) {
|
|
119
|
+
if (error.message.indexOf(Errors.INVALID_TOKEN.message) != -1) {
|
|
120
|
+
// try to get token
|
|
121
|
+
const token = await this.client.GetToken(
|
|
122
|
+
this.context.appInfo.appId || "",
|
|
123
|
+
this.context.getPublicKey().string(),
|
|
124
|
+
(payload: Uint8Array): Promise<Uint8Array> => {
|
|
125
|
+
return this.context.sign(payload);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
if (!token) {
|
|
129
|
+
throw new Error(Errors.INVALID_TOKEN.message);
|
|
130
|
+
}
|
|
131
|
+
const grpcClient = new Libp2pGrpcClient(
|
|
132
|
+
this.client.p2pNode,
|
|
133
|
+
this.client.peerAddr,
|
|
134
|
+
this.client.token,
|
|
135
|
+
this.client.protocol
|
|
136
|
+
);
|
|
137
|
+
const reply = await grpcClient.unaryCall(
|
|
138
|
+
"/dcnet.pb.Service/GetUserOffChainUsedInfo",
|
|
139
|
+
messageBytes,
|
|
140
|
+
30000
|
|
141
|
+
);
|
|
142
|
+
const decoded = dcnet.pb.GetUserOffChainUsedInfoReply.decode(reply);
|
|
143
|
+
console.log("GetUserOffChainUsedInfo decoded", decoded);
|
|
144
|
+
return decoded;
|
|
145
|
+
}
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
}
|
|
98
150
|
|
|
99
151
|
async addUserOffChainOpTimes(
|
|
100
152
|
pubkey: string,
|
|
@@ -310,7 +310,25 @@ export class CommentManager {
|
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
|
|
313
|
+
async getUserOffChainUsedInfo(vaccount: string = ""): Promise<[dcnet.pb.GetUserOffChainUsedInfoReply | null, Error | null]> {
|
|
314
|
+
try {
|
|
315
|
+
if (!this.accountBackupDc?.client) {
|
|
316
|
+
return [null, Errors.ErrNoDcPeerConnected];
|
|
317
|
+
}
|
|
318
|
+
if(!this.accountBackupDc?.nodeAddr){
|
|
319
|
+
return [null, Errors.ErrNoDcPeerConnected];
|
|
320
|
+
}
|
|
321
|
+
const commentClient = new CommentClient(
|
|
322
|
+
this.accountBackupDc.client,
|
|
323
|
+
this.dcNodeClient,
|
|
324
|
+
this.context
|
|
325
|
+
);
|
|
326
|
+
const res = await commentClient.getUserOffChainUsedInfo(vaccount);
|
|
327
|
+
return [res, null];
|
|
328
|
+
} catch (err) {
|
|
329
|
+
return [null, err as Error];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
314
332
|
|
|
315
333
|
async addUserOffChainOpTimes(
|
|
316
334
|
times: number,
|
|
@@ -1516,7 +1516,21 @@ async create(threadId: string, collectionName: string, jsonInstance: string): P
|
|
|
1516
1516
|
// if (jsonInstance.length > 100 * 1024) { // 100 KB
|
|
1517
1517
|
// throw new Error("instance too big");
|
|
1518
1518
|
// }
|
|
1519
|
+
|
|
1520
|
+
// 判断instance里面是否有_mod字段,存在则删除
|
|
1521
|
+
try {
|
|
1522
|
+
const instanceObj = JSON.parse(jsonInstance);
|
|
1523
|
+
if (instanceObj && typeof instanceObj === 'object' && '_mod' in instanceObj) {
|
|
1524
|
+
delete instanceObj._mod;
|
|
1525
|
+
jsonInstance = JSON.stringify(instanceObj);
|
|
1526
|
+
}
|
|
1527
|
+
} catch (err) {
|
|
1528
|
+
// JSON解析失败,保持原字符串不变
|
|
1529
|
+
console.warn('Failed to parse instance JSON, keeping original:', err);
|
|
1530
|
+
throw new Error("Invalid instance JSON format");
|
|
1531
|
+
}
|
|
1519
1532
|
try {
|
|
1533
|
+
|
|
1520
1534
|
// 解码threaddbID
|
|
1521
1535
|
const tID = ThreadID.fromString(threadId);
|
|
1522
1536
|
|
|
@@ -1587,7 +1601,20 @@ async save(threadId: string, collectionName: string, instance: string): Promise<
|
|
|
1587
1601
|
// 解码线程ID
|
|
1588
1602
|
const tID = ThreadID.fromString(threadId);
|
|
1589
1603
|
|
|
1590
|
-
|
|
1604
|
+
// 判断instance里面是否有_mod字段,存在则删除
|
|
1605
|
+
try {
|
|
1606
|
+
const instanceObj = JSON.parse(instance);
|
|
1607
|
+
if (instanceObj && typeof instanceObj === 'object' && '_mod' in instanceObj) {
|
|
1608
|
+
delete instanceObj._mod;
|
|
1609
|
+
instance = JSON.stringify(instanceObj);
|
|
1610
|
+
}
|
|
1611
|
+
} catch (err) {
|
|
1612
|
+
// JSON解析失败,保持原字符串不变
|
|
1613
|
+
console.warn('Failed to parse instance JSON, keeping original:', err);
|
|
1614
|
+
throw new Error("Invalid instance JSON format");
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
|
|
1591
1618
|
|
|
1592
1619
|
// 获取线程数据库
|
|
1593
1620
|
const threadDB = await this.getDB(tID);
|
|
@@ -484,10 +484,14 @@ async ensureUniqueLog(id: ThreadID, key?: Ed25519PrivKey | Ed25519PubKey, identi
|
|
|
484
484
|
*/
|
|
485
485
|
async pullThread(id: ThreadID): Promise<void> {
|
|
486
486
|
try {
|
|
487
|
+
let recs: Record<string, PeerRecords> = {};
|
|
487
488
|
const mutex = this.getMutexForThread(id.toString());
|
|
488
489
|
await mutex.acquire();
|
|
489
|
-
|
|
490
|
-
|
|
490
|
+
try {
|
|
491
|
+
recs = await this.pullThreadDeal(id);
|
|
492
|
+
} finally {
|
|
493
|
+
mutex.release();
|
|
494
|
+
}
|
|
491
495
|
|
|
492
496
|
const [connector, appConnected] = this.getConnector(id);
|
|
493
497
|
|
|
@@ -1148,66 +1152,113 @@ async getRecord( id: ThreadID, rid: CID): Promise<IRecord> {
|
|
|
1148
1152
|
|
|
1149
1153
|
// 确定需要获取的对等点数量
|
|
1150
1154
|
const needFetched = Math.min(2, peers.length);
|
|
1151
|
-
let
|
|
1155
|
+
let resolved = false; // 防止重复调用 resolve
|
|
1156
|
+
// 使用对象来保证引用一致性
|
|
1157
|
+
const fetchState = {
|
|
1158
|
+
fetchedPeers: 0
|
|
1159
|
+
};
|
|
1160
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
1161
|
+
// 设置超时
|
|
1162
|
+
const timeoutPromise = new Promise<void>((resolve) => {
|
|
1163
|
+
timeoutId = setTimeout(() => {
|
|
1164
|
+
resolve(); // 超时时总是resolve,在外部判断是否有足够的数据
|
|
1165
|
+
}, 30000);
|
|
1166
|
+
});
|
|
1152
1167
|
|
|
1153
1168
|
// 创建一个 Promise 在足够的对等点响应时解析
|
|
1154
1169
|
const fetchPromise = new Promise<void>((resolve) => {
|
|
1155
|
-
//
|
|
1170
|
+
// 立即检查是否满足条件
|
|
1156
1171
|
const checkComplete = () => {
|
|
1157
|
-
if (fetchedPeers >= needFetched) {
|
|
1172
|
+
if (!resolved && fetchState.fetchedPeers >= needFetched) {
|
|
1173
|
+
resolved = true;
|
|
1158
1174
|
resolve();
|
|
1159
1175
|
}
|
|
1160
1176
|
};
|
|
1161
1177
|
|
|
1162
1178
|
// 从每个对等点查询记录
|
|
1163
1179
|
const fetchPromises = peers.map(async (peerId) => {
|
|
1180
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
1181
|
+
// 为每个peer设置独立的超时
|
|
1182
|
+
const peerTimeout = new Promise<never>((_, reject) => {
|
|
1183
|
+
timeoutId = setTimeout(() => reject(new Error(`Peer ${peerId} timeout after 30s`)), 30000);
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1164
1186
|
try {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
// 更新收集器
|
|
1176
|
-
Object.entries(records).forEach(([logId, rs]) => {
|
|
1177
|
-
recordCollector.updateHeadCounter(logId, rs.counter);
|
|
1178
|
-
rs.records.forEach(record => {
|
|
1179
|
-
recordCollector.store(logId, record);
|
|
1180
|
-
});
|
|
1181
|
-
});
|
|
1182
|
-
|
|
1183
|
-
fetchedPeers++;
|
|
1184
|
-
|
|
1185
|
-
// 如果获取了记录并且达到了所需的对等点数量,则解析 Promise
|
|
1186
|
-
if (Object.keys(records).length > 0 && fetchedPeers >= needFetched) {
|
|
1187
|
-
resolve();
|
|
1188
|
-
}
|
|
1187
|
+
// 使用 Promise.race 为每个peer设置超时
|
|
1188
|
+
await Promise.race([
|
|
1189
|
+
(async () => {
|
|
1190
|
+
//连接到指定peerId,返回一个Client
|
|
1191
|
+
const client = await this.getClient(peerId);
|
|
1192
|
+
if (!client) {
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const dbClient = new DBClient(client,this.dc,this,this.logstore);
|
|
1189
1196
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1197
|
+
// 这里使用一个队列来控制并发,类似于 Go 代码中的 queueGetRecords
|
|
1198
|
+
const records = await dbClient.getRecordsFromPeer( req, serviceKey);
|
|
1199
|
+
|
|
1200
|
+
// 更新收集器
|
|
1201
|
+
Object.entries(records).forEach(([logId, rs]) => {
|
|
1202
|
+
recordCollector.updateHeadCounter(logId, rs.counter);
|
|
1203
|
+
rs.records.forEach(record => {
|
|
1204
|
+
recordCollector.store(logId, record);
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
fetchState.fetchedPeers++;
|
|
1209
|
+
|
|
1210
|
+
// 每次成功获取记录后立即检查是否满足条件
|
|
1211
|
+
checkComplete();
|
|
1212
|
+
})(),
|
|
1213
|
+
peerTimeout
|
|
1214
|
+
]);
|
|
1192
1215
|
} catch (err) {
|
|
1193
1216
|
console.error(`Error getting records from peer ${peerId}:`, err);
|
|
1217
|
+
// 错误不影响其他peer的执行
|
|
1218
|
+
} finally {
|
|
1219
|
+
// 清除定时器,防止内存泄漏
|
|
1220
|
+
if (timeoutId) {
|
|
1221
|
+
clearTimeout(timeoutId);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
// 使用 Promise.allSettled 替代 Promise.all,避免被单个失败阻塞
|
|
1227
|
+
// 同时设置额外的兜底检查机制
|
|
1228
|
+
Promise.allSettled(fetchPromises).then(() => {
|
|
1229
|
+
if (!resolved) {
|
|
1230
|
+
checkComplete();
|
|
1194
1231
|
}
|
|
1195
1232
|
});
|
|
1196
1233
|
|
|
1197
|
-
//
|
|
1198
|
-
|
|
1199
|
-
|
|
1234
|
+
// 添加定期检查机制,防止被某些未知问题阻塞
|
|
1235
|
+
const checkInterval = setInterval(() => {
|
|
1236
|
+
if (resolved) {
|
|
1237
|
+
clearInterval(checkInterval);
|
|
1238
|
+
} else {
|
|
1239
|
+
checkComplete();
|
|
1240
|
+
}
|
|
1241
|
+
}, 5000); // 每5秒检查一次
|
|
1242
|
+
|
|
1243
|
+
// 确保interval最终被清理
|
|
1244
|
+
setTimeout(() => clearInterval(checkInterval), 35000);
|
|
1200
1245
|
});
|
|
1201
1246
|
|
|
1202
|
-
|
|
1203
|
-
const timeoutPromise = new Promise<void>((_, reject) => {
|
|
1204
|
-
setTimeout(() => reject(new Error("Fetch records timeout")), 30000);
|
|
1205
|
-
});
|
|
1247
|
+
|
|
1206
1248
|
|
|
1207
1249
|
// 等待获取足够的记录或超时
|
|
1208
1250
|
await Promise.race([fetchPromise, timeoutPromise]);
|
|
1251
|
+
// 清除超时定时器
|
|
1252
|
+
if (timeoutId) {
|
|
1253
|
+
clearTimeout(timeoutId);
|
|
1254
|
+
}
|
|
1255
|
+
// 检查是否获取到足够的数据
|
|
1256
|
+
if (fetchState.fetchedPeers === 0) {
|
|
1257
|
+
throw new Error("Fetch records timeout: no peers responded");
|
|
1258
|
+
}
|
|
1209
1259
|
|
|
1210
|
-
//
|
|
1260
|
+
// 如果有数据就返回(不管是1个还是2个节点)
|
|
1261
|
+
console.log(`获取记录完成: ${fetchState.fetchedPeers}/${needFetched} 个节点响应`);
|
|
1211
1262
|
return recordCollector.list();
|
|
1212
1263
|
} catch (err) {
|
|
1213
1264
|
console.error("getRecords error:", err);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IAppInfo } from '../../lib/common/types/types';
|
|
2
2
|
import { SymmetricKey, Key as ThreadKey } from '../implements/threaddb/common/key';
|
|
3
|
+
import { IFileOperations } from './file-interface';
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
export interface IUtilOperations {
|
|
@@ -8,11 +9,23 @@ export interface IUtilOperations {
|
|
|
8
9
|
/**
|
|
9
10
|
* 设置应用信息,发布应用时调用
|
|
10
11
|
* @param appId string 应用ID
|
|
11
|
-
* @param domain
|
|
12
12
|
* @param fid
|
|
13
|
+
* @param domain
|
|
13
14
|
* @param owner
|
|
14
15
|
* @param rewarder
|
|
15
16
|
*/
|
|
16
|
-
setAppInfo(appId: string,
|
|
17
|
+
setAppInfo(appId: string,fid:string,domain:string,owner?: string,rewarder?: string): Promise<[boolean|null, Error | null]>;
|
|
17
18
|
getAppInfo(appId: string): Promise<[IAppInfo|null, Error | null]>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 处理IPFS请求
|
|
22
|
+
* @param data 请求数据
|
|
23
|
+
* @param port 消息端口
|
|
24
|
+
* @param fileOps 文件操作对象
|
|
25
|
+
*/
|
|
26
|
+
handleIpfsRequest(
|
|
27
|
+
data: { id: string, pathname: string, range?: string },
|
|
28
|
+
port: MessagePort,
|
|
29
|
+
fileOps?: IFileOperations
|
|
30
|
+
): Promise<void>
|
|
18
31
|
}
|
|
@@ -115,21 +115,26 @@ export class AuthModule implements DCModule, IAuthOperations {
|
|
|
115
115
|
if (userInfo == null) {
|
|
116
116
|
throw Errors.USER_NOT_BIND_TO_PEER;
|
|
117
117
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
118
|
+
//获取用户已经使用的评论空间和操作次数
|
|
119
|
+
const commentManager = new CommentManager(this.context);
|
|
120
|
+
const [offchainUsedInfo,resErr] = await commentManager.getUserOffChainUsedInfo()
|
|
121
|
+
if (resErr) {
|
|
122
|
+
throw resErr;
|
|
123
|
+
}
|
|
124
|
+
const leftSpace = offchainUsedInfo?userInfo.offchainSpace - Number(offchainUsedInfo.usedspace) :userInfo.offchainSpace;
|
|
125
|
+
const leftOptimes = offchainUsedInfo?userInfo.offchainOptimes - Number(offchainUsedInfo.usedtimes):userInfo.offchainOptimes;
|
|
126
|
+
logger.info(
|
|
127
|
+
`用户线下评论空间剩余: ${leftSpace} / ${userInfo.offchainSpace}, 线下操作次数剩余: ${leftOptimes} / ${userInfo.offchainOptimes}`
|
|
128
|
+
);
|
|
129
|
+
if (leftSpace < OffChainSpaceLimit || leftOptimes < OffChainOpTimesLimit) {
|
|
130
|
+
if (leftSpace < OffChainSpaceLimit) {
|
|
131
|
+
const [addOffChainBool, addOffChainError] = await commentManager.addUserOffChainSpace();
|
|
126
132
|
if (addOffChainError || !addOffChainBool) {
|
|
127
133
|
throw addOffChainError || new Error("addUserOffChainSpace error");
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
|
-
if (
|
|
131
|
-
const [addOffChainOpTimesBool, addOffChainOpTimesError] =
|
|
132
|
-
await commentManager.addUserOffChainOpTimes(OffChainOpTimes);
|
|
136
|
+
if (leftOptimes < OffChainOpTimesLimit) {
|
|
137
|
+
const [addOffChainOpTimesBool, addOffChainOpTimesError] =await commentManager.addUserOffChainOpTimes(OffChainOpTimes);
|
|
133
138
|
if (addOffChainOpTimesError || !addOffChainOpTimesBool) {
|
|
134
139
|
throw (
|
|
135
140
|
addOffChainOpTimesError || new Error("addUserOffChainSpace error")
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { IUtilOperations } from '../interfaces/util-interface';
|
|
3
3
|
import { SymmetricKey,Key as ThreadKey } from '../implements/threaddb/common/key';
|
|
4
4
|
import { CoreModuleName } from '../common/module-system';
|
|
5
|
-
import { DCContext } from '../interfaces';
|
|
5
|
+
import { DCContext, IFileOperations } from '../interfaces';
|
|
6
6
|
import { createLogger } from '../util/logger';
|
|
7
7
|
import { UtilManager } from '../../lib/implements/util/manager';
|
|
8
8
|
import { IAppInfo } from '../../lib/common/types/types';
|
|
9
9
|
import { Errors } from '../../lib/common/error';
|
|
10
|
+
import { handleIpfsRequest } from '../common/service-worker';
|
|
10
11
|
const logger = createLogger("UtilModule");
|
|
11
12
|
export class UtilModule implements IUtilOperations {
|
|
12
13
|
readonly moduleName = CoreModuleName.UTIL;
|
|
@@ -121,5 +122,27 @@ export class UtilModule implements IUtilOperations {
|
|
|
121
122
|
async shutdown(): Promise<void> {
|
|
122
123
|
this.initialized = false;
|
|
123
124
|
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 处理IPFS请求
|
|
129
|
+
* @param data 请求数据
|
|
130
|
+
* @param port 消息端口
|
|
131
|
+
* @param fileOps 文件操作对象
|
|
132
|
+
*/
|
|
133
|
+
async handleIpfsRequest(
|
|
134
|
+
data: { id: string, pathname: string, range?: string },
|
|
135
|
+
port: MessagePort,
|
|
136
|
+
fileOps?: IFileOperations
|
|
137
|
+
): Promise<void>{
|
|
138
|
+
if (!this.initialized) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
await handleIpfsRequest(data, port, fileOps);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
logger.error("页面处理IPFS请求失败:", error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
124
147
|
|
|
125
148
|
}
|