xray-manager 1.8.5 → 2.0.0
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/README.md +102 -42
- package/dist/cli.mjs +8587 -0
- package/dist/cli.mjs.map +7 -0
- package/package.json +13 -5
- package/dist/cli.d.ts +0 -11
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -134
- package/dist/cli.js.map +0 -1
- package/dist/commands/clash.d.ts +0 -20
- package/dist/commands/clash.d.ts.map +0 -1
- package/dist/commands/clash.js +0 -156
- package/dist/commands/clash.js.map +0 -1
- package/dist/commands/config.d.ts +0 -56
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js +0 -461
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/interactive.d.ts +0 -55
- package/dist/commands/interactive.d.ts.map +0 -1
- package/dist/commands/interactive.js +0 -418
- package/dist/commands/interactive.js.map +0 -1
- package/dist/commands/logs.d.ts +0 -62
- package/dist/commands/logs.d.ts.map +0 -1
- package/dist/commands/logs.js +0 -389
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/quota.d.ts +0 -54
- package/dist/commands/quota.d.ts.map +0 -1
- package/dist/commands/quota.js +0 -661
- package/dist/commands/quota.js.map +0 -1
- package/dist/commands/review.d.ts +0 -8
- package/dist/commands/review.d.ts.map +0 -1
- package/dist/commands/review.js +0 -79
- package/dist/commands/review.js.map +0 -1
- package/dist/commands/service.d.ts +0 -43
- package/dist/commands/service.d.ts.map +0 -1
- package/dist/commands/service.js +0 -202
- package/dist/commands/service.js.map +0 -1
- package/dist/commands/user.d.ts +0 -49
- package/dist/commands/user.d.ts.map +0 -1
- package/dist/commands/user.js +0 -371
- package/dist/commands/user.js.map +0 -1
- package/dist/components/dashboard-widget.d.ts +0 -21
- package/dist/components/dashboard-widget.d.ts.map +0 -1
- package/dist/components/dashboard-widget.js +0 -234
- package/dist/components/dashboard-widget.js.map +0 -1
- package/dist/components/progress-bar.d.ts +0 -67
- package/dist/components/progress-bar.d.ts.map +0 -1
- package/dist/components/progress-bar.js +0 -138
- package/dist/components/progress-bar.js.map +0 -1
- package/dist/components/user-table.d.ts +0 -8
- package/dist/components/user-table.d.ts.map +0 -1
- package/dist/components/user-table.js +0 -91
- package/dist/components/user-table.js.map +0 -1
- package/dist/config/i18n.d.ts +0 -136
- package/dist/config/i18n.d.ts.map +0 -1
- package/dist/config/i18n.js +0 -323
- package/dist/config/i18n.js.map +0 -1
- package/dist/constants/error-codes.d.ts +0 -344
- package/dist/constants/error-codes.d.ts.map +0 -1
- package/dist/constants/error-codes.js +0 -462
- package/dist/constants/error-codes.js.map +0 -1
- package/dist/constants/exit-codes.d.ts +0 -49
- package/dist/constants/exit-codes.d.ts.map +0 -1
- package/dist/constants/exit-codes.js +0 -100
- package/dist/constants/exit-codes.js.map +0 -1
- package/dist/constants/paths.d.ts +0 -63
- package/dist/constants/paths.d.ts.map +0 -1
- package/dist/constants/paths.js +0 -78
- package/dist/constants/paths.js.map +0 -1
- package/dist/constants/quota.d.ts +0 -126
- package/dist/constants/quota.d.ts.map +0 -1
- package/dist/constants/quota.js +0 -135
- package/dist/constants/quota.js.map +0 -1
- package/dist/constants/review.d.ts +0 -14
- package/dist/constants/review.d.ts.map +0 -1
- package/dist/constants/review.js +0 -33
- package/dist/constants/review.js.map +0 -1
- package/dist/constants/supported-distros.d.ts +0 -17
- package/dist/constants/supported-distros.d.ts.map +0 -1
- package/dist/constants/supported-distros.js +0 -68
- package/dist/constants/supported-distros.js.map +0 -1
- package/dist/constants/theme.d.ts +0 -34
- package/dist/constants/theme.d.ts.map +0 -1
- package/dist/constants/theme.js +0 -52
- package/dist/constants/theme.js.map +0 -1
- package/dist/constants/timeouts.d.ts +0 -84
- package/dist/constants/timeouts.d.ts.map +0 -1
- package/dist/constants/timeouts.js +0 -111
- package/dist/constants/timeouts.js.map +0 -1
- package/dist/constants/ui-symbols.d.ts +0 -47
- package/dist/constants/ui-symbols.d.ts.map +0 -1
- package/dist/constants/ui-symbols.js +0 -48
- package/dist/constants/ui-symbols.js.map +0 -1
- package/dist/services/clash-config.d.ts +0 -16
- package/dist/services/clash-config.d.ts.map +0 -1
- package/dist/services/clash-config.js +0 -107
- package/dist/services/clash-config.js.map +0 -1
- package/dist/services/config-manager.d.ts +0 -90
- package/dist/services/config-manager.d.ts.map +0 -1
- package/dist/services/config-manager.js +0 -307
- package/dist/services/config-manager.js.map +0 -1
- package/dist/services/layout-manager.d.ts +0 -94
- package/dist/services/layout-manager.d.ts.map +0 -1
- package/dist/services/layout-manager.js +0 -192
- package/dist/services/layout-manager.js.map +0 -1
- package/dist/services/log-manager.d.ts +0 -157
- package/dist/services/log-manager.d.ts.map +0 -1
- package/dist/services/log-manager.js +0 -397
- package/dist/services/log-manager.js.map +0 -1
- package/dist/services/navigation-manager.d.ts +0 -23
- package/dist/services/navigation-manager.d.ts.map +0 -1
- package/dist/services/navigation-manager.js +0 -39
- package/dist/services/navigation-manager.js.map +0 -1
- package/dist/services/platform-detector.d.ts +0 -10
- package/dist/services/platform-detector.d.ts.map +0 -1
- package/dist/services/platform-detector.js +0 -106
- package/dist/services/platform-detector.js.map +0 -1
- package/dist/services/public-ip-manager.d.ts +0 -78
- package/dist/services/public-ip-manager.d.ts.map +0 -1
- package/dist/services/public-ip-manager.js +0 -296
- package/dist/services/public-ip-manager.js.map +0 -1
- package/dist/services/quota-enforcer.d.ts +0 -86
- package/dist/services/quota-enforcer.d.ts.map +0 -1
- package/dist/services/quota-enforcer.js +0 -151
- package/dist/services/quota-enforcer.js.map +0 -1
- package/dist/services/quota-manager.d.ts +0 -101
- package/dist/services/quota-manager.d.ts.map +0 -1
- package/dist/services/quota-manager.js +0 -257
- package/dist/services/quota-manager.js.map +0 -1
- package/dist/services/review/areas/community.d.ts +0 -9
- package/dist/services/review/areas/community.d.ts.map +0 -1
- package/dist/services/review/areas/community.js +0 -59
- package/dist/services/review/areas/community.js.map +0 -1
- package/dist/services/review/areas/contribution.d.ts +0 -9
- package/dist/services/review/areas/contribution.d.ts.map +0 -1
- package/dist/services/review/areas/contribution.js +0 -107
- package/dist/services/review/areas/contribution.js.map +0 -1
- package/dist/services/review/areas/documentation.d.ts +0 -9
- package/dist/services/review/areas/documentation.d.ts.map +0 -1
- package/dist/services/review/areas/documentation.js +0 -110
- package/dist/services/review/areas/documentation.js.map +0 -1
- package/dist/services/review/areas/license.d.ts +0 -9
- package/dist/services/review/areas/license.d.ts.map +0 -1
- package/dist/services/review/areas/license.js +0 -86
- package/dist/services/review/areas/license.js.map +0 -1
- package/dist/services/review/areas/quality.d.ts +0 -9
- package/dist/services/review/areas/quality.d.ts.map +0 -1
- package/dist/services/review/areas/quality.js +0 -84
- package/dist/services/review/areas/quality.js.map +0 -1
- package/dist/services/review/areas/security.d.ts +0 -9
- package/dist/services/review/areas/security.d.ts.map +0 -1
- package/dist/services/review/areas/security.js +0 -64
- package/dist/services/review/areas/security.js.map +0 -1
- package/dist/services/review/evidence.d.ts +0 -10
- package/dist/services/review/evidence.d.ts.map +0 -1
- package/dist/services/review/evidence.js +0 -40
- package/dist/services/review/evidence.js.map +0 -1
- package/dist/services/review/recommendations-filter.d.ts +0 -12
- package/dist/services/review/recommendations-filter.d.ts.map +0 -1
- package/dist/services/review/recommendations-filter.js +0 -20
- package/dist/services/review/recommendations-filter.js.map +0 -1
- package/dist/services/review/repo-scanner.d.ts +0 -8
- package/dist/services/review/repo-scanner.d.ts.map +0 -1
- package/dist/services/review/repo-scanner.js +0 -15
- package/dist/services/review/repo-scanner.js.map +0 -1
- package/dist/services/review/report-renderer.d.ts +0 -15
- package/dist/services/review/report-renderer.d.ts.map +0 -1
- package/dist/services/review/report-renderer.js +0 -120
- package/dist/services/review/report-renderer.js.map +0 -1
- package/dist/services/review/review-engine.d.ts +0 -15
- package/dist/services/review/review-engine.d.ts.map +0 -1
- package/dist/services/review/review-engine.js +0 -61
- package/dist/services/review/review-engine.js.map +0 -1
- package/dist/services/review/summary-builder.d.ts +0 -8
- package/dist/services/review/summary-builder.d.ts.map +0 -1
- package/dist/services/review/summary-builder.js +0 -26
- package/dist/services/review/summary-builder.js.map +0 -1
- package/dist/services/screen-manager.d.ts +0 -13
- package/dist/services/screen-manager.d.ts.map +0 -1
- package/dist/services/screen-manager.js +0 -42
- package/dist/services/screen-manager.js.map +0 -1
- package/dist/services/stats-config-manager.d.ts +0 -110
- package/dist/services/stats-config-manager.d.ts.map +0 -1
- package/dist/services/stats-config-manager.js +0 -443
- package/dist/services/stats-config-manager.js.map +0 -1
- package/dist/services/systemd-manager.d.ts +0 -197
- package/dist/services/systemd-manager.d.ts.map +0 -1
- package/dist/services/systemd-manager.js +0 -458
- package/dist/services/systemd-manager.js.map +0 -1
- package/dist/services/traffic-manager.d.ts +0 -106
- package/dist/services/traffic-manager.d.ts.map +0 -1
- package/dist/services/traffic-manager.js +0 -368
- package/dist/services/traffic-manager.js.map +0 -1
- package/dist/services/user-manager.d.ts +0 -90
- package/dist/services/user-manager.d.ts.map +0 -1
- package/dist/services/user-manager.js +0 -347
- package/dist/services/user-manager.js.map +0 -1
- package/dist/services/user-metadata-manager.d.ts +0 -85
- package/dist/services/user-metadata-manager.d.ts.map +0 -1
- package/dist/services/user-metadata-manager.js +0 -181
- package/dist/services/user-metadata-manager.js.map +0 -1
- package/dist/types/config.d.ts +0 -340
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js +0 -7
- package/dist/types/config.js.map +0 -1
- package/dist/types/layout.d.ts +0 -119
- package/dist/types/layout.d.ts.map +0 -1
- package/dist/types/layout.js +0 -142
- package/dist/types/layout.js.map +0 -1
- package/dist/types/platform.d.ts +0 -72
- package/dist/types/platform.d.ts.map +0 -1
- package/dist/types/platform.js +0 -38
- package/dist/types/platform.js.map +0 -1
- package/dist/types/quota.d.ts +0 -158
- package/dist/types/quota.d.ts.map +0 -1
- package/dist/types/quota.js +0 -26
- package/dist/types/quota.js.map +0 -1
- package/dist/types/review.d.ts +0 -62
- package/dist/types/review.d.ts.map +0 -1
- package/dist/types/review.js +0 -8
- package/dist/types/review.js.map +0 -1
- package/dist/types/server-config.d.ts +0 -53
- package/dist/types/server-config.d.ts.map +0 -1
- package/dist/types/server-config.js +0 -19
- package/dist/types/server-config.js.map +0 -1
- package/dist/types/service.d.ts +0 -96
- package/dist/types/service.d.ts.map +0 -1
- package/dist/types/service.js +0 -7
- package/dist/types/service.js.map +0 -1
- package/dist/types/terminal.d.ts +0 -35
- package/dist/types/terminal.d.ts.map +0 -1
- package/dist/types/terminal.js +0 -20
- package/dist/types/terminal.js.map +0 -1
- package/dist/types/ui-components.d.ts +0 -53
- package/dist/types/ui-components.d.ts.map +0 -1
- package/dist/types/ui-components.js +0 -3
- package/dist/types/ui-components.js.map +0 -1
- package/dist/types/user-metadata.d.ts +0 -43
- package/dist/types/user-metadata.d.ts.map +0 -1
- package/dist/types/user-metadata.js +0 -17
- package/dist/types/user-metadata.js.map +0 -1
- package/dist/types/user.d.ts +0 -134
- package/dist/types/user.d.ts.map +0 -1
- package/dist/types/user.js +0 -7
- package/dist/types/user.js.map +0 -1
- package/dist/utils/clipboard.d.ts +0 -21
- package/dist/utils/clipboard.d.ts.map +0 -1
- package/dist/utils/clipboard.js +0 -45
- package/dist/utils/clipboard.js.map +0 -1
- package/dist/utils/error-formatter.d.ts +0 -60
- package/dist/utils/error-formatter.d.ts.map +0 -1
- package/dist/utils/error-formatter.js +0 -244
- package/dist/utils/error-formatter.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -92
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -143
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/firewall.d.ts +0 -14
- package/dist/utils/firewall.d.ts.map +0 -1
- package/dist/utils/firewall.js +0 -78
- package/dist/utils/firewall.js.map +0 -1
- package/dist/utils/format.d.ts +0 -93
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.js +0 -240
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/icons.d.ts +0 -53
- package/dist/utils/icons.d.ts.map +0 -1
- package/dist/utils/icons.js +0 -143
- package/dist/utils/icons.js.map +0 -1
- package/dist/utils/layout.d.ts +0 -142
- package/dist/utils/layout.d.ts.map +0 -1
- package/dist/utils/layout.js +0 -381
- package/dist/utils/layout.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -160
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -351
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/network.d.ts +0 -39
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js +0 -227
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/os-detection.d.ts +0 -10
- package/dist/utils/os-detection.d.ts.map +0 -1
- package/dist/utils/os-detection.js +0 -86
- package/dist/utils/os-detection.js.map +0 -1
- package/dist/utils/preflight.d.ts +0 -57
- package/dist/utils/preflight.d.ts.map +0 -1
- package/dist/utils/preflight.js +0 -215
- package/dist/utils/preflight.js.map +0 -1
- package/dist/utils/repo-scan.d.ts +0 -54
- package/dist/utils/repo-scan.d.ts.map +0 -1
- package/dist/utils/repo-scan.js +0 -165
- package/dist/utils/repo-scan.js.map +0 -1
- package/dist/utils/report-output.d.ts +0 -13
- package/dist/utils/report-output.d.ts.map +0 -1
- package/dist/utils/report-output.js +0 -33
- package/dist/utils/report-output.js.map +0 -1
- package/dist/utils/review-id.d.ts +0 -8
- package/dist/utils/review-id.d.ts.map +0 -1
- package/dist/utils/review-id.js +0 -17
- package/dist/utils/review-id.js.map +0 -1
- package/dist/utils/splash.d.ts +0 -21
- package/dist/utils/splash.d.ts.map +0 -1
- package/dist/utils/splash.js +0 -179
- package/dist/utils/splash.js.map +0 -1
- package/dist/utils/terminal.d.ts +0 -32
- package/dist/utils/terminal.d.ts.map +0 -1
- package/dist/utils/terminal.js +0 -93
- package/dist/utils/terminal.js.map +0 -1
- package/dist/utils/traffic-formatter.d.ts +0 -45
- package/dist/utils/traffic-formatter.d.ts.map +0 -1
- package/dist/utils/traffic-formatter.js +0 -131
- package/dist/utils/traffic-formatter.js.map +0 -1
- package/dist/utils/validator.d.ts +0 -102
- package/dist/utils/validator.d.ts.map +0 -1
- package/dist/utils/validator.js +0 -238
- package/dist/utils/validator.js.map +0 -1
- package/dist/utils/vless-link.d.ts +0 -27
- package/dist/utils/vless-link.d.ts.map +0 -1
- package/dist/utils/vless-link.js +0 -69
- package/dist/utils/vless-link.js.map +0 -1
- package/dist/utils/which.d.ts +0 -13
- package/dist/utils/which.d.ts.map +0 -1
- package/dist/utils/which.js +0 -28
- package/dist/utils/which.js.map +0 -1
package/dist/commands/quota.js
DELETED
|
@@ -1,661 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Quota Command Handler
|
|
4
|
-
*
|
|
5
|
-
* Handles quota-related commands (set, show, reset, list)
|
|
6
|
-
*
|
|
7
|
-
* @module commands/quota
|
|
8
|
-
*/
|
|
9
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
-
};
|
|
12
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.promptQuotaInput = promptQuotaInput;
|
|
14
|
-
exports.setQuota = setQuota;
|
|
15
|
-
exports.showQuota = showQuota;
|
|
16
|
-
exports.resetQuota = resetQuota;
|
|
17
|
-
exports.listQuotas = listQuotas;
|
|
18
|
-
exports.reenableUser = reenableUser;
|
|
19
|
-
exports.configureStatsApi = configureStatsApi;
|
|
20
|
-
exports.executeQuotaCheck = executeQuotaCheck;
|
|
21
|
-
const quota_manager_1 = require("../services/quota-manager");
|
|
22
|
-
const traffic_manager_1 = require("../services/traffic-manager");
|
|
23
|
-
const user_manager_1 = require("../services/user-manager");
|
|
24
|
-
const stats_config_manager_1 = require("../services/stats-config-manager");
|
|
25
|
-
const quota_enforcer_1 = require("../services/quota-enforcer");
|
|
26
|
-
const traffic_formatter_1 = require("../utils/traffic-formatter");
|
|
27
|
-
const quota_1 = require("../constants/quota");
|
|
28
|
-
const logger_1 = __importDefault(require("../utils/logger"));
|
|
29
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
30
|
-
const ora_1 = __importDefault(require("ora"));
|
|
31
|
-
const prompts_1 = require("@inquirer/prompts");
|
|
32
|
-
const ui_symbols_1 = require("../constants/ui-symbols");
|
|
33
|
-
const layout_1 = require("../utils/layout");
|
|
34
|
-
const layout_manager_1 = __importDefault(require("../services/layout-manager"));
|
|
35
|
-
const i18n_1 = require("../config/i18n");
|
|
36
|
-
/**
|
|
37
|
-
* Get alert level color function
|
|
38
|
-
*/
|
|
39
|
-
function getAlertColor(level) {
|
|
40
|
-
switch (level) {
|
|
41
|
-
case 'exceeded':
|
|
42
|
-
return chalk_1.default.red;
|
|
43
|
-
case 'warning':
|
|
44
|
-
return chalk_1.default.yellow;
|
|
45
|
-
default:
|
|
46
|
-
return chalk_1.default.green;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Prompt user to setup Stats API if not available
|
|
51
|
-
* @returns true if Stats API is now available, false otherwise
|
|
52
|
-
*/
|
|
53
|
-
async function promptStatsApiSetup(options = {}) {
|
|
54
|
-
const statsManager = new stats_config_manager_1.StatsConfigManager(options.configPath, options.serviceName);
|
|
55
|
-
const detection = await statsManager.detectStatsConfig();
|
|
56
|
-
if (detection.available) {
|
|
57
|
-
if (detection.detectedPort) {
|
|
58
|
-
try {
|
|
59
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
60
|
-
await quotaManager.setApiPort(detection.detectedPort);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
logger_1.default.warn(`保存 API 端口失败: ${error.message}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
// Show detection result
|
|
69
|
-
logger_1.default.newline();
|
|
70
|
-
console.log(chalk_1.default.yellow(` ⚠️ ${detection.message}`));
|
|
71
|
-
logger_1.default.newline();
|
|
72
|
-
if (detection.missingComponents.length === 0) {
|
|
73
|
-
// Config exists but API not responding
|
|
74
|
-
console.log(chalk_1.default.gray(' Stats API 已配置但无法连接,请检查 Xray 服务状态'));
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
// Prompt for auto-configuration
|
|
78
|
-
const shouldConfigure = await (0, prompts_1.confirm)({
|
|
79
|
-
message: '是否自动配置 Stats API?配置后可查看流量统计',
|
|
80
|
-
default: true,
|
|
81
|
-
});
|
|
82
|
-
if (!shouldConfigure) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
// Execute configuration
|
|
86
|
-
const spinner = (0, ora_1.default)('正在配置 Stats API...').start();
|
|
87
|
-
spinner.text = '正在备份配置...';
|
|
88
|
-
const result = await statsManager.enableStatsApi();
|
|
89
|
-
if (result.success) {
|
|
90
|
-
spinner.succeed(chalk_1.default.green(result.message));
|
|
91
|
-
logger_1.default.newline();
|
|
92
|
-
console.log(chalk_1.default.cyan(' API 端口: ') + chalk_1.default.white(result.apiPort));
|
|
93
|
-
if (result.backupPath) {
|
|
94
|
-
console.log(chalk_1.default.cyan(' 备份文件: ') + chalk_1.default.gray(result.backupPath));
|
|
95
|
-
}
|
|
96
|
-
logger_1.default.newline();
|
|
97
|
-
try {
|
|
98
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
99
|
-
await quotaManager.setApiPort(result.apiPort);
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
logger_1.default.warn(`保存 API 端口失败: ${error.message}`);
|
|
103
|
-
}
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
spinner.fail(chalk_1.default.red(result.message));
|
|
108
|
-
if (result.error) {
|
|
109
|
-
console.log(chalk_1.default.red(` 错误: ${result.error}`));
|
|
110
|
-
}
|
|
111
|
-
if (result.rolledBack) {
|
|
112
|
-
console.log(chalk_1.default.yellow(' 已自动恢复原配置'));
|
|
113
|
-
}
|
|
114
|
-
if (result.backupPath) {
|
|
115
|
-
console.log(chalk_1.default.gray(` 备份文件: ${result.backupPath}`));
|
|
116
|
-
}
|
|
117
|
-
logger_1.default.newline();
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Parse quota input with validation
|
|
123
|
-
* Supports formats: "10GB", "500MB", "1TB", preset selection
|
|
124
|
-
*/
|
|
125
|
-
async function promptQuotaInput() {
|
|
126
|
-
// First, offer preset options
|
|
127
|
-
const presetChoices = quota_1.PRESET_QUOTAS.map((p) => ({
|
|
128
|
-
name: p.label,
|
|
129
|
-
value: p.bytes,
|
|
130
|
-
}));
|
|
131
|
-
presetChoices.push({
|
|
132
|
-
name: '自定义输入',
|
|
133
|
-
value: -2, // Special value for custom input
|
|
134
|
-
});
|
|
135
|
-
const selected = await (0, prompts_1.select)({
|
|
136
|
-
message: '选择流量配额:',
|
|
137
|
-
choices: presetChoices,
|
|
138
|
-
});
|
|
139
|
-
if (selected === -2) {
|
|
140
|
-
// Custom input
|
|
141
|
-
const customInput = await (0, prompts_1.input)({
|
|
142
|
-
message: '请输入配额 (例如: 10GB, 500MB, 1TB):',
|
|
143
|
-
validate: (value) => {
|
|
144
|
-
const bytes = (0, traffic_formatter_1.parseTraffic)(value);
|
|
145
|
-
if (bytes === -1 &&
|
|
146
|
-
value.toLowerCase() !== '无限制' &&
|
|
147
|
-
value.toLowerCase() !== 'unlimited') {
|
|
148
|
-
return '无效的配额格式,请使用如 10GB, 500MB, 1TB 的格式';
|
|
149
|
-
}
|
|
150
|
-
return true;
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
return (0, traffic_formatter_1.parseTraffic)(customInput);
|
|
154
|
-
}
|
|
155
|
-
return selected;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Set quota for a user
|
|
159
|
-
*/
|
|
160
|
-
async function setQuota(options = {}) {
|
|
161
|
-
try {
|
|
162
|
-
const userManager = new user_manager_1.UserManager(options.configPath, options.serviceName);
|
|
163
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
164
|
-
// List users first
|
|
165
|
-
const users = await userManager.listUsers();
|
|
166
|
-
if (users.length === 0) {
|
|
167
|
-
logger_1.default.warn('暂无用户,请先添加用户');
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
// Select user
|
|
171
|
-
const userChoices = users.map((u) => ({
|
|
172
|
-
name: `${u.email} (${u.id.substring(0, 8)}...)`,
|
|
173
|
-
value: u.email,
|
|
174
|
-
}));
|
|
175
|
-
const selectedEmail = await (0, prompts_1.select)({
|
|
176
|
-
message: '选择要设置配额的用户:',
|
|
177
|
-
choices: userChoices,
|
|
178
|
-
});
|
|
179
|
-
// Get current quota
|
|
180
|
-
const currentQuota = await quotaManager.getQuota(selectedEmail);
|
|
181
|
-
const currentDisplay = currentQuota.quotaBytes < 0 ? '无限制' : (0, traffic_formatter_1.formatTraffic)(currentQuota.quotaBytes).display;
|
|
182
|
-
logger_1.default.newline();
|
|
183
|
-
console.log(chalk_1.default.gray(`当前配额: ${currentDisplay}`));
|
|
184
|
-
logger_1.default.newline();
|
|
185
|
-
// Prompt for new quota
|
|
186
|
-
const quotaBytes = await promptQuotaInput();
|
|
187
|
-
const spinner = (0, ora_1.default)('正在设置配额...').start();
|
|
188
|
-
await quotaManager.setQuota({
|
|
189
|
-
email: selectedEmail,
|
|
190
|
-
quotaBytes,
|
|
191
|
-
quotaType: quotaBytes < 0 ? 'unlimited' : 'limited',
|
|
192
|
-
});
|
|
193
|
-
spinner.succeed(chalk_1.default.green('配额设置成功!'));
|
|
194
|
-
const newDisplay = quotaBytes < 0 ? '无限制' : (0, traffic_formatter_1.formatTraffic)(quotaBytes).display;
|
|
195
|
-
logger_1.default.newline();
|
|
196
|
-
console.log(chalk_1.default.cyan(' 用户: ') + chalk_1.default.white(selectedEmail));
|
|
197
|
-
console.log(chalk_1.default.cyan(' 新配额: ') + chalk_1.default.white(newDisplay));
|
|
198
|
-
logger_1.default.newline();
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
logger_1.default.error(error.message);
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Show quota details for a user
|
|
207
|
-
*/
|
|
208
|
-
async function showQuota(options = {}) {
|
|
209
|
-
try {
|
|
210
|
-
const userManager = new user_manager_1.UserManager(options.configPath, options.serviceName);
|
|
211
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
212
|
-
const trafficManager = new traffic_manager_1.TrafficManager();
|
|
213
|
-
// List users first
|
|
214
|
-
const users = await userManager.listUsers();
|
|
215
|
-
if (users.length === 0) {
|
|
216
|
-
logger_1.default.warn('暂无用户');
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
// Select user
|
|
220
|
-
const userChoices = users.map((u) => ({
|
|
221
|
-
name: `${u.email} (${u.id.substring(0, 8)}...)`,
|
|
222
|
-
value: u.email,
|
|
223
|
-
}));
|
|
224
|
-
const selectedEmail = await (0, prompts_1.select)({
|
|
225
|
-
message: '选择要查看的用户:',
|
|
226
|
-
choices: userChoices,
|
|
227
|
-
});
|
|
228
|
-
const spinner = (0, ora_1.default)('正在获取配额信息...').start();
|
|
229
|
-
// Get quota and usage
|
|
230
|
-
const quota = await quotaManager.getQuota(selectedEmail);
|
|
231
|
-
let usage = await trafficManager.getUsage(selectedEmail);
|
|
232
|
-
spinner.stop();
|
|
233
|
-
const terminalSize = layout_manager_1.default.detectTerminalSize();
|
|
234
|
-
const headerTitle = `${ui_symbols_1.menuIcons.STATS} 用户配额详情`;
|
|
235
|
-
const headerText = (0, layout_1.renderHeader)(headerTitle, terminalSize.width, 'left');
|
|
236
|
-
logger_1.default.newline();
|
|
237
|
-
logger_1.default.separator();
|
|
238
|
-
console.log(chalk_1.default.bold.cyan(headerText));
|
|
239
|
-
logger_1.default.separator();
|
|
240
|
-
logger_1.default.newline();
|
|
241
|
-
// User info
|
|
242
|
-
console.log(chalk_1.default.cyan(' 用户: ') + chalk_1.default.white(selectedEmail));
|
|
243
|
-
console.log(chalk_1.default.cyan(' 状态: ') +
|
|
244
|
-
(quota.status === 'active' ? chalk_1.default.green('活跃') : chalk_1.default.red('已禁用')));
|
|
245
|
-
logger_1.default.newline();
|
|
246
|
-
// Quota info
|
|
247
|
-
const quotaDisplay = quota.quotaBytes < 0 ? '无限制' : (0, traffic_formatter_1.formatTraffic)(quota.quotaBytes).display;
|
|
248
|
-
console.log(chalk_1.default.cyan(' 配额: ') + chalk_1.default.white(quotaDisplay));
|
|
249
|
-
// Usage info
|
|
250
|
-
if (usage) {
|
|
251
|
-
const usedDisplay = (0, traffic_formatter_1.formatTraffic)(usage.total).display;
|
|
252
|
-
const percent = (0, traffic_formatter_1.calculateUsagePercent)(usage.total, quota.quotaBytes);
|
|
253
|
-
const alertLevel = (0, traffic_formatter_1.getAlertLevel)(percent);
|
|
254
|
-
const colorFn = getAlertColor(alertLevel);
|
|
255
|
-
console.log(chalk_1.default.cyan(' 已用: ') + colorFn(usedDisplay));
|
|
256
|
-
console.log(chalk_1.default.cyan(' 使用率: ') + colorFn(`${percent}%`));
|
|
257
|
-
if (quota.quotaBytes > 0) {
|
|
258
|
-
const remaining = Math.max(0, quota.quotaBytes - usage.total);
|
|
259
|
-
console.log(chalk_1.default.cyan(' 剩余: ') + chalk_1.default.white((0, traffic_formatter_1.formatTraffic)(remaining).display));
|
|
260
|
-
}
|
|
261
|
-
logger_1.default.newline();
|
|
262
|
-
console.log(chalk_1.default.gray(` 上行: ${(0, traffic_formatter_1.formatTraffic)(usage.uplink).display}`));
|
|
263
|
-
console.log(chalk_1.default.gray(` 下行: ${(0, traffic_formatter_1.formatTraffic)(usage.downlink).display}`));
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
// Stats API not available - prompt for setup
|
|
267
|
-
const configured = await promptStatsApiSetup(options);
|
|
268
|
-
if (configured) {
|
|
269
|
-
// Retry getting usage after configuration
|
|
270
|
-
usage = await trafficManager.getUsage(selectedEmail);
|
|
271
|
-
if (usage) {
|
|
272
|
-
const usedDisplay = (0, traffic_formatter_1.formatTraffic)(usage.total).display;
|
|
273
|
-
const percent = (0, traffic_formatter_1.calculateUsagePercent)(usage.total, quota.quotaBytes);
|
|
274
|
-
const alertLevel = (0, traffic_formatter_1.getAlertLevel)(percent);
|
|
275
|
-
const colorFn = getAlertColor(alertLevel);
|
|
276
|
-
console.log(chalk_1.default.cyan(' 已用: ') + colorFn(usedDisplay));
|
|
277
|
-
console.log(chalk_1.default.cyan(' 使用率: ') + colorFn(`${percent}%`));
|
|
278
|
-
if (quota.quotaBytes > 0) {
|
|
279
|
-
const remaining = Math.max(0, quota.quotaBytes - usage.total);
|
|
280
|
-
console.log(chalk_1.default.cyan(' 剩余: ') + chalk_1.default.white((0, traffic_formatter_1.formatTraffic)(remaining).display));
|
|
281
|
-
}
|
|
282
|
-
logger_1.default.newline();
|
|
283
|
-
console.log(chalk_1.default.gray(` 上行: ${(0, traffic_formatter_1.formatTraffic)(usage.uplink).display}`));
|
|
284
|
-
console.log(chalk_1.default.gray(` 下行: ${(0, traffic_formatter_1.formatTraffic)(usage.downlink).display}`));
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
logger_1.default.newline();
|
|
289
|
-
console.log(chalk_1.default.gray(` 上次重置: ${quota.lastReset}`));
|
|
290
|
-
logger_1.default.newline();
|
|
291
|
-
if (options.json) {
|
|
292
|
-
console.log(JSON.stringify({ quota, usage }, null, 2));
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
catch (error) {
|
|
296
|
-
logger_1.default.error(error.message);
|
|
297
|
-
process.exit(1);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Reset usage for a user
|
|
302
|
-
*/
|
|
303
|
-
async function resetQuota(options = {}) {
|
|
304
|
-
try {
|
|
305
|
-
const userManager = new user_manager_1.UserManager(options.configPath, options.serviceName);
|
|
306
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
307
|
-
// List users first
|
|
308
|
-
const users = await userManager.listUsers();
|
|
309
|
-
if (users.length === 0) {
|
|
310
|
-
logger_1.default.warn('暂无用户');
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
// Select user
|
|
314
|
-
const userChoices = users.map((u) => ({
|
|
315
|
-
name: `${u.email} (${u.id.substring(0, 8)}...)`,
|
|
316
|
-
value: u.email,
|
|
317
|
-
}));
|
|
318
|
-
const selectedEmail = await (0, prompts_1.select)({
|
|
319
|
-
message: '选择要重置流量的用户:',
|
|
320
|
-
choices: userChoices,
|
|
321
|
-
});
|
|
322
|
-
// Confirm
|
|
323
|
-
const confirmed = await (0, prompts_1.confirm)({
|
|
324
|
-
message: `确定要重置 ${selectedEmail} 的已用流量吗?`,
|
|
325
|
-
default: false,
|
|
326
|
-
});
|
|
327
|
-
if (!confirmed) {
|
|
328
|
-
logger_1.default.info('操作已取消');
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
const spinner = (0, ora_1.default)('正在重置流量...').start();
|
|
332
|
-
await quotaManager.resetUsage(selectedEmail);
|
|
333
|
-
spinner.succeed(chalk_1.default.green('流量重置成功!'));
|
|
334
|
-
logger_1.default.newline();
|
|
335
|
-
}
|
|
336
|
-
catch (error) {
|
|
337
|
-
logger_1.default.error(error.message);
|
|
338
|
-
process.exit(1);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* List all users with quota info
|
|
343
|
-
*/
|
|
344
|
-
async function listQuotas(options = {}) {
|
|
345
|
-
try {
|
|
346
|
-
const userManager = new user_manager_1.UserManager(options.configPath, options.serviceName);
|
|
347
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
348
|
-
const trafficManager = new traffic_manager_1.TrafficManager();
|
|
349
|
-
let statsAvailable = await trafficManager.isUsageAvailable();
|
|
350
|
-
const shouldPromptStats = !options.json && !process.env.VITEST && process.env.NODE_ENV !== 'test';
|
|
351
|
-
if (!statsAvailable && shouldPromptStats) {
|
|
352
|
-
const configured = await promptStatsApiSetup(options);
|
|
353
|
-
if (configured) {
|
|
354
|
-
statsAvailable = await trafficManager.isUsageAvailable();
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
const spinner = (0, ora_1.default)('正在获取配额信息...').start();
|
|
358
|
-
const users = await userManager.listUsers();
|
|
359
|
-
const quotas = await quotaManager.getAllQuotas();
|
|
360
|
-
const usages = statsAvailable ? await trafficManager.getAllUsage() : [];
|
|
361
|
-
spinner.stop();
|
|
362
|
-
const terminalSize = layout_manager_1.default.detectTerminalSize();
|
|
363
|
-
const headerTitle = `${ui_symbols_1.menuIcons.STATS} 用户配额列表 (共 ${users.length} 个用户)`;
|
|
364
|
-
const headerText = (0, layout_1.renderHeader)(headerTitle, terminalSize.width, 'left');
|
|
365
|
-
logger_1.default.newline();
|
|
366
|
-
logger_1.default.separator();
|
|
367
|
-
console.log(chalk_1.default.bold.cyan(headerText));
|
|
368
|
-
logger_1.default.separator();
|
|
369
|
-
logger_1.default.newline();
|
|
370
|
-
if (users.length === 0) {
|
|
371
|
-
console.log(chalk_1.default.gray(' 暂无用户'));
|
|
372
|
-
logger_1.default.newline();
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
if (!statsAvailable) {
|
|
376
|
-
console.log(chalk_1.default.yellow(' 流量统计未启用,无法显示实际使用量。'));
|
|
377
|
-
console.log(chalk_1.default.gray(' 提示: 配额管理 → 配置 Stats API'));
|
|
378
|
-
logger_1.default.newline();
|
|
379
|
-
}
|
|
380
|
-
// Build user list with quota info
|
|
381
|
-
const usersWithQuota = [];
|
|
382
|
-
for (const user of users) {
|
|
383
|
-
const quota = quotas[user.email] || {
|
|
384
|
-
quotaBytes: -1,
|
|
385
|
-
quotaType: 'unlimited',
|
|
386
|
-
usedBytes: 0,
|
|
387
|
-
lastReset: '',
|
|
388
|
-
status: 'active',
|
|
389
|
-
};
|
|
390
|
-
const usage = statsAvailable ? usages.find((u) => u.email === user.email) : undefined;
|
|
391
|
-
const usedBytes = statsAvailable ? usage?.total || 0 : 0;
|
|
392
|
-
const percent = statsAvailable ? (0, traffic_formatter_1.calculateUsagePercent)(usedBytes, quota.quotaBytes) : 0;
|
|
393
|
-
const alertLevel = statsAvailable ? (0, traffic_formatter_1.getAlertLevel)(percent) : 'normal';
|
|
394
|
-
usersWithQuota.push({
|
|
395
|
-
...user,
|
|
396
|
-
quota,
|
|
397
|
-
usage,
|
|
398
|
-
usagePercent: percent,
|
|
399
|
-
alertLevel,
|
|
400
|
-
quotaDisplay: quota.quotaBytes < 0 ? '无限制' : (0, traffic_formatter_1.formatTraffic)(quota.quotaBytes).display,
|
|
401
|
-
usageDisplay: statsAvailable
|
|
402
|
-
? (0, traffic_formatter_1.formatUsageSummary)(usedBytes, quota.quotaBytes)
|
|
403
|
-
: '统计未启用',
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
// Display table
|
|
407
|
-
for (const user of usersWithQuota) {
|
|
408
|
-
const colorFn = statsAvailable ? getAlertColor(user.alertLevel) : chalk_1.default.gray;
|
|
409
|
-
const statusIcon = statsAvailable
|
|
410
|
-
? user.alertLevel === 'exceeded'
|
|
411
|
-
? '🔴'
|
|
412
|
-
: user.alertLevel === 'warning'
|
|
413
|
-
? '🟡'
|
|
414
|
-
: '🟢'
|
|
415
|
-
: '⚪';
|
|
416
|
-
console.log(` ${statusIcon} ${chalk_1.default.white(user.email)}`);
|
|
417
|
-
console.log(` 配额: ${chalk_1.default.cyan(user.quotaDisplay)}`);
|
|
418
|
-
console.log(` 使用: ${colorFn(user.usageDisplay)}`);
|
|
419
|
-
logger_1.default.newline();
|
|
420
|
-
}
|
|
421
|
-
if (options.json) {
|
|
422
|
-
console.log(JSON.stringify(usersWithQuota, null, 2));
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
logger_1.default.error(error.message);
|
|
427
|
-
process.exit(1);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Re-enable a disabled user
|
|
432
|
-
*/
|
|
433
|
-
async function reenableUser(_options = {}) {
|
|
434
|
-
try {
|
|
435
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
436
|
-
// Get all quotas and filter disabled users
|
|
437
|
-
const quotas = await quotaManager.getAllQuotas();
|
|
438
|
-
const disabledUsers = Object.entries(quotas)
|
|
439
|
-
.filter(([, q]) => q.status === 'disabled' || q.status === 'exceeded')
|
|
440
|
-
.map(([email]) => email);
|
|
441
|
-
if (disabledUsers.length === 0) {
|
|
442
|
-
logger_1.default.info('没有被禁用的用户');
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
// Select user
|
|
446
|
-
const userChoices = disabledUsers.map((email) => ({
|
|
447
|
-
name: email,
|
|
448
|
-
value: email,
|
|
449
|
-
}));
|
|
450
|
-
const selectedEmail = await (0, prompts_1.select)({
|
|
451
|
-
message: '选择要重新启用的用户:',
|
|
452
|
-
choices: userChoices,
|
|
453
|
-
});
|
|
454
|
-
// Confirm
|
|
455
|
-
const confirmed = await (0, prompts_1.confirm)({
|
|
456
|
-
message: `确定要重新启用 ${selectedEmail} 吗?`,
|
|
457
|
-
default: true,
|
|
458
|
-
});
|
|
459
|
-
if (!confirmed) {
|
|
460
|
-
logger_1.default.info('操作已取消');
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
const spinner = (0, ora_1.default)('正在重新启用用户...').start();
|
|
464
|
-
await quotaManager.setStatus(selectedEmail, 'active');
|
|
465
|
-
spinner.succeed(chalk_1.default.green('用户已重新启用!'));
|
|
466
|
-
logger_1.default.newline();
|
|
467
|
-
}
|
|
468
|
-
catch (error) {
|
|
469
|
-
logger_1.default.error(error.message);
|
|
470
|
-
process.exit(1);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Configure Stats API manually
|
|
475
|
-
*/
|
|
476
|
-
async function configureStatsApi(options = {}) {
|
|
477
|
-
try {
|
|
478
|
-
const statsManager = new stats_config_manager_1.StatsConfigManager(options.configPath, options.serviceName);
|
|
479
|
-
const detection = await statsManager.detectStatsConfig();
|
|
480
|
-
const terminalSize = layout_manager_1.default.detectTerminalSize();
|
|
481
|
-
const headerTitle = `${ui_symbols_1.menuIcons.CONFIG} Stats API 配置`;
|
|
482
|
-
const headerText = (0, layout_1.renderHeader)(headerTitle, terminalSize.width, 'left');
|
|
483
|
-
logger_1.default.newline();
|
|
484
|
-
logger_1.default.separator();
|
|
485
|
-
console.log(chalk_1.default.bold.cyan(headerText));
|
|
486
|
-
logger_1.default.separator();
|
|
487
|
-
logger_1.default.newline();
|
|
488
|
-
// Show current status
|
|
489
|
-
console.log(chalk_1.default.cyan(' 当前状态: ') +
|
|
490
|
-
(detection.available ? chalk_1.default.green('已配置且可用') : chalk_1.default.yellow('未配置或不可用')));
|
|
491
|
-
if (detection.available && detection.detectedPort) {
|
|
492
|
-
console.log(chalk_1.default.cyan(' API 端口: ') + chalk_1.default.white(detection.detectedPort));
|
|
493
|
-
console.log(chalk_1.default.cyan(' 服务状态: ') +
|
|
494
|
-
(detection.serviceRunning ? chalk_1.default.green('运行中') : chalk_1.default.red('已停止')));
|
|
495
|
-
logger_1.default.newline();
|
|
496
|
-
try {
|
|
497
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
498
|
-
await quotaManager.setApiPort(detection.detectedPort);
|
|
499
|
-
}
|
|
500
|
-
catch (error) {
|
|
501
|
-
logger_1.default.warn(`保存 API 端口失败: ${error.message}`);
|
|
502
|
-
}
|
|
503
|
-
logger_1.default.info('Stats API 已配置,无需重新配置');
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
// Show missing components
|
|
507
|
-
if (detection.missingComponents.length > 0) {
|
|
508
|
-
const componentNames = {
|
|
509
|
-
stats: 'stats 配置块',
|
|
510
|
-
policy: 'policy 配置',
|
|
511
|
-
api: 'API 配置',
|
|
512
|
-
'api-inbound': 'API 入站配置',
|
|
513
|
-
'api-outbound': 'API 出站配置',
|
|
514
|
-
'api-routing': 'API 路由规则',
|
|
515
|
-
};
|
|
516
|
-
const missingNames = detection.missingComponents
|
|
517
|
-
.map((c) => componentNames[c] || c)
|
|
518
|
-
.join('、');
|
|
519
|
-
console.log(chalk_1.default.cyan(' 缺失组件: ') + chalk_1.default.yellow(missingNames));
|
|
520
|
-
}
|
|
521
|
-
logger_1.default.newline();
|
|
522
|
-
// Show benefits
|
|
523
|
-
console.log(chalk_1.default.gray(' 配置 Stats API 后,您可以:'));
|
|
524
|
-
console.log(chalk_1.default.gray(' • 查看用户实时流量使用情况'));
|
|
525
|
-
console.log(chalk_1.default.gray(' • 设置流量配额并自动限制'));
|
|
526
|
-
console.log(chalk_1.default.gray(' • 查看流量统计报表'));
|
|
527
|
-
logger_1.default.newline();
|
|
528
|
-
// Confirm configuration
|
|
529
|
-
const shouldConfigure = await (0, prompts_1.confirm)({
|
|
530
|
-
message: '是否立即配置 Stats API?',
|
|
531
|
-
default: true,
|
|
532
|
-
});
|
|
533
|
-
if (!shouldConfigure) {
|
|
534
|
-
logger_1.default.info('操作已取消');
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
// Execute configuration
|
|
538
|
-
const spinner = (0, ora_1.default)('正在配置 Stats API...').start();
|
|
539
|
-
spinner.text = '正在备份配置...';
|
|
540
|
-
const result = await statsManager.enableStatsApi();
|
|
541
|
-
if (result.success) {
|
|
542
|
-
spinner.succeed(chalk_1.default.green(result.message));
|
|
543
|
-
logger_1.default.newline();
|
|
544
|
-
console.log(chalk_1.default.cyan(' API 端口: ') + chalk_1.default.white(result.apiPort));
|
|
545
|
-
if (result.backupPath) {
|
|
546
|
-
console.log(chalk_1.default.cyan(' 备份文件: ') + chalk_1.default.gray(result.backupPath));
|
|
547
|
-
}
|
|
548
|
-
logger_1.default.newline();
|
|
549
|
-
try {
|
|
550
|
-
const quotaManager = new quota_manager_1.QuotaManager();
|
|
551
|
-
await quotaManager.setApiPort(result.apiPort);
|
|
552
|
-
}
|
|
553
|
-
catch (error) {
|
|
554
|
-
logger_1.default.warn(`保存 API 端口失败: ${error.message}`);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
spinner.fail(chalk_1.default.red(result.message));
|
|
559
|
-
if (result.error) {
|
|
560
|
-
console.log(chalk_1.default.red(` 错误: ${result.error}`));
|
|
561
|
-
}
|
|
562
|
-
if (result.rolledBack) {
|
|
563
|
-
console.log(chalk_1.default.yellow(' 已自动恢复原配置'));
|
|
564
|
-
}
|
|
565
|
-
if (result.backupPath) {
|
|
566
|
-
console.log(chalk_1.default.gray(` 备份文件: ${result.backupPath}`));
|
|
567
|
-
}
|
|
568
|
-
logger_1.default.newline();
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
catch (error) {
|
|
572
|
-
logger_1.default.error(error.message);
|
|
573
|
-
process.exit(1);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Execute quota check and enforce limits
|
|
578
|
-
*
|
|
579
|
-
* @param options - Command options
|
|
580
|
-
*/
|
|
581
|
-
async function executeQuotaCheck(options = {}) {
|
|
582
|
-
const translations = (0, i18n_1.t)();
|
|
583
|
-
try {
|
|
584
|
-
const terminalSize = layout_manager_1.default.detectTerminalSize();
|
|
585
|
-
const headerTitle = `${ui_symbols_1.menuIcons.QUOTA} ${translations.quota.executeCheck}`;
|
|
586
|
-
const headerText = (0, layout_1.renderHeader)(headerTitle, terminalSize.width, 'left');
|
|
587
|
-
logger_1.default.newline();
|
|
588
|
-
logger_1.default.separator();
|
|
589
|
-
console.log(chalk_1.default.bold.cyan(headerText));
|
|
590
|
-
logger_1.default.separator();
|
|
591
|
-
logger_1.default.newline();
|
|
592
|
-
// Check if Stats API is available
|
|
593
|
-
const statsAvailable = await promptStatsApiSetup(options);
|
|
594
|
-
if (!statsAvailable) {
|
|
595
|
-
logger_1.default.warn('Stats API 不可用,无法执行配额检查');
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
// Execute quota enforcement
|
|
599
|
-
const spinner = (0, ora_1.default)('正在检查用户配额...').start();
|
|
600
|
-
const enforcer = new quota_enforcer_1.QuotaEnforcer(options.configPath, options.serviceName);
|
|
601
|
-
const summary = await enforcer.enforceQuotas(true); // Auto-disable exceeded users
|
|
602
|
-
spinner.succeed(chalk_1.default.green(translations.quota.checkComplete));
|
|
603
|
-
logger_1.default.newline();
|
|
604
|
-
// Display summary
|
|
605
|
-
console.log(chalk_1.default.cyan(' 检查结果:'));
|
|
606
|
-
logger_1.default.newline();
|
|
607
|
-
// Normal users
|
|
608
|
-
console.log(chalk_1.default.green(` ✓ ${translations.quota.normalUsers}: ${summary.normalCount}`));
|
|
609
|
-
// Warning users
|
|
610
|
-
if (summary.warningCount > 0) {
|
|
611
|
-
console.log(chalk_1.default.yellow(` ⚠ ${translations.quota.warningUsers}: ${summary.warningCount}`));
|
|
612
|
-
}
|
|
613
|
-
// Exceeded users
|
|
614
|
-
if (summary.exceededCount > 0) {
|
|
615
|
-
console.log(chalk_1.default.red(` ✗ ${translations.quota.exceededUsers}: ${summary.exceededCount} (${translations.quota.disabledUsers})`));
|
|
616
|
-
}
|
|
617
|
-
// Newly disabled
|
|
618
|
-
if (summary.newlyDisabledCount > 0) {
|
|
619
|
-
console.log(chalk_1.default.red(` ! 新禁用: ${summary.newlyDisabledCount}`));
|
|
620
|
-
}
|
|
621
|
-
logger_1.default.newline();
|
|
622
|
-
// Show details if there are exceeded users
|
|
623
|
-
if (summary.exceededCount > 0 && summary.results) {
|
|
624
|
-
console.log(chalk_1.default.cyan(' 超限用户详情:'));
|
|
625
|
-
for (const detail of summary.results) {
|
|
626
|
-
if (detail.alertLevel === 'exceeded') {
|
|
627
|
-
const usedFormatted = (0, traffic_formatter_1.formatTraffic)(detail.usedBytes);
|
|
628
|
-
const quotaFormatted = (0, traffic_formatter_1.formatTraffic)(detail.quotaBytes);
|
|
629
|
-
console.log(chalk_1.default.red(` • ${detail.email}: ${usedFormatted} / ${quotaFormatted}`));
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
logger_1.default.newline();
|
|
633
|
-
}
|
|
634
|
-
// Update user metadata status
|
|
635
|
-
const userManager = new user_manager_1.UserManager(options.configPath, options.serviceName);
|
|
636
|
-
const metadataManager = userManager.getMetadataManager();
|
|
637
|
-
if (summary.results) {
|
|
638
|
-
for (const detail of summary.results) {
|
|
639
|
-
if (detail.alertLevel === 'exceeded') {
|
|
640
|
-
try {
|
|
641
|
-
// Find user by email to get UUID
|
|
642
|
-
const users = await userManager.listUsers();
|
|
643
|
-
const user = users.find((u) => u.email === detail.email);
|
|
644
|
-
if (user) {
|
|
645
|
-
await metadataManager.updateStatus(user.id, 'exceeded');
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
catch {
|
|
649
|
-
// Ignore metadata update errors
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
logger_1.default.info('配额检查完成');
|
|
655
|
-
}
|
|
656
|
-
catch (error) {
|
|
657
|
-
logger_1.default.error(error.message);
|
|
658
|
-
process.exit(1);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
//# sourceMappingURL=quota.js.map
|