UNPKG

zan-proxy

Version:
354 lines 14.5 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const es6_promisify_1 = require("es6-promisify"); const events_1 = __importDefault(require("events")); const fs_1 = __importDefault(require("fs")); const jsonfile_1 = __importDefault(require("jsonfile")); const lodash_1 = require("lodash"); const node_fetch_1 = __importDefault(require("node-fetch")); const path_1 = __importDefault(require("path")); const typedi_1 = require("typedi"); const v4_1 = __importDefault(require("uuid/v4")); const appInfo_1 = require("./appInfo"); const jsonfileWriteFile = es6_promisify_1.promisify(jsonfile_1.default.writeFile); const fsUnlink = es6_promisify_1.promisify(fs_1.default.unlink); exports.ErrNameExists = new Error('name exists'); let RuleService = class RuleService extends events_1.default { constructor(appInfoService) { super(); // userId - > (filename -> rule) this.rules = {}; // 缓存数据: 正在使用的规则 userId -> inUsingRuleList this.usingRuleCache = {}; const proxyDataDir = appInfoService.getProxyDataDir(); this.ruleSaveDir = path_1.default.join(proxyDataDir, 'rule'); const contentMap = fs_1.default .readdirSync(this.ruleSaveDir) .filter(name => name.endsWith('.json')) .reduce((prev, curr) => { try { prev[curr] = jsonfile_1.default.readFileSync(path_1.default.join(this.ruleSaveDir, curr)); } catch (e) { // ignore } return prev; }, {}); lodash_1.forEach(contentMap, (content, fileName) => { const ruleName = content.name; const userId = fileName.substr(0, this._getUserIdLength(fileName, ruleName)); this.rules[userId] = this.rules[userId] || {}; this.rules[userId][ruleName] = content; }); } // 创建规则文件 createRuleFile(userId, name, description) { return __awaiter(this, void 0, void 0, function* () { if (this.rules[userId] && this.rules[userId][name]) { return exports.ErrNameExists; } const ruleFile = { checked: false, content: [], description, meta: { ETag: '', remote: false, remoteETag: '', url: '', }, name, }; this.rules[userId] = this.rules[userId] || {}; this.rules[userId][name] = ruleFile; // 写文件 const filePath = this._getRuleFilePath(userId, name); yield jsonfileWriteFile(filePath, ruleFile, { encoding: 'utf-8' }); // 发送消息通知 this.emit('data-change', userId, this.getRuleFileList(userId)); return true; }); } // 返回用户的规则文件列表 getRuleFileList(userId) { const ruleMap = (this.rules[userId] = this.rules[userId] || {}); const rulesLocal = []; const rulesRemote = []; lodash_1.forEach(ruleMap, content => { if (content.meta && content.meta.remote) { rulesRemote.push({ checked: content.checked, description: content.description, disableSync: content.disableSync, meta: content.meta, name: content.name, }); } else { rulesLocal.push({ checked: content.checked, description: content.description, meta: content.meta, name: content.name, }); } }); return rulesLocal.concat(rulesRemote); } // 删除规则文件 deleteRuleFile(userId, name) { return __awaiter(this, void 0, void 0, function* () { const rule = this.rules[userId][name]; delete this.rules[userId][name]; const ruleFilePath = this._getRuleFilePath(userId, name); yield fsUnlink(ruleFilePath); // 发送消息通知 this.emit('data-change', userId, this.getRuleFileList(userId)); if (rule.checked) { // 清空缓存 delete this.usingRuleCache[userId]; } }); } // 设置规则文件的使用状态 setRuleFileCheckStatus(userId, name, checked) { return __awaiter(this, void 0, void 0, function* () { this.rules[userId][name].checked = checked; const ruleFilePath = this._getRuleFilePath(userId, name); yield jsonfileWriteFile(ruleFilePath, this.rules[userId][name], { encoding: 'utf-8', }); // 发送消息通知 this.emit('data-change', userId, this.getRuleFileList(userId)); delete this.usingRuleCache[userId]; }); } // 设置规则文件的禁用同步状态 setRuleFileDisableSync(userId, name, disable) { return __awaiter(this, void 0, void 0, function* () { this.rules[userId][name].disableSync = disable; const ruleFilePath = this._getRuleFilePath(userId, name); yield jsonfileWriteFile(ruleFilePath, this.rules[userId][name], { encoding: 'utf-8', }); // 发送消息通知 this.emit('data-change', userId, this.getRuleFileList(userId)); delete this.usingRuleCache[userId]; }); } // 获取规则文件的内容 getRuleFile(userId, name) { return this.rules[userId][name]; } // 保存规则文件(可能是远程、或者本地) saveRuleFile(userId, ruleFile) { return __awaiter(this, void 0, void 0, function* () { const userRuleMap = this.rules[userId] || {}; const originRuleFile = userRuleMap[ruleFile.name]; if (originRuleFile) { ruleFile.checked = originRuleFile.checked; ruleFile.meta = originRuleFile.meta; } userRuleMap[ruleFile.name] = ruleFile; this.rules[userId] = userRuleMap; // 写文件 const filePath = this._getRuleFilePath(userId, ruleFile.name); yield jsonfileWriteFile(filePath, userRuleMap[ruleFile.name], { encoding: 'utf-8', }); // 清空缓存 delete this.usingRuleCache[userId]; this.emit('data-change', userId, this.getRuleFileList(userId)); }); } // 修改规则文件名称 updateFileInfo(userId, originName, { name, description, }) { return __awaiter(this, void 0, void 0, function* () { const userRuleMap = this.rules[userId] || {}; if (userRuleMap[name] && name !== originName) { throw exports.ErrNameExists; } const ruleFile = userRuleMap[originName]; // 删除旧的rule delete this.rules[userId][originName]; const ruleFilePath = this._getRuleFilePath(userId, originName); yield fsUnlink(ruleFilePath); // 修改rule名称 ruleFile.name = name; ruleFile.description = description; yield this.saveRuleFile(userId, ruleFile); }); } /** * 根据请求,获取处理请求的规则 * @param method * @param urlObj */ getProcessRule(userId, method, urlObj) { let candidateRule = null; const inusingRules = this._getInuseRules(userId); for (const rule of inusingRules) { // 捕获规则 if (this._isUrlMatch(urlObj.href, rule.match) && this._isMethodMatch(method, rule.method)) { candidateRule = rule; break; } } return candidateRule; } importRemoteRuleFile(userId, url) { return __awaiter(this, void 0, void 0, function* () { const ruleFile = yield this.fetchRemoteRuleFile(url); ruleFile.content.forEach(rule => { if (rule.action && !rule.actionList) { rule.actionList = [rule.action]; } }); yield this.saveRuleFile(userId, ruleFile); return ruleFile; }); } fetchRemoteRuleFile(url) { return __awaiter(this, void 0, void 0, function* () { const response = yield node_fetch_1.default(url); const responseData = yield response.json(); const ETag = response.headers.etag || ''; const content = responseData.content.map(remoteRule => { if (remoteRule.action && !remoteRule.actionList) { remoteRule.actionList = [remoteRule.action]; } const actionList = remoteRule.actionList.map(remoteAction => { const actionData = { dataId: '', headerKey: remoteAction.data.headerKey || '', headerValue: remoteAction.data.headerValue || '', target: remoteAction.data.target || '', }; const action = { data: actionData, type: remoteAction.type, }; return action; }); const rule = { actionList, checked: remoteRule.checked, key: remoteRule.key || v4_1.default(), match: remoteRule.match, method: remoteRule.method, name: remoteRule.name, }; return rule; }); const ruleFile = { checked: false, content, description: responseData.description, meta: { ETag, remote: true, remoteETag: ETag, url, }, name: responseData.name, }; return ruleFile; }); } copyRuleFile(userId, name) { return __awaiter(this, void 0, void 0, function* () { const ruleFile = this.getRuleFile(userId, name); if (!ruleFile) { return; } const copied = lodash_1.cloneDeep(ruleFile); copied.checked = false; copied.name = `${copied.name}-复制`; copied.meta = Object.assign({}, copied.meta, { isCopy: true, remote: false, }); yield this.saveRuleFile(userId, copied); return copied; }); } _getInuseRules(userId) { if (this.usingRuleCache[userId]) { return this.usingRuleCache[userId]; } const ruleMap = this.rules[userId] || {}; // 计算使用中的规则 const rulesLocal = []; const rulesRemote = []; lodash_1.forEach(ruleMap, (file, filename) => { if (!file.checked) { return; } lodash_1.forEach(file.content, rule => { if (!rule.checked) { return; } const copy = lodash_1.cloneDeep(rule); copy.ruleFileName = filename; if (file.meta && file.meta.remote) { rulesRemote.push(copy); } else { rulesLocal.push(copy); } }); }); const merged = rulesLocal.concat(rulesRemote); this.usingRuleCache[userId] = merged; return merged; } _getRuleFilePath(userId, ruleName) { const fileName = `${userId}_${ruleName}.json`; const filePath = path_1.default.join(this.ruleSaveDir, fileName); return filePath; } _getUserIdLength(ruleFileName, ruleName) { return ruleFileName.length - ruleName.length - 6; } // 请求的方法是否匹配规则 _isMethodMatch(reqMethod, ruleMethod) { const loweredReqMethod = lodash_1.lowerCase(reqMethod); const loweredRuleMethod = lodash_1.lowerCase(ruleMethod); return (loweredReqMethod === loweredRuleMethod || !ruleMethod || loweredReqMethod === 'option'); } // 请求的url是否匹配规则 _isUrlMatch(reqUrl, ruleMatchStr) { return (ruleMatchStr && (reqUrl.indexOf(ruleMatchStr) >= 0 || new RegExp(ruleMatchStr).test(reqUrl))); } }; RuleService = __decorate([ typedi_1.Service(), __metadata("design:paramtypes", [appInfo_1.AppInfoService]) ], RuleService); exports.RuleService = RuleService; //# sourceMappingURL=rule.js.map