@cliz/inlets
Version:
Cloud Native Tunnel
187 lines (186 loc) • 9.62 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("@cliz/cli");
const fs = require("fs");
const path = require("path");
const doreamon_1 = require("@zodash/doreamon");
const config_1 = require("../config");
const server_1 = require("../core/server");
const utils_1 = require("../core/server/utils");
exports.default = (0, cli_1.defineSubCommand)(createCommand => {
return createCommand('inlets server')
.option('-d, --domain', 'Domain for server', { required: true })
.option('-p, --port <port>', 'Port for server (default 8080)', { default: 8080 })
.option('--tcp-port <tcpPort>', 'TCP Port for server (default 8443)', { default: config_1.default.defaultTCPPort })
.option('-s, --secure', 'Server with https, only for url', {
default: config_1.default.defaultSecure,
})
.option('-t, --token <token>', 'Token for authentication', { required: false })
.option('-c, --config <config>', 'Config file', {
default: cli_1.api.path.configfile('inlets.yml'),
})
.action(async ({ args, options: _options, program }) => {
var _a;
const options = _options;
const configLogger = doreamon_1.default.logger.getLogger('server:config');
const configRef = { current: null };
const loadConfig = async (configPath) => {
try {
if (await cli_1.api.fs.isExist(configPath)) {
const loadedConfig = await cli_1.api.fs.yml.load(configPath);
configRef.current = loadedConfig;
configLogger.info(`配置文件已加载: ${configPath}`);
return loadedConfig;
}
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
configLogger.error(`加载配置文件失败: ${message}`);
}
return null;
};
const configPath = path.resolve(options.config);
const initialConfig = await loadConfig(configPath);
if (!options.token && !(initialConfig === null || initialConfig === void 0 ? void 0 : initialConfig.token) && !(initialConfig === null || initialConfig === void 0 ? void 0 : initialConfig.clients)) {
throw new Error(`token or clients is required in ${options.config}`);
}
let notificationInstance = null;
if (initialConfig === null || initialConfig === void 0 ? void 0 : initialConfig.notification) {
notificationInstance = new utils_1.Notification(initialConfig.notification);
}
let watcher = null;
let reloadTimer = null;
if (await cli_1.api.fs.isExist(configPath)) {
watcher = fs.watch(configPath, async (eventType, filename) => {
if (eventType === 'change') {
configLogger.info(`检测到配置文件变化: ${filename}`);
if (reloadTimer) {
clearTimeout(reloadTimer);
}
reloadTimer = setTimeout(async () => {
var _a, _b, _c, _d, _e;
try {
const oldClientsCount = ((_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.clients) === null || _b === void 0 ? void 0 : _b.length) || 0;
await loadConfig(configPath);
const newClientsCount = ((_d = (_c = configRef.current) === null || _c === void 0 ? void 0 : _c.clients) === null || _d === void 0 ? void 0 : _d.length) || 0;
configLogger.info(`配置文件已热更新 (客户端数量: ${oldClientsCount} -> ${newClientsCount})`);
if ((_e = configRef.current) === null || _e === void 0 ? void 0 : _e.notification) {
notificationInstance = new utils_1.Notification(configRef.current.notification);
}
else {
notificationInstance = null;
}
if (notificationInstance) {
await notificationInstance.notify(`[配置更新] 配置文件已重新加载`, [
`配置文件路径: ${configPath}`,
`客户端数量: ${oldClientsCount} -> ${newClientsCount}`,
`当前时间: ${doreamon_1.default.date().format('YYYY-MM-DD HH:mm:ss')}`,
]);
}
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
configLogger.error(`热更新失败: ${message}`);
}
}, 200);
}
});
watcher.on('error', (error) => {
const message = error instanceof Error ? error.message : String(error);
configLogger.error(`文件监听出错: ${message}`);
});
configLogger.info(`已开始监听配置文件: ${configPath}`);
}
const cleanup = () => {
if (watcher) {
watcher.close();
watcher = null;
}
if (reloadTimer) {
clearTimeout(reloadTimer);
reloadTimer = null;
}
};
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
process.on('exit', cleanup);
const token = async (authType, clientId, _options) => {
var _a;
const currentConfig = configRef.current;
if (authType === 'credentials') {
const client = (_a = currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.clients) === null || _a === void 0 ? void 0 : _a.find(c => c.clientId === clientId);
if (!client || !client.clientSecret) {
throw new Error(`invalid client credentials for clientId: ${clientId}`);
}
return {
authType,
token: client.clientSecret,
};
}
if (authType === 'public') {
if ((_options === null || _options === void 0 ? void 0 : _options.type) !== 'http') {
throw new Error(`auth type public is only allowed by http type`);
}
return {
authType,
token: 'public',
};
}
const token = options.token || (currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.token);
if (!token) {
throw new Error(`unsupported auth type: ${authType}`);
}
return {
authType,
token: token,
config: {
version: program.getVersion(),
notification: currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.notification,
},
};
};
const get = (name) => {
var _a;
return options[name] || ((_a = configRef.current) === null || _a === void 0 ? void 0 : _a[name]);
};
const buildBandwidthLimits = () => {
var _a, _b, _c;
const currentConfig = configRef.current;
if (!(currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.bandwidthLimits) && !((_a = currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.clients) === null || _a === void 0 ? void 0 : _a.some(c => c.bandwidthLimit))) {
return undefined;
}
const limits = {
byClientId: {},
};
if ((_b = currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.bandwidthLimits) === null || _b === void 0 ? void 0 : _b.global) {
limits.global = currentConfig.bandwidthLimits.global;
}
if (currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.clients) {
for (const client of currentConfig.clients) {
if (client.bandwidthLimit) {
limits.byClientId[client.clientId] = client.bandwidthLimit;
}
}
}
if ((_c = currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.bandwidthLimits) === null || _c === void 0 ? void 0 : _c.clients) {
for (const [clientId, limit] of Object.entries(currentConfig.bandwidthLimits.clients)) {
limits.byClientId[clientId] = limit;
}
}
return limits;
};
const _opt = {
...args,
...options,
domain: get('domain'),
port: get('port'),
tcpPort: get('tcpPort'),
secure: get('secure'),
version: program.getVersion(),
token,
notification: ((_a = configRef.current) === null || _a === void 0 ? void 0 : _a.notification) || (initialConfig === null || initialConfig === void 0 ? void 0 : initialConfig.notification),
bandwidthLimits: buildBandwidthLimits(),
};
return (0, server_1.server)(_opt);
});
});