@dignetwork/dig-sdk 0.0.1-alpha.18 → 0.0.1-alpha.180
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/DataIntegrityTree/DataIntegrityTree.d.ts +10 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.js +77 -10
- package/dist/DigNetwork/ContentServer.d.ts +22 -5
- package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
- package/dist/DigNetwork/ContentServer.js +226 -28
- package/dist/DigNetwork/DigNetwork.d.ts +15 -9
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +129 -189
- package/dist/DigNetwork/DigPeer.d.ts +8 -3
- package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
- package/dist/DigNetwork/DigPeer.js +72 -115
- package/dist/DigNetwork/IncentiveServer.d.ts.map +1 -1
- package/dist/DigNetwork/IncentiveServer.js +6 -5
- package/dist/DigNetwork/PropagationServer.d.ts +89 -33
- package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
- package/dist/DigNetwork/PropagationServer.js +625 -358
- package/dist/blockchain/DataStore.d.ts +25 -7
- package/dist/blockchain/DataStore.d.ts.map +1 -1
- package/dist/blockchain/DataStore.js +117 -159
- package/dist/blockchain/DataStoreSerializer.d.ts +1 -1
- package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.d.ts +95 -10
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +325 -115
- package/dist/blockchain/ServerCoin.d.ts +13 -5
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +89 -28
- package/dist/blockchain/StoreMonitorRegistry.d.ts +85 -0
- package/dist/blockchain/StoreMonitorRegistry.d.ts.map +1 -0
- package/dist/blockchain/StoreMonitorRegistry.js +238 -0
- package/dist/blockchain/Wallet.d.ts +2 -2
- package/dist/blockchain/Wallet.d.ts.map +1 -1
- package/dist/blockchain/Wallet.js +50 -18
- package/dist/blockchain/coins.d.ts +1 -2
- package/dist/blockchain/coins.d.ts.map +1 -1
- package/dist/blockchain/coins.js +1 -55
- package/dist/blockchain/index.d.ts +1 -0
- package/dist/blockchain/index.d.ts.map +1 -1
- package/dist/blockchain/index.js +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/ContentScanner.d.ts +63 -0
- package/dist/utils/ContentScanner.d.ts.map +1 -0
- package/dist/utils/ContentScanner.js +175 -0
- package/dist/utils/DigCache.d.ts +38 -0
- package/dist/utils/DigCache.d.ts.map +1 -0
- package/dist/utils/DigCache.js +236 -0
- package/dist/utils/Environment.d.ts +21 -0
- package/dist/utils/Environment.d.ts.map +1 -0
- package/dist/utils/Environment.js +93 -0
- package/dist/utils/FileCache.d.ts +1 -1
- package/dist/utils/FileCache.d.ts.map +1 -1
- package/dist/utils/FileCache.js +2 -2
- package/dist/utils/FileTransfer.d.ts +47 -0
- package/dist/utils/FileTransfer.d.ts.map +1 -0
- package/dist/utils/FileTransfer.js +209 -0
- package/dist/utils/NconfManager.d.ts.map +1 -1
- package/dist/utils/NconfManager.js +16 -7
- package/dist/utils/PeerRanker.d.ts +55 -0
- package/dist/utils/PeerRanker.d.ts.map +1 -0
- package/dist/utils/PeerRanker.js +186 -0
- package/dist/utils/StoreArchiveManager.d.ts +45 -0
- package/dist/utils/StoreArchiveManager.d.ts.map +1 -0
- package/dist/utils/StoreArchiveManager.js +153 -0
- package/dist/utils/Udi.d.ts +27 -0
- package/dist/utils/Udi.d.ts.map +1 -0
- package/dist/utils/Udi.js +145 -0
- package/dist/utils/config.d.ts +4 -3
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +13 -8
- package/dist/utils/directoryUtils.d.ts +16 -0
- package/dist/utils/directoryUtils.d.ts.map +1 -1
- package/dist/utils/directoryUtils.js +66 -0
- package/dist/utils/index.d.ts +5 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -1
- package/dist/utils/merkle.d.ts +2 -0
- package/dist/utils/merkle.d.ts.map +1 -0
- package/dist/utils/merkle.js +28 -0
- package/dist/utils/network.d.ts +2 -1
- package/dist/utils/network.d.ts.map +1 -1
- package/dist/utils/network.js +40 -15
- package/dist/utils/promiseUtils.d.ts +27 -0
- package/dist/utils/promiseUtils.d.ts.map +1 -0
- package/dist/utils/promiseUtils.js +69 -0
- package/dist/utils/ssl.js +2 -2
- package/package.json +31 -13
- package/dist/utils/deltaUtils.d.ts +0 -2
- package/dist/utils/deltaUtils.d.ts.map +0 -1
- package/dist/utils/deltaUtils.js +0 -83
|
@@ -38,9 +38,10 @@ const chia_root_resolver_1 = require("chia-root-resolver");
|
|
|
38
38
|
const chia_config_loader_1 = require("chia-config-loader");
|
|
39
39
|
const encryption_1 = require("../utils/encryption");
|
|
40
40
|
const buffer_1 = require("buffer");
|
|
41
|
-
const datalayer_driver_1 = require("datalayer-driver");
|
|
41
|
+
const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
|
|
42
42
|
const config_1 = require("../utils/config");
|
|
43
43
|
const FileCache_1 = require("../utils/FileCache");
|
|
44
|
+
const config_2 = require("../utils/config");
|
|
44
45
|
const KEYRING_FILE = "keyring.json";
|
|
45
46
|
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
46
47
|
exports.DEFAULT_FEE_COIN_COST = 64000000;
|
|
@@ -174,31 +175,62 @@ class Wallet {
|
|
|
174
175
|
const signature = (0, datalayer_driver_1.signMessage)(buffer_1.Buffer.from(message, "utf-8"), privateSyntheticKey);
|
|
175
176
|
return signature.toString("hex");
|
|
176
177
|
}
|
|
177
|
-
async verifyKeyOwnershipSignature(nonce, signature, publicKey) {
|
|
178
|
+
static async verifyKeyOwnershipSignature(nonce, signature, publicKey) {
|
|
178
179
|
const message = `Signing this message to prove ownership of key.\n\nNonce: ${nonce}`;
|
|
179
180
|
return (0, datalayer_driver_1.verifySignedMessage)(buffer_1.Buffer.from(signature, "hex"), buffer_1.Buffer.from(publicKey, "hex"), buffer_1.Buffer.from(message, "utf-8"));
|
|
180
181
|
}
|
|
181
182
|
async selectUnspentCoins(peer, coinAmount, feeBigInt, omitCoins = []) {
|
|
182
|
-
const cache = new FileCache_1.FileCache("reserved_coins");
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
const cache = new FileCache_1.FileCache("reserved_coins", config_2.USER_DIR_PATH);
|
|
184
|
+
const ownerPuzzleHash = await this.getOwnerPuzzleHash();
|
|
185
|
+
// Define a function to attempt selecting unspent coins
|
|
186
|
+
const trySelectCoins = async () => {
|
|
187
|
+
const now = Date.now();
|
|
188
|
+
const omitCoinIds = omitCoins.map((coin) => (0, datalayer_driver_1.getCoinId)(coin).toString("hex"));
|
|
189
|
+
// Update omitCoinIds with currently valid reserved coins
|
|
190
|
+
const cachedReservedCoins = cache.getCachedKeys();
|
|
191
|
+
cachedReservedCoins.forEach((coinId) => {
|
|
192
|
+
const reservation = cache.get(coinId);
|
|
193
|
+
if (reservation && reservation.expiry > now) {
|
|
194
|
+
if (!omitCoinIds.includes(coinId)) {
|
|
195
|
+
omitCoinIds.push(coinId);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
cache.delete(coinId);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
const coinsResp = await peer.getAllUnspentCoins(ownerPuzzleHash, config_1.MIN_HEIGHT, buffer_1.Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
|
|
203
|
+
const unspentCoins = coinsResp.coins.filter((coin) => !omitCoinIds.includes((0, datalayer_driver_1.getCoinId)(coin).toString("hex")));
|
|
204
|
+
const selectedCoins = (0, datalayer_driver_1.selectCoins)(unspentCoins, feeBigInt + coinAmount);
|
|
205
|
+
return selectedCoins;
|
|
206
|
+
};
|
|
207
|
+
let selectedCoins = [];
|
|
208
|
+
let retry = true;
|
|
209
|
+
while (retry) {
|
|
210
|
+
selectedCoins = await trySelectCoins();
|
|
211
|
+
if (selectedCoins.length > 0) {
|
|
212
|
+
// Coins have been successfully selected
|
|
213
|
+
retry = false;
|
|
190
214
|
}
|
|
191
215
|
else {
|
|
192
|
-
|
|
216
|
+
const now = Date.now();
|
|
217
|
+
// Check if there are any valid cached reserved coins left
|
|
218
|
+
const cachedReservedCoins = cache.getCachedKeys().filter((coinId) => {
|
|
219
|
+
const reservation = cache.get(coinId);
|
|
220
|
+
return reservation && reservation.expiry > now;
|
|
221
|
+
});
|
|
222
|
+
if (cachedReservedCoins.length > 0) {
|
|
223
|
+
// Wait 10 seconds and try again
|
|
224
|
+
console.log("No unspent coins available. Waiting 10 seconds...");
|
|
225
|
+
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
// No unspent coins and no reserved coins
|
|
229
|
+
throw new Error("No unspent coins available.");
|
|
230
|
+
}
|
|
193
231
|
}
|
|
194
|
-
});
|
|
195
|
-
const ownerPuzzleHash = await this.getOwnerPuzzleHash();
|
|
196
|
-
const coinsResp = await peer.getAllUnspentCoins(ownerPuzzleHash, config_1.MIN_HEIGHT, buffer_1.Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
|
|
197
|
-
const unspentCoins = coinsResp.coins.filter((coin) => !omitCoinIds.includes((0, datalayer_driver_1.getCoinId)(coin).toString("hex")));
|
|
198
|
-
const selectedCoins = (0, datalayer_driver_1.selectCoins)(unspentCoins, feeBigInt + coinAmount);
|
|
199
|
-
if (selectedCoins.length === 0) {
|
|
200
|
-
throw new Error("No unspent coins available.");
|
|
201
232
|
}
|
|
233
|
+
// Reserve the selected coins
|
|
202
234
|
selectedCoins.forEach((coin) => {
|
|
203
235
|
const coinId = (0, datalayer_driver_1.getCoinId)(coin).toString("hex");
|
|
204
236
|
cache.set(coinId, { coinId, expiry: Date.now() + CACHE_DURATION });
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { Peer,
|
|
1
|
+
import { Peer, CoinSpend } from "@dignetwork/datalayer-driver";
|
|
2
2
|
export declare const DEFAULT_FEE_COIN_COST = 64000000;
|
|
3
3
|
export declare const calculateFeeForCoinSpends: (peer: Peer, coinSpends: CoinSpend[] | null) => Promise<bigint>;
|
|
4
|
-
export declare const selectUnspentCoins: (peer: Peer, coinAmount: bigint, feeBigInt: bigint, omitCoins?: Coin[], walletName?: string) => Promise<Coin[]>;
|
|
5
4
|
export declare const isCoinSpendable: (peer: Peer, coinId: Buffer) => Promise<boolean>;
|
|
6
5
|
//# sourceMappingURL=coins.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coins.d.ts","sourceRoot":"","sources":["../../src/blockchain/coins.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,
|
|
1
|
+
{"version":3,"file":"coins.d.ts","sourceRoot":"","sources":["../../src/blockchain/coins.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,EAGJ,SAAS,EAEV,MAAM,8BAA8B,CAAC;AAMtC,eAAO,MAAM,qBAAqB,WAAa,CAAC;AAWhD,eAAO,MAAM,yBAAyB,SAC9B,IAAI,cACE,SAAS,EAAE,GAAG,IAAI,KAC7B,OAAO,CAAC,MAAM,CA4BhB,CAAC;AAEF,eAAO,MAAM,eAAe,SACpB,IAAI,UACF,MAAM,KACb,OAAO,CAAC,OAAO,CAWjB,CAAC"}
|
package/dist/blockchain/coins.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isCoinSpendable = exports.
|
|
4
|
-
const datalayer_driver_1 = require("datalayer-driver");
|
|
5
|
-
const Wallet_1 = require("./Wallet");
|
|
3
|
+
exports.isCoinSpendable = exports.calculateFeeForCoinSpends = exports.DEFAULT_FEE_COIN_COST = void 0;
|
|
6
4
|
const config_1 = require("../utils/config");
|
|
7
|
-
const FileCache_1 = require("../utils/FileCache");
|
|
8
5
|
exports.DEFAULT_FEE_COIN_COST = 64000000;
|
|
9
6
|
// Set cache expiration time (5 minutes)
|
|
10
7
|
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes in milliseconds
|
|
@@ -38,57 +35,6 @@ const calculateFeeForCoinSpends = async (peer, coinSpends) => {
|
|
|
38
35
|
*/
|
|
39
36
|
};
|
|
40
37
|
exports.calculateFeeForCoinSpends = calculateFeeForCoinSpends;
|
|
41
|
-
const selectUnspentCoins = async (peer, coinAmount, feeBigInt, omitCoins = [], walletName = "default") => {
|
|
42
|
-
// Initialize the cache for reserved coins
|
|
43
|
-
const cache = new FileCache_1.FileCache("reserved_coins");
|
|
44
|
-
// Get all cached reserved coins
|
|
45
|
-
const cachedReservedCoins = cache.getCachedKeys();
|
|
46
|
-
// Filter expired reserved coins and omit valid reservations
|
|
47
|
-
const now = Date.now();
|
|
48
|
-
const omitCoinIds = omitCoins.map((coin) => (0, datalayer_driver_1.getCoinId)(coin).toString("hex"));
|
|
49
|
-
const validReservedCoins = cachedReservedCoins.filter((coinId) => {
|
|
50
|
-
const reservation = cache.get(coinId);
|
|
51
|
-
if (reservation && reservation.expiry > now) {
|
|
52
|
-
// Valid reservation, add to omit list
|
|
53
|
-
omitCoinIds.push(coinId);
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
// Reservation expired, remove it
|
|
58
|
-
cache.delete(coinId);
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
// Fetch all unspent coins from the peer
|
|
63
|
-
const wallet = await Wallet_1.Wallet.load(walletName);
|
|
64
|
-
const ownerPuzzleHash = await wallet.getOwnerPuzzleHash();
|
|
65
|
-
const coinsResp = await peer.getAllUnspentCoins(ownerPuzzleHash, config_1.MIN_HEIGHT, Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
|
|
66
|
-
if (process.env.DIG_DEBUG === "1") {
|
|
67
|
-
console.log("Unspent Coins:", coinsResp); // Debugging
|
|
68
|
-
}
|
|
69
|
-
const unspentCoins = coinsResp.coins;
|
|
70
|
-
// Filter out the omitted coins
|
|
71
|
-
const filteredUnspentCoins = unspentCoins.filter((coin) => !omitCoinIds.includes((0, datalayer_driver_1.getCoinId)(coin).toString("hex")));
|
|
72
|
-
if (process.env.DIG_DEBUG === "1") {
|
|
73
|
-
console.log("Unspent Coins after filtering:", filteredUnspentCoins); // Debugging
|
|
74
|
-
}
|
|
75
|
-
// Select coins needed for the transaction
|
|
76
|
-
const selectedCoins = (0, datalayer_driver_1.selectCoins)(filteredUnspentCoins, feeBigInt + coinAmount);
|
|
77
|
-
if (process.env.DIG_DEBUG === "1") {
|
|
78
|
-
console.log("Selected Coins:", selectedCoins); // Debugging
|
|
79
|
-
}
|
|
80
|
-
// If no coins are selected, throw an error
|
|
81
|
-
if (selectedCoins.length === 0) {
|
|
82
|
-
throw new Error("No unspent coins available.");
|
|
83
|
-
}
|
|
84
|
-
// Cache the selected coins as reserved for the next 5 minutes
|
|
85
|
-
selectedCoins.forEach((coin) => {
|
|
86
|
-
const coinId = (0, datalayer_driver_1.getCoinId)(coin).toString("hex");
|
|
87
|
-
cache.set(coinId, { coinId, expiry: Date.now() + CACHE_DURATION });
|
|
88
|
-
});
|
|
89
|
-
return selectedCoins;
|
|
90
|
-
};
|
|
91
|
-
exports.selectUnspentCoins = selectUnspentCoins;
|
|
92
38
|
const isCoinSpendable = async (peer, coinId) => {
|
|
93
39
|
try {
|
|
94
40
|
const spent = await peer.isCoinSpent(coinId, config_1.MIN_HEIGHT, Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blockchain/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAa,gBAAgB,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blockchain/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAa,gBAAgB,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,wBAAwB,CAAC"}
|
package/dist/blockchain/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface DigConfig {
|
|
|
23
23
|
export interface RootHistoryItem {
|
|
24
24
|
root_hash: string;
|
|
25
25
|
timestamp: Number | undefined;
|
|
26
|
+
synced: boolean;
|
|
26
27
|
}
|
|
27
28
|
export interface DatFile {
|
|
28
29
|
root: string;
|
|
@@ -47,6 +48,7 @@ export interface ServerCoinData {
|
|
|
47
48
|
coin: CoinData;
|
|
48
49
|
createdAt: string;
|
|
49
50
|
epoch: number;
|
|
51
|
+
rootHash: string;
|
|
50
52
|
}
|
|
51
53
|
export interface IncentiveProgramData {
|
|
52
54
|
storeId: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE;QACL,CAAC,GAAG,EAAE,MAAM,GAAG;YACb,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class IllegalContentScanner
|
|
3
|
+
* Scans files for illegal content using ClamAV running in a Docker container and deletes files if flagged.
|
|
4
|
+
*/
|
|
5
|
+
export declare class IllegalContentScanner {
|
|
6
|
+
private clamAVContainerName;
|
|
7
|
+
private virusTotalApiKey?;
|
|
8
|
+
private openAiApiKey?;
|
|
9
|
+
/**
|
|
10
|
+
* @constructor
|
|
11
|
+
* @param clamAVContainerName The name of the Docker container running ClamAV (default: 'clamav').
|
|
12
|
+
*/
|
|
13
|
+
constructor(clamAVContainerName?: string);
|
|
14
|
+
/**
|
|
15
|
+
* Scans a file for illegal content using ClamAV, VirusTotal, and OpenAI Moderation API.
|
|
16
|
+
* If a service is missing its API key, the scan for that service is skipped.
|
|
17
|
+
* @param filePath The path of the file to scan.
|
|
18
|
+
* @returns {Promise<void>} A promise that resolves after the scan and potential deletion.
|
|
19
|
+
*/
|
|
20
|
+
scanFile(filePath: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Scans a file using ClamAV running in Docker.
|
|
23
|
+
* @param filePath The path of the file to scan.
|
|
24
|
+
* @returns {Promise<string>} The result of the ClamAV scan.
|
|
25
|
+
*/
|
|
26
|
+
private scanWithClamAV;
|
|
27
|
+
/**
|
|
28
|
+
* Submits a file to VirusTotal for scanning.
|
|
29
|
+
* @param filePath The path of the file to scan.
|
|
30
|
+
* @returns {Promise<any>} The result from VirusTotal.
|
|
31
|
+
*/
|
|
32
|
+
private scanWithVirusTotal;
|
|
33
|
+
/**
|
|
34
|
+
* Scans file content (text) using OpenAI's Moderation API.
|
|
35
|
+
* @param content The text content to scan.
|
|
36
|
+
* @returns {Promise<any>} The result from OpenAI's Moderation API.
|
|
37
|
+
*/
|
|
38
|
+
private scanWithOpenAI;
|
|
39
|
+
/**
|
|
40
|
+
* Checks if the ClamAV scan result indicates a malicious file.
|
|
41
|
+
* @param result The ClamAV scan output.
|
|
42
|
+
* @returns {boolean} True if the file is flagged, false otherwise.
|
|
43
|
+
*/
|
|
44
|
+
private isClamAVResultMalicious;
|
|
45
|
+
/**
|
|
46
|
+
* Checks if the VirusTotal result indicates a malicious file.
|
|
47
|
+
* @param result The VirusTotal scan result.
|
|
48
|
+
* @returns {boolean} True if the file is flagged, false otherwise.
|
|
49
|
+
*/
|
|
50
|
+
private isVirusTotalResultMalicious;
|
|
51
|
+
/**
|
|
52
|
+
* Checks if the OpenAI Moderation result indicates harmful content.
|
|
53
|
+
* @param result The OpenAI Moderation result.
|
|
54
|
+
* @returns {boolean} True if harmful content is detected, false otherwise.
|
|
55
|
+
*/
|
|
56
|
+
private isOpenAiResultMalicious;
|
|
57
|
+
/**
|
|
58
|
+
* Deletes a file from the filesystem.
|
|
59
|
+
* @param filePath The file to delete.
|
|
60
|
+
*/
|
|
61
|
+
private deleteFile;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=ContentScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContentScanner.d.ts","sourceRoot":"","sources":["../../src/utils/ContentScanner.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,YAAY,CAAC,CAAS;IAE9B;;;OAGG;gBACS,mBAAmB,GAAE,MAAiB;IAMlD;;;;;OAKG;IACU,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqDtD;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAYtB;;;;OAIG;YACW,kBAAkB;IAgBhC;;;;OAIG;YACW,cAAc;IAa5B;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAI/B;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;IAKnC;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAI/B;;;OAGG;IACH,OAAO,CAAC,UAAU;CASrB"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.IllegalContentScanner = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
/**
|
|
12
|
+
* @class IllegalContentScanner
|
|
13
|
+
* Scans files for illegal content using ClamAV running in a Docker container and deletes files if flagged.
|
|
14
|
+
*/
|
|
15
|
+
class IllegalContentScanner {
|
|
16
|
+
/**
|
|
17
|
+
* @constructor
|
|
18
|
+
* @param clamAVContainerName The name of the Docker container running ClamAV (default: 'clamav').
|
|
19
|
+
*/
|
|
20
|
+
constructor(clamAVContainerName = "clamav") {
|
|
21
|
+
this.clamAVContainerName = clamAVContainerName;
|
|
22
|
+
this.virusTotalApiKey = process.env.VIRUSTOTAL_API_KEY || undefined;
|
|
23
|
+
this.openAiApiKey = process.env.OPENAI_API_KEY || undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Scans a file for illegal content using ClamAV, VirusTotal, and OpenAI Moderation API.
|
|
27
|
+
* If a service is missing its API key, the scan for that service is skipped.
|
|
28
|
+
* @param filePath The path of the file to scan.
|
|
29
|
+
* @returns {Promise<void>} A promise that resolves after the scan and potential deletion.
|
|
30
|
+
*/
|
|
31
|
+
async scanFile(filePath) {
|
|
32
|
+
try {
|
|
33
|
+
// 1. ClamAV Scan
|
|
34
|
+
console.log(`Starting ClamAV scan for file: ${filePath}`);
|
|
35
|
+
const clamAvResult = await this.scanWithClamAV(filePath);
|
|
36
|
+
if (this.isClamAVResultMalicious(clamAvResult)) {
|
|
37
|
+
console.log(`ClamAV flagged file: ${filePath}. Deleting...`);
|
|
38
|
+
this.deleteFile(filePath);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.log(`ClamAV did not flag the file: ${filePath}.`);
|
|
43
|
+
}
|
|
44
|
+
// 2. VirusTotal Scan (if API key is provided)
|
|
45
|
+
if (this.virusTotalApiKey) {
|
|
46
|
+
console.log(`Submitting file to VirusTotal for scanning: ${filePath}`);
|
|
47
|
+
const virusTotalResult = await this.scanWithVirusTotal(filePath);
|
|
48
|
+
if (this.isVirusTotalResultMalicious(virusTotalResult)) {
|
|
49
|
+
console.log(`VirusTotal flagged file: ${filePath}. Deleting...`);
|
|
50
|
+
this.deleteFile(filePath);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.log(`VirusTotal did not flag the file: ${filePath}.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log(`VirusTotal API key not provided. Skipping VirusTotal scan.`);
|
|
59
|
+
}
|
|
60
|
+
// 3. OpenAI Moderation API (if API key is provided and file is text-based)
|
|
61
|
+
const fileExtension = path_1.default.extname(filePath).toLowerCase();
|
|
62
|
+
if (this.openAiApiKey && (fileExtension === ".txt" || fileExtension === ".json")) {
|
|
63
|
+
console.log(`Scanning file content with OpenAI Moderation API: ${filePath}`);
|
|
64
|
+
const fileContent = await fs_1.default.promises.readFile(filePath, "utf-8");
|
|
65
|
+
const openAiResult = await this.scanWithOpenAI(fileContent);
|
|
66
|
+
if (this.isOpenAiResultMalicious(openAiResult)) {
|
|
67
|
+
console.log(`OpenAI flagged file: ${filePath}. Deleting...`);
|
|
68
|
+
this.deleteFile(filePath);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
if (!this.openAiApiKey) {
|
|
74
|
+
console.log(`OpenAI API key not provided. Skipping OpenAI scan.`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.log(`OpenAI scan skipped: File type not supported (${fileExtension}).`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
console.log(`File ${filePath} passed all multilayer scans.`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error(`Error scanning file: ${filePath}`, error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Scans a file using ClamAV running in Docker.
|
|
88
|
+
* @param filePath The path of the file to scan.
|
|
89
|
+
* @returns {Promise<string>} The result of the ClamAV scan.
|
|
90
|
+
*/
|
|
91
|
+
scanWithClamAV(filePath) {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
(0, child_process_1.exec)(`docker exec ${this.clamAVContainerName} clamdscan ${filePath}`, (error, stdout, stderr) => {
|
|
94
|
+
if (error) {
|
|
95
|
+
reject(`ClamAV scan error: ${stderr}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
resolve(stdout);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Submits a file to VirusTotal for scanning.
|
|
105
|
+
* @param filePath The path of the file to scan.
|
|
106
|
+
* @returns {Promise<any>} The result from VirusTotal.
|
|
107
|
+
*/
|
|
108
|
+
async scanWithVirusTotal(filePath) {
|
|
109
|
+
const fileContent = await fs_1.default.promises.readFile(filePath);
|
|
110
|
+
const formData = new FormData();
|
|
111
|
+
// @ts-ignore
|
|
112
|
+
formData.append("file", fileContent);
|
|
113
|
+
const response = await axios_1.default.post("https://www.virustotal.com/vtapi/v2/file/scan", formData, {
|
|
114
|
+
headers: {
|
|
115
|
+
"x-apikey": this.virusTotalApiKey,
|
|
116
|
+
// @ts-ignore
|
|
117
|
+
...formData.getHeaders(),
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return response.data;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Scans file content (text) using OpenAI's Moderation API.
|
|
124
|
+
* @param content The text content to scan.
|
|
125
|
+
* @returns {Promise<any>} The result from OpenAI's Moderation API.
|
|
126
|
+
*/
|
|
127
|
+
async scanWithOpenAI(content) {
|
|
128
|
+
const response = await axios_1.default.post("https://api.openai.com/v1/moderations", { input: content }, {
|
|
129
|
+
headers: {
|
|
130
|
+
Authorization: `Bearer ${this.openAiApiKey}`,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
return response.data;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Checks if the ClamAV scan result indicates a malicious file.
|
|
137
|
+
* @param result The ClamAV scan output.
|
|
138
|
+
* @returns {boolean} True if the file is flagged, false otherwise.
|
|
139
|
+
*/
|
|
140
|
+
isClamAVResultMalicious(result) {
|
|
141
|
+
return result.includes("FOUND");
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Checks if the VirusTotal result indicates a malicious file.
|
|
145
|
+
* @param result The VirusTotal scan result.
|
|
146
|
+
* @returns {boolean} True if the file is flagged, false otherwise.
|
|
147
|
+
*/
|
|
148
|
+
isVirusTotalResultMalicious(result) {
|
|
149
|
+
// Check if VirusTotal flagged the file based on its multiple antivirus results
|
|
150
|
+
return result.positives > 0;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Checks if the OpenAI Moderation result indicates harmful content.
|
|
154
|
+
* @param result The OpenAI Moderation result.
|
|
155
|
+
* @returns {boolean} True if harmful content is detected, false otherwise.
|
|
156
|
+
*/
|
|
157
|
+
isOpenAiResultMalicious(result) {
|
|
158
|
+
return result.flagged === true;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Deletes a file from the filesystem.
|
|
162
|
+
* @param filePath The file to delete.
|
|
163
|
+
*/
|
|
164
|
+
deleteFile(filePath) {
|
|
165
|
+
fs_1.default.unlink(filePath, (err) => {
|
|
166
|
+
if (err) {
|
|
167
|
+
console.error(`Error deleting file: ${filePath}`, err);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
console.log(`File ${filePath} was deleted successfully.`);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.IllegalContentScanner = IllegalContentScanner;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import NodeCache from "node-cache";
|
|
2
|
+
export interface ICache {
|
|
3
|
+
set<T>(key: string, value: T, ttl?: number): Promise<boolean>;
|
|
4
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
5
|
+
del(key: string): Promise<number>;
|
|
6
|
+
has(key: string): Promise<boolean>;
|
|
7
|
+
flushAll(): Promise<boolean>;
|
|
8
|
+
keys(pattern?: string): Promise<string[]>;
|
|
9
|
+
ttl(key: string, ttl?: number): Promise<boolean>;
|
|
10
|
+
}
|
|
11
|
+
declare class DigCache implements ICache {
|
|
12
|
+
private cache;
|
|
13
|
+
private useRedis;
|
|
14
|
+
/**
|
|
15
|
+
* Constructor with the same signature as NodeCache.
|
|
16
|
+
* @param options - NodeCache options
|
|
17
|
+
*/
|
|
18
|
+
constructor(options?: NodeCache.Options);
|
|
19
|
+
/**
|
|
20
|
+
* Initializes the Redis connection if Redis is being used.
|
|
21
|
+
* Call this method before performing any cache operations.
|
|
22
|
+
*/
|
|
23
|
+
initialize(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Gracefully disconnects the Redis client if Redis is being used.
|
|
26
|
+
* Call this method when your application is shutting down.
|
|
27
|
+
*/
|
|
28
|
+
disconnect(): Promise<void>;
|
|
29
|
+
set<T>(key: string, value: T, ttl?: number): Promise<boolean>;
|
|
30
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
31
|
+
del(key: string): Promise<number>;
|
|
32
|
+
has(key: string): Promise<boolean>;
|
|
33
|
+
flushAll(): Promise<boolean>;
|
|
34
|
+
keys(pattern?: string): Promise<string[]>;
|
|
35
|
+
ttl(key: string, ttl?: number): Promise<boolean>;
|
|
36
|
+
}
|
|
37
|
+
export { DigCache };
|
|
38
|
+
//# sourceMappingURL=DigCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DigCache.d.ts","sourceRoot":"","sources":["../../src/utils/DigCache.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,YAAY,CAAC;AAGnC,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAElD;AAED,cAAM,QAAS,YAAW,MAAM;IAC9B,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,QAAQ,CAAU;IAE1B;;;OAGG;gBACS,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO;IAqBvC;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkC7D,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAuB3C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBlC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAoB5B,IAAI,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmB9C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CA0BvD;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|