@clonegod/ttd-core 3.1.5 → 3.1.7
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/app_config/EnvArgs.d.ts +2 -1
- package/dist/app_config/EnvArgs.js +47 -27
- package/dist/app_config/env_loader.d.ts +3 -0
- package/dist/app_config/env_loader.js +102 -0
- package/dist/app_config/env_registry.d.ts +11 -0
- package/dist/app_config/env_registry.js +38 -0
- package/dist/app_config/index.d.ts +2 -0
- package/dist/app_config/index.js +2 -0
- package/dist/cache/loading_cache.js +2 -4
- package/dist/cache/redis_client.d.ts +2 -2
- package/dist/cache/redis_client.js +30 -58
- package/dist/cache/redis_cmd.d.ts +25 -3
- package/dist/cache/redis_cmd.js +207 -20
- package/dist/index.d.ts +3 -1
- package/dist/index.js +11 -7
- package/package.json +1 -1
|
@@ -2,39 +2,59 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EnvArgs = void 0;
|
|
4
4
|
const __1 = require("..");
|
|
5
|
+
const env_loader_1 = require("./env_loader");
|
|
6
|
+
const ENVARGS_KEYS = [
|
|
7
|
+
'app_name', 'app_base_dir', 'chain_id', 'dex_id',
|
|
8
|
+
'redis_host', 'redis_port',
|
|
9
|
+
'config_center_host', 'trade_analyze_url',
|
|
10
|
+
'rpc_endpoint', 'ws_endpoint', 'grpc_endpoint', 'grpc_token',
|
|
11
|
+
'auto_quote_enable', 'auto_quote_interval_mills',
|
|
12
|
+
'quote_pair', 'quote_amount_usd', 'quote_pool_address', 'quote_pool_name', 'quote_pool_fee_rate',
|
|
13
|
+
'trade_group_id', 'trade_pair',
|
|
14
|
+
'token_price_cache_seconds', 'wallet_dir',
|
|
15
|
+
];
|
|
5
16
|
class EnvArgs {
|
|
6
17
|
constructor(chain_id, dex_id) {
|
|
7
|
-
var _a
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
var _a;
|
|
19
|
+
const overrides = {};
|
|
20
|
+
if (chain_id)
|
|
21
|
+
overrides.chain_id = chain_id;
|
|
22
|
+
if (dex_id)
|
|
23
|
+
overrides.dex_id = dex_id;
|
|
24
|
+
const cfg = (0, env_loader_1.loadEnvConfig)(ENVARGS_KEYS, overrides);
|
|
25
|
+
this.app_name = cfg.app_name || '';
|
|
26
|
+
this.app_base_dir = cfg.app_base_dir || '';
|
|
27
|
+
this.chain_id = (cfg.chain_id || '').toUpperCase();
|
|
28
|
+
this.dex_id = (cfg.dex_id || '').toUpperCase();
|
|
11
29
|
if ((0, __1.isEmpty)(this.chain_id)) {
|
|
12
30
|
throw new Error(`environment: CHAIN_ID is empty!!!`);
|
|
13
31
|
}
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
this.trade_pair = process.env.TRADE_PAIR || '';
|
|
34
|
-
this.token_price_cache_seconds = parseInt(process.env.TOKEN_PRICE_CACHE_SECONDS || '1200');
|
|
32
|
+
this.redis_host = cfg.redis_host || '127.0.0.1';
|
|
33
|
+
this.redis_port = ((_a = cfg.redis_port) === null || _a === void 0 ? void 0 : _a.toString()) || '6379';
|
|
34
|
+
this.config_center_host = cfg.config_center_host || '';
|
|
35
|
+
this.trade_analyze_url = cfg.trade_analyze_url || '';
|
|
36
|
+
this.rpc_endpoint = cfg.rpc_endpoint || '';
|
|
37
|
+
this.ws_endpoint = cfg.ws_endpoint || '';
|
|
38
|
+
this.grpc_endpoint = cfg.grpc_endpoint || '';
|
|
39
|
+
this.grpc_token = cfg.grpc_token || '';
|
|
40
|
+
this.auto_quote_enable = cfg.auto_quote_enable || false;
|
|
41
|
+
this.auto_quote_interval_mills = cfg.auto_quote_interval_mills || 60000;
|
|
42
|
+
this.quote_pair = cfg.quote_pair || '';
|
|
43
|
+
this.quote_amount_usd = cfg.quote_amount_usd || 100;
|
|
44
|
+
this.quote_pool_address = cfg.quote_pool_address || '';
|
|
45
|
+
this.quote_pool_name = cfg.quote_pool_name || '';
|
|
46
|
+
this.quote_pool_fee_rate = cfg.quote_pool_fee_rate || 0;
|
|
47
|
+
this.trade_group_id = cfg.trade_group_id || '';
|
|
48
|
+
this.trade_pair = cfg.trade_pair || '';
|
|
49
|
+
this.token_price_cache_seconds = cfg.token_price_cache_seconds || 1200;
|
|
50
|
+
this.wallet_dir = cfg.wallet_dir || '';
|
|
35
51
|
}
|
|
36
|
-
print() {
|
|
37
|
-
|
|
52
|
+
print(moduleName) {
|
|
53
|
+
const cfg = {};
|
|
54
|
+
for (const key of ENVARGS_KEYS) {
|
|
55
|
+
cfg[key] = this[key];
|
|
56
|
+
}
|
|
57
|
+
(0, env_loader_1.printEnvConfig)(moduleName || this.app_name || 'EnvArgs', cfg, ENVARGS_KEYS);
|
|
38
58
|
}
|
|
39
59
|
}
|
|
40
60
|
exports.EnvArgs = EnvArgs;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function loadEnvConfig(keys: string[], overrides?: Record<string, any>): Record<string, any>;
|
|
2
|
+
export declare function validateEnvConfig(keys: string[]): string[];
|
|
3
|
+
export declare function printEnvConfig(moduleName: string, config: Record<string, any>, keys: string[]): void;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadEnvConfig = loadEnvConfig;
|
|
4
|
+
exports.validateEnvConfig = validateEnvConfig;
|
|
5
|
+
exports.printEnvConfig = printEnvConfig;
|
|
6
|
+
const env_registry_1 = require("./env_registry");
|
|
7
|
+
function parseValue(raw, def) {
|
|
8
|
+
var _a;
|
|
9
|
+
const str = raw !== undefined ? raw : undefined;
|
|
10
|
+
if (str === undefined || str === '') {
|
|
11
|
+
return def.default !== undefined ? def.default : getTypeDefault(def.type);
|
|
12
|
+
}
|
|
13
|
+
switch (def.type) {
|
|
14
|
+
case 'number':
|
|
15
|
+
const n = Number(str);
|
|
16
|
+
return isNaN(n) ? ((_a = def.default) !== null && _a !== void 0 ? _a : 0) : n;
|
|
17
|
+
case 'boolean':
|
|
18
|
+
return str === 'true' || str === '1';
|
|
19
|
+
case 'string[]':
|
|
20
|
+
return str.split(',').map(s => s.trim()).filter(Boolean);
|
|
21
|
+
default:
|
|
22
|
+
return str;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getTypeDefault(type) {
|
|
26
|
+
switch (type) {
|
|
27
|
+
case 'number': return 0;
|
|
28
|
+
case 'boolean': return false;
|
|
29
|
+
case 'string[]': return [];
|
|
30
|
+
default: return '';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function loadEnvConfig(keys, overrides) {
|
|
34
|
+
var _a, _b;
|
|
35
|
+
const registry = (0, env_registry_1.getEnvRegistry)();
|
|
36
|
+
const config = {};
|
|
37
|
+
for (const key of keys) {
|
|
38
|
+
const def = registry[key];
|
|
39
|
+
if (!def) {
|
|
40
|
+
console.warn(`[EnvConfig] unknown key: ${key} (not registered)`);
|
|
41
|
+
config[key] = (_b = (_a = overrides === null || overrides === void 0 ? void 0 : overrides[key]) !== null && _a !== void 0 ? _a : process.env[key.toUpperCase()]) !== null && _b !== void 0 ? _b : '';
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (overrides && key in overrides && overrides[key] !== undefined) {
|
|
45
|
+
config[key] = overrides[key];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
config[key] = parseValue(process.env[def.env], def);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
function validateEnvConfig(keys) {
|
|
54
|
+
const registry = (0, env_registry_1.getEnvRegistry)();
|
|
55
|
+
const missing = [];
|
|
56
|
+
for (const key of keys) {
|
|
57
|
+
const def = registry[key];
|
|
58
|
+
if (!def)
|
|
59
|
+
continue;
|
|
60
|
+
if (!def.required)
|
|
61
|
+
continue;
|
|
62
|
+
const val = process.env[def.env];
|
|
63
|
+
if (val === undefined || val === '') {
|
|
64
|
+
missing.push(`${def.env} (${def.desc || key})`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return missing;
|
|
68
|
+
}
|
|
69
|
+
const SENSITIVE_RE = /key|token|secret|password|private|auth/i;
|
|
70
|
+
function maskValue(key, value, def) {
|
|
71
|
+
if (value === undefined || value === null || value === '')
|
|
72
|
+
return '(empty)';
|
|
73
|
+
const str = String(value);
|
|
74
|
+
const isSensitive = (def === null || def === void 0 ? void 0 : def.sensitive) || SENSITIVE_RE.test(key);
|
|
75
|
+
if (isSensitive && str.length > 4) {
|
|
76
|
+
return str.slice(0, 4) + '****';
|
|
77
|
+
}
|
|
78
|
+
return str;
|
|
79
|
+
}
|
|
80
|
+
function printEnvConfig(moduleName, config, keys) {
|
|
81
|
+
const registry = (0, env_registry_1.getEnvRegistry)();
|
|
82
|
+
const divider = '═'.repeat(70);
|
|
83
|
+
const lines = [];
|
|
84
|
+
lines.push('');
|
|
85
|
+
lines.push(divider);
|
|
86
|
+
lines.push(` ${moduleName.toUpperCase()} — Configuration`);
|
|
87
|
+
lines.push(divider);
|
|
88
|
+
const maxKeyLen = Math.max(...keys.map(k => {
|
|
89
|
+
const def = registry[k];
|
|
90
|
+
return def ? def.env.length : k.length;
|
|
91
|
+
}), 20);
|
|
92
|
+
for (const key of keys) {
|
|
93
|
+
const def = registry[key];
|
|
94
|
+
const envName = def ? def.env : key.toUpperCase();
|
|
95
|
+
const val = maskValue(key, config[key], def);
|
|
96
|
+
const desc = (def === null || def === void 0 ? void 0 : def.desc) ? ` # ${def.desc}` : '';
|
|
97
|
+
lines.push(` ${envName.padEnd(maxKeyLen)} ${val}${desc}`);
|
|
98
|
+
}
|
|
99
|
+
lines.push(divider);
|
|
100
|
+
lines.push('');
|
|
101
|
+
console.log(lines.join('\n'));
|
|
102
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type EnvVarType = 'string' | 'number' | 'boolean' | 'string[]';
|
|
2
|
+
export interface EnvVarDef {
|
|
3
|
+
env: string;
|
|
4
|
+
type: EnvVarType;
|
|
5
|
+
default?: any;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
sensitive?: boolean;
|
|
8
|
+
desc?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function registerEnvVars(defs: Record<string, EnvVarDef>): void;
|
|
11
|
+
export declare function getEnvRegistry(): Readonly<Record<string, EnvVarDef>>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerEnvVars = registerEnvVars;
|
|
4
|
+
exports.getEnvRegistry = getEnvRegistry;
|
|
5
|
+
const _registry = {};
|
|
6
|
+
function registerEnvVars(defs) {
|
|
7
|
+
Object.assign(_registry, defs);
|
|
8
|
+
}
|
|
9
|
+
function getEnvRegistry() {
|
|
10
|
+
return _registry;
|
|
11
|
+
}
|
|
12
|
+
registerEnvVars({
|
|
13
|
+
app_name: { env: 'APP_NAME', type: 'string', desc: '应用名称' },
|
|
14
|
+
app_base_dir: { env: 'APP_BASE_DIR', type: 'string', desc: '应用根目录' },
|
|
15
|
+
chain_id: { env: 'CHAIN_ID', type: 'string', required: true, desc: '链标识(BSC/SOLANA/TRON)' },
|
|
16
|
+
dex_id: { env: 'DEX_ID', type: 'string', desc: 'DEX 标识' },
|
|
17
|
+
redis_host: { env: 'REDIS_HOST', type: 'string', default: '127.0.0.1', desc: 'Redis 地址' },
|
|
18
|
+
redis_port: { env: 'REDIS_PORT', type: 'number', default: 6379, desc: 'Redis 端口' },
|
|
19
|
+
config_center_host: { env: 'CONFIG_CENTER_HOST', type: 'string', default: '', desc: 'config-center 地址' },
|
|
20
|
+
trade_analyze_host: { env: 'TRADE_ANALYZE_HOST', type: 'string', default: '', desc: 'analyze 地址' },
|
|
21
|
+
trade_analyze_port: { env: 'TRADE_ANALYZE_PORT', type: 'number', default: 8004, desc: 'analyze 端口' },
|
|
22
|
+
trade_analyze_url: { env: 'TRADE_ANALYZE_URL', type: 'string', default: '', desc: 'analyze URL(旧)' },
|
|
23
|
+
rpc_endpoint: { env: 'RPC_ENDPOINT', type: 'string', default: '', desc: 'RPC HTTP 端点' },
|
|
24
|
+
ws_endpoint: { env: 'WS_ENDPOINT', type: 'string', default: '', desc: 'RPC WebSocket 端点' },
|
|
25
|
+
grpc_endpoint: { env: 'GRPC_ENDPOINT', type: 'string', default: '', desc: 'gRPC 端点' },
|
|
26
|
+
grpc_token: { env: 'GRPC_TOKEN', type: 'string', default: '', sensitive: true, desc: 'gRPC 认证 token' },
|
|
27
|
+
auto_quote_enable: { env: 'AUTO_QUOTE_ENABLE', type: 'boolean', default: false, desc: '自动报价开关' },
|
|
28
|
+
auto_quote_interval_mills: { env: 'AUTO_QUOTE_INTERVAL_MILLS', type: 'number', default: 60000, desc: '自动报价间隔(ms)' },
|
|
29
|
+
quote_pair: { env: 'QUOTE_PAIR', type: 'string', default: '', desc: '报价币对' },
|
|
30
|
+
quote_amount_usd: { env: 'QUOTE_AMOUNT_USD', type: 'number', default: 100, desc: '报价金额(USD)' },
|
|
31
|
+
quote_pool_address: { env: 'QUOTE_POOL_ADDRESS', type: 'string', default: '', desc: '指定池子地址' },
|
|
32
|
+
quote_pool_name: { env: 'QUOTE_POOL_NAME', type: 'string', default: '', desc: '指定池子名称' },
|
|
33
|
+
quote_pool_fee_rate: { env: 'QUOTE_POOL_FEE_RATE', type: 'number', default: 0, desc: '指定池子费率' },
|
|
34
|
+
trade_group_id: { env: 'TRADE_GROUP_ID', type: 'string', default: '', desc: '交易组 ID' },
|
|
35
|
+
trade_pair: { env: 'TRADE_PAIR', type: 'string', default: '', desc: '交易币对' },
|
|
36
|
+
token_price_cache_seconds: { env: 'TOKEN_PRICE_CACHE_SECONDS', type: 'number', default: 1200, desc: 'token 价格缓存时间(s)' },
|
|
37
|
+
wallet_dir: { env: 'WALLET_DIR', type: 'string', default: '', desc: '钱包目录' },
|
|
38
|
+
});
|
package/dist/app_config/index.js
CHANGED
|
@@ -18,6 +18,8 @@ exports.getGlobalAppConfig = void 0;
|
|
|
18
18
|
__exportStar(require("./EnvArgs"), exports);
|
|
19
19
|
__exportStar(require("./AppConfig"), exports);
|
|
20
20
|
__exportStar(require("./AbstractTradeAppConfig"), exports);
|
|
21
|
+
__exportStar(require("./env_registry"), exports);
|
|
22
|
+
__exportStar(require("./env_loader"), exports);
|
|
21
23
|
const getGlobalAppConfig = () => {
|
|
22
24
|
let app_config = global.app_config;
|
|
23
25
|
if (!app_config) {
|
|
@@ -17,12 +17,10 @@ class LoadingCache {
|
|
|
17
17
|
}
|
|
18
18
|
init() {
|
|
19
19
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
-
(0, index_1.log_info)('init redis client start');
|
|
21
20
|
this.local_cache = new Map();
|
|
22
21
|
this.refresh_cache_timer = new Map();
|
|
23
|
-
yield (0,
|
|
24
|
-
|
|
25
|
-
(0, index_1.log_info)('init redis client done');
|
|
22
|
+
this.redis_client = yield (0, redis_client_1.getRedisClient)('LoadingCache');
|
|
23
|
+
(0, index_1.log_info)('[LoadingCache] init done');
|
|
26
24
|
});
|
|
27
25
|
}
|
|
28
26
|
get_key(chain_id, cache_type, id) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { RedisClientType } from 'redis';
|
|
2
|
-
declare function
|
|
3
|
-
export {
|
|
2
|
+
declare function getRedisClient(module_name?: string): Promise<RedisClientType>;
|
|
3
|
+
export { getRedisClient, };
|
|
@@ -8,66 +8,38 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
16
|
-
const moment_1 = __importDefault(require("moment"));
|
|
12
|
+
exports.getRedisClient = getRedisClient;
|
|
17
13
|
const redis_1 = require("redis");
|
|
18
|
-
let redisClient;
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
url
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
console.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
console.log(current_time(), module_name, `[${i}] ping, got: ${res}`);
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
console.error(current_time(), module_name, `[${i}] ping, got error!`, err);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
function getCache() {
|
|
14
|
+
let redisClient = null;
|
|
15
|
+
let connectPromise = null;
|
|
16
|
+
function createConnection() {
|
|
17
|
+
const host = process.env.REDIS_HOST || '127.0.0.1';
|
|
18
|
+
const port = process.env.REDIS_PORT || '6379';
|
|
19
|
+
const url = `redis://${host}:${port}`;
|
|
20
|
+
console.log(`[Redis] connecting to ${url}`);
|
|
21
|
+
const client = (0, redis_1.createClient)({ url });
|
|
22
|
+
client.on('error', (err) => console.error(`[Redis] error: ${err.message}`));
|
|
23
|
+
client.on('reconnecting', () => console.info('[Redis] reconnecting...'));
|
|
24
|
+
client.on('ready', () => console.info('[Redis] ready'));
|
|
25
|
+
return client.connect().then(() => {
|
|
26
|
+
redisClient = client;
|
|
27
|
+
console.info(`[Redis] connected to ${url}`);
|
|
28
|
+
return client;
|
|
29
|
+
}).catch((err) => {
|
|
30
|
+
redisClient = null;
|
|
31
|
+
connectPromise = null;
|
|
32
|
+
console.error(`[Redis] connect failed: ${err.message}`);
|
|
33
|
+
throw err;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function getRedisClient() {
|
|
50
37
|
return __awaiter(this, arguments, void 0, function* (module_name = '') {
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
redisClient.on('error', err => console.error(`Redis Error: ${err}`));
|
|
58
|
-
redisClient.on('connect', () => console.info('Redis connected'));
|
|
59
|
-
redisClient.on('reconnecting', () => console.info('Redis reconnecting'));
|
|
60
|
-
redisClient.on('ready', () => {
|
|
61
|
-
isReady = true;
|
|
62
|
-
console.info('Redis ready!');
|
|
63
|
-
});
|
|
64
|
-
yield redisClient.connect();
|
|
65
|
-
}
|
|
66
|
-
return redisClient;
|
|
38
|
+
if (redisClient)
|
|
39
|
+
return redisClient;
|
|
40
|
+
if (connectPromise)
|
|
41
|
+
return connectPromise;
|
|
42
|
+
connectPromise = createConnection();
|
|
43
|
+
return connectPromise;
|
|
67
44
|
});
|
|
68
45
|
}
|
|
69
|
-
getCache().then(connection => {
|
|
70
|
-
redisClient = connection;
|
|
71
|
-
}).catch(err => {
|
|
72
|
-
console.error({ err }, 'Failed to connect to Redis');
|
|
73
|
-
});
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import { RedisClientType } from "redis";
|
|
2
|
-
export declare class
|
|
2
|
+
export declare class RedisClient {
|
|
3
3
|
redis_client: RedisClientType;
|
|
4
|
-
|
|
4
|
+
private lockPrefix;
|
|
5
|
+
private lockMaxRetries;
|
|
6
|
+
private lockRetryDelayMs;
|
|
7
|
+
private lockExpireSeconds;
|
|
8
|
+
constructor(lockPrefix?: string);
|
|
5
9
|
init(): Promise<void>;
|
|
10
|
+
private ensureClient;
|
|
6
11
|
exists(key: string): Promise<number>;
|
|
7
12
|
ttl(key: string): Promise<number>;
|
|
8
13
|
expire(key: string, ttl: number): Promise<boolean>;
|
|
9
|
-
del(key: string): Promise<number>;
|
|
14
|
+
del(key: string, field?: string): Promise<number>;
|
|
10
15
|
set_nx(key: string, value: string): Promise<boolean>;
|
|
16
|
+
get(key: string): Promise<string | null>;
|
|
17
|
+
set(key: string, value: string, expireSeconds?: number): Promise<string | null>;
|
|
18
|
+
hset(key: string, field: string, value: string, expireSeconds?: number): Promise<number>;
|
|
19
|
+
hget(key: string, field: string): Promise<string | undefined>;
|
|
20
|
+
hgetall(key: string): Promise<Record<string, string>>;
|
|
21
|
+
hmget(key: string, fields: string[]): Promise<(string | null)[]>;
|
|
22
|
+
hkeys(key: string): Promise<string[]>;
|
|
23
|
+
hdel(key: string, field: string): Promise<number>;
|
|
24
|
+
hlen(key: string): Promise<number>;
|
|
11
25
|
hinc(key: string, field: string, ttl?: number): Promise<number[]>;
|
|
26
|
+
lrange(key: string, start?: number, stop?: number): Promise<string[]>;
|
|
27
|
+
lpush(key: string, value: string): Promise<number>;
|
|
28
|
+
ltrim(key: string, start: number, stop: number): Promise<string>;
|
|
29
|
+
private getLockKey;
|
|
30
|
+
acquireLock(lock_key: string, lock_value: string, expireSeconds?: number): Promise<boolean>;
|
|
31
|
+
releaseLock(lock_key: string, lock_value: string): Promise<boolean>;
|
|
32
|
+
withLock<T>(lock_identifier: string, callback: () => Promise<T>, release_lock_delay_ms?: number): Promise<T>;
|
|
12
33
|
subscribe(channel: string, listener: any): Promise<void>;
|
|
13
34
|
publish(channel: string, message: string): Promise<void>;
|
|
14
35
|
}
|
|
36
|
+
export declare const RedisCommand: typeof RedisClient;
|
package/dist/cache/redis_cmd.js
CHANGED
|
@@ -9,18 +9,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.RedisCommand = void 0;
|
|
12
|
+
exports.RedisCommand = exports.RedisClient = void 0;
|
|
13
13
|
const index_1 = require("../index");
|
|
14
14
|
const redis_client_1 = require("./redis_client");
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const MAX_SUBSCRIBE_RECONNECT = 10;
|
|
16
|
+
class RedisClient {
|
|
17
|
+
constructor(lockPrefix = '') {
|
|
18
|
+
this.lockMaxRetries = 10;
|
|
19
|
+
this.lockRetryDelayMs = 300;
|
|
20
|
+
this.lockExpireSeconds = 3;
|
|
21
|
+
this.lockPrefix = lockPrefix;
|
|
17
22
|
}
|
|
18
23
|
init() {
|
|
19
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
-
(0,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
this.redis_client = yield (0, redis_client_1.getRedisClient)('RedisClient');
|
|
26
|
+
(0, index_1.log_info)('[RedisClient] init done');
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
ensureClient() {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
if (!this.redis_client) {
|
|
32
|
+
this.redis_client = yield (0, redis_client_1.getRedisClient)('RedisClient');
|
|
33
|
+
}
|
|
34
|
+
return this.redis_client;
|
|
24
35
|
});
|
|
25
36
|
}
|
|
26
37
|
exists(key) {
|
|
@@ -38,8 +49,11 @@ class RedisCommand {
|
|
|
38
49
|
return yield this.redis_client.expire(key, ttl);
|
|
39
50
|
});
|
|
40
51
|
}
|
|
41
|
-
del(
|
|
42
|
-
return __awaiter(this,
|
|
52
|
+
del(key_1) {
|
|
53
|
+
return __awaiter(this, arguments, void 0, function* (key, field = '') {
|
|
54
|
+
if (field) {
|
|
55
|
+
return yield this.redis_client.hDel(key, field);
|
|
56
|
+
}
|
|
43
57
|
return yield this.redis_client.del(key);
|
|
44
58
|
});
|
|
45
59
|
}
|
|
@@ -48,6 +62,71 @@ class RedisCommand {
|
|
|
48
62
|
return yield this.redis_client.setNX(key, value);
|
|
49
63
|
});
|
|
50
64
|
}
|
|
65
|
+
get(key) {
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
const client = yield this.ensureClient();
|
|
68
|
+
return yield client.get(key);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
set(key, value, expireSeconds) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
const client = yield this.ensureClient();
|
|
74
|
+
if (expireSeconds && expireSeconds > 0) {
|
|
75
|
+
return yield client.set(key, value, { EX: expireSeconds });
|
|
76
|
+
}
|
|
77
|
+
return yield client.set(key, value);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
hset(key, field, value, expireSeconds) {
|
|
81
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
+
const client = yield this.ensureClient();
|
|
83
|
+
const res = yield client.hSet(key, field, value);
|
|
84
|
+
if (expireSeconds && expireSeconds > 0) {
|
|
85
|
+
try {
|
|
86
|
+
yield client.hExpire(key, field, expireSeconds);
|
|
87
|
+
}
|
|
88
|
+
catch (_) {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return res;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
hget(key, field) {
|
|
95
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
const client = yield this.ensureClient();
|
|
97
|
+
return yield client.hGet(key, field);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
hgetall(key) {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
const client = yield this.ensureClient();
|
|
103
|
+
return yield client.hGetAll(key);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
hmget(key, fields) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
const client = yield this.ensureClient();
|
|
109
|
+
return yield client.hmGet(key, fields);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
hkeys(key) {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
const client = yield this.ensureClient();
|
|
115
|
+
return yield client.hKeys(key);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
hdel(key, field) {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
const client = yield this.ensureClient();
|
|
121
|
+
return yield client.hDel(key, field);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
hlen(key) {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
const client = yield this.ensureClient();
|
|
127
|
+
return yield client.hLen(key);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
51
130
|
hinc(key_1, field_1) {
|
|
52
131
|
return __awaiter(this, arguments, void 0, function* (key, field, ttl = 3600) {
|
|
53
132
|
let res = [];
|
|
@@ -60,18 +139,125 @@ class RedisCommand {
|
|
|
60
139
|
return res;
|
|
61
140
|
});
|
|
62
141
|
}
|
|
142
|
+
lrange(key_1) {
|
|
143
|
+
return __awaiter(this, arguments, void 0, function* (key, start = 0, stop = -1) {
|
|
144
|
+
const client = yield this.ensureClient();
|
|
145
|
+
return yield client.lRange(key, start, stop);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
lpush(key, value) {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
const client = yield this.ensureClient();
|
|
151
|
+
return yield client.lPush(key, value);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
ltrim(key, start, stop) {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
const client = yield this.ensureClient();
|
|
157
|
+
return yield client.lTrim(key, start, stop);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
getLockKey(lock_identifier) {
|
|
161
|
+
return `${this.lockPrefix}:lock:${lock_identifier}`;
|
|
162
|
+
}
|
|
163
|
+
acquireLock(lock_key_1, lock_value_1) {
|
|
164
|
+
return __awaiter(this, arguments, void 0, function* (lock_key, lock_value, expireSeconds = this.lockExpireSeconds) {
|
|
165
|
+
const client = yield this.ensureClient();
|
|
166
|
+
const result = yield client.set(lock_key, lock_value, {
|
|
167
|
+
NX: true,
|
|
168
|
+
EX: expireSeconds,
|
|
169
|
+
});
|
|
170
|
+
(0, index_1.log_info)(`[Lock] try acquire: key=${lock_key}, value=${lock_value}, expire=${expireSeconds}s, result=${result}`);
|
|
171
|
+
return result === 'OK';
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
releaseLock(lock_key, lock_value) {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
const client = yield this.ensureClient();
|
|
177
|
+
const script = `
|
|
178
|
+
if redis.call('get', KEYS[1]) == ARGV[1] then
|
|
179
|
+
return redis.call('del', KEYS[1])
|
|
180
|
+
else
|
|
181
|
+
return 0
|
|
182
|
+
end
|
|
183
|
+
`;
|
|
184
|
+
const result = yield client.eval(script, {
|
|
185
|
+
keys: [lock_key],
|
|
186
|
+
arguments: [lock_value],
|
|
187
|
+
});
|
|
188
|
+
return Number(result) === 1;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
withLock(lock_identifier_1, callback_1) {
|
|
192
|
+
return __awaiter(this, arguments, void 0, function* (lock_identifier, callback, release_lock_delay_ms = 100) {
|
|
193
|
+
const lock_key = this.getLockKey(lock_identifier);
|
|
194
|
+
const lock_value = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
195
|
+
let retries = 0;
|
|
196
|
+
let acquired = false;
|
|
197
|
+
const firstTryTime = Date.now();
|
|
198
|
+
let getLockTime = 0;
|
|
199
|
+
try {
|
|
200
|
+
while (retries < this.lockMaxRetries) {
|
|
201
|
+
acquired = yield this.acquireLock(lock_key, lock_value);
|
|
202
|
+
if (acquired)
|
|
203
|
+
break;
|
|
204
|
+
yield new Promise(resolve => setTimeout(resolve, this.lockRetryDelayMs));
|
|
205
|
+
retries++;
|
|
206
|
+
}
|
|
207
|
+
if (acquired) {
|
|
208
|
+
getLockTime = Date.now();
|
|
209
|
+
return yield callback();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
if (acquired) {
|
|
214
|
+
const releaseDelay = parseInt(process.env.NONCE_LOCK_RELEASE_DELAY_MS || String(release_lock_delay_ms));
|
|
215
|
+
if (releaseDelay > 0) {
|
|
216
|
+
yield (0, index_1.sleep)(releaseDelay);
|
|
217
|
+
}
|
|
218
|
+
this.releaseLock(lock_key, lock_value);
|
|
219
|
+
(0, index_1.log_info)(`[Lock] withLock ok: key=${lock_key}, retries=${retries}, wait=${getLockTime - firstTryTime}ms, hold=${Date.now() - getLockTime}ms`);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
(0, index_1.log_warn)(`[Lock] withLock failed: key=${lock_key}, retries=${retries}, took=${Date.now() - firstTryTime}ms`);
|
|
223
|
+
throw new Error(`get lock for ${lock_key} failed`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
63
228
|
subscribe(channel, listener) {
|
|
64
229
|
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
-
let
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
230
|
+
let reconnectCount = 0;
|
|
231
|
+
const doSubscribe = () => __awaiter(this, void 0, void 0, function* () {
|
|
232
|
+
try {
|
|
233
|
+
const subscriber = this.redis_client.duplicate();
|
|
234
|
+
subscriber.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
|
|
235
|
+
(0, index_1.log_error)(`[PubSub] channel=${channel} error`, err);
|
|
236
|
+
if (reconnectCount >= MAX_SUBSCRIBE_RECONNECT) {
|
|
237
|
+
(0, index_1.log_warn)(`[PubSub] channel=${channel} exceeded max reconnect (${MAX_SUBSCRIBE_RECONNECT}), giving up`);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
reconnectCount++;
|
|
241
|
+
const delay = Math.min(1000 * reconnectCount, 10000);
|
|
242
|
+
(0, index_1.log_info)(`[PubSub] channel=${channel} reconnect #${reconnectCount} in ${delay}ms`);
|
|
243
|
+
yield (0, index_1.sleep)(delay);
|
|
244
|
+
doSubscribe();
|
|
245
|
+
}));
|
|
246
|
+
yield subscriber.connect();
|
|
247
|
+
yield subscriber.subscribe(channel, listener);
|
|
248
|
+
reconnectCount = 0;
|
|
249
|
+
(0, index_1.log_info)(`[PubSub] subscribed to ${channel}`);
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
(0, index_1.log_error)(`[PubSub] subscribe to ${channel} failed`, err);
|
|
253
|
+
if (reconnectCount < MAX_SUBSCRIBE_RECONNECT) {
|
|
254
|
+
reconnectCount++;
|
|
255
|
+
yield (0, index_1.sleep)(3000);
|
|
256
|
+
doSubscribe();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
yield doSubscribe();
|
|
75
261
|
});
|
|
76
262
|
}
|
|
77
263
|
publish(channel, message) {
|
|
@@ -80,4 +266,5 @@ class RedisCommand {
|
|
|
80
266
|
});
|
|
81
267
|
}
|
|
82
268
|
}
|
|
83
|
-
exports.
|
|
269
|
+
exports.RedisClient = RedisClient;
|
|
270
|
+
exports.RedisCommand = RedisClient;
|
package/dist/index.d.ts
CHANGED
|
@@ -133,9 +133,11 @@ export declare const format_unique_orderbook_id: (chain_id: CHAIN_ID, dex_id: DE
|
|
|
133
133
|
export declare const parse_unique_orderbook_id: (unique_orderbook_id: string) => UniqueOrderbookIdType;
|
|
134
134
|
export declare const format_unique_order_msg_id: (group_id: string, price_id: string, order_trace_id: string) => string;
|
|
135
135
|
export declare const format_order_lock_key: (chain_id: string, group_id: string, price_id: string, order_trace_id: string) => string;
|
|
136
|
+
export declare const getRedisClient: () => Promise<import("redis").RedisClientType>;
|
|
136
137
|
export declare const getRedisCache: () => Promise<import("redis").RedisClientType>;
|
|
137
138
|
export declare const getLoadingCache: () => cache.LoadingCache;
|
|
138
|
-
export declare const getRedisCommamd: () => cache.
|
|
139
|
+
export declare const getRedisCommamd: () => cache.RedisClient;
|
|
140
|
+
export declare const createRedisClient: (lockPrefix?: string) => cache.RedisClient;
|
|
139
141
|
export declare const getArbCache: (env_args: any) => cache.ArbCache;
|
|
140
142
|
export declare const getArbEventPublisher: (arb_cache: any) => cache.ArbEventPublisher;
|
|
141
143
|
export declare const getArbEventSubscriber: (arb_cache: any) => cache.ArbEventSubscriber;
|
package/dist/index.js
CHANGED
|
@@ -48,8 +48,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
48
48
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
49
49
|
};
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
-
exports.
|
|
52
|
-
exports.execute_bash = exports.inspect_arb_local_cache = exports.save_trade_execution_logs = exports.parse_trade_cmdline_args = exports.get_input_out_token_fix = exports.get_input_out_token = exports.calc_amount_in_token = exports.estimate_amount_in_token = exports.failed = exports.success = exports.home_dir = exports.postJSON = exports.deep_merge_object = exports.isArray = exports.isTrue = exports.deep_clone = exports.uuid = exports.DATE_TIME_FORMAT_NO_WHITESPACE = void 0;
|
|
51
|
+
exports.DecimalUtil = exports.generateRandomNumber = exports.LOG = exports._active_log_level = exports.LOG_LEVEL = exports.REDIS_EVENT_TYPE_TRADE_INSTANCE_CHANGE = exports.REDIS_EVENT_TYPE_ORDER = exports.REDIS_EVENT_TYPE_QUOTE = exports.REDIS_EVENT_TYPE_CONFIG_CHANGE = exports.REDIS_EVENT_CHANNEL = exports.get_new_block_channel_name = exports.get_tx_result_channel_name = exports.get_wallet_raw_tx_channel_name = exports.get_order_channel_name = exports.get_quote_channel_name = exports.get_config_channel_name = exports.get_cache_key = exports.getArbEventSubscriber = exports.getArbEventPublisher = exports.getArbCache = exports.createRedisClient = exports.getRedisCommamd = exports.getLoadingCache = exports.getRedisCache = exports.getRedisClient = exports.format_order_lock_key = exports.format_unique_order_msg_id = exports.parse_unique_orderbook_id = exports.format_unique_orderbook_id = exports.format_symbol_name = exports.LOCAL_EVENT_NAME = exports.CACHE_KEY_TYPE = exports.OnChainDataSubscribeType = exports.TradeErrorCodeType = exports.SystemErrorCodeType = exports.TRADE_TYPE = exports.GROUP_ID = exports.DEX_ID = exports.CHAIN_ID = exports.STREAMING_TRANSACTION_CONFIRMED = exports.TRANSACTION_STATE_FAILED = exports.TRANSACTION_STATE_SUCCESS = exports.TRANSACTION_STATE_PROCESSING = exports.QUOTE_AMOUNT_USD_MAX = exports.QUOTE_AMOUNT_USD_MIN = exports.REGEX_HEX_PREFIX = exports.PROCESSING = exports.NOT_FOUND = exports.SUCCESS = exports.FAILED = void 0;
|
|
52
|
+
exports.execute_bash = exports.inspect_arb_local_cache = exports.save_trade_execution_logs = exports.parse_trade_cmdline_args = exports.get_input_out_token_fix = exports.get_input_out_token = exports.calc_amount_in_token = exports.estimate_amount_in_token = exports.failed = exports.success = exports.home_dir = exports.postJSON = exports.deep_merge_object = exports.isArray = exports.isTrue = exports.deep_clone = exports.uuid = exports.DATE_TIME_FORMAT_NO_WHITESPACE = exports.DATE_TIME_FORMAT_DEFAULT = exports.to_json_str = void 0;
|
|
53
53
|
exports._caller = _caller;
|
|
54
54
|
exports.log_trace = log_trace;
|
|
55
55
|
exports.log_debug = log_debug;
|
|
@@ -254,19 +254,23 @@ const format_order_lock_key = (chain_id, group_id, price_id, order_trace_id) =>
|
|
|
254
254
|
return `${chain_id}:lock:${group_id}:${price_id}:${order_trace_id}`;
|
|
255
255
|
};
|
|
256
256
|
exports.format_order_lock_key = format_order_lock_key;
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
return cache.getCache();
|
|
257
|
+
const getRedisClient = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
258
|
+
return cache.getRedisClient();
|
|
260
259
|
});
|
|
261
|
-
exports.
|
|
260
|
+
exports.getRedisClient = getRedisClient;
|
|
261
|
+
exports.getRedisCache = exports.getRedisClient;
|
|
262
262
|
const getLoadingCache = () => {
|
|
263
263
|
return new cache.LoadingCache();
|
|
264
264
|
};
|
|
265
265
|
exports.getLoadingCache = getLoadingCache;
|
|
266
266
|
const getRedisCommamd = () => {
|
|
267
|
-
return new cache.
|
|
267
|
+
return new cache.RedisClient();
|
|
268
268
|
};
|
|
269
269
|
exports.getRedisCommamd = getRedisCommamd;
|
|
270
|
+
const createRedisClient = (lockPrefix = '') => {
|
|
271
|
+
return new cache.RedisClient(lockPrefix);
|
|
272
|
+
};
|
|
273
|
+
exports.createRedisClient = createRedisClient;
|
|
270
274
|
const getArbCache = (env_args) => {
|
|
271
275
|
return new cache.ArbCache(env_args);
|
|
272
276
|
};
|