1 | ;
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | return new (P || (P = Promise))(function (resolve, reject) {
|
4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
7 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8 | });
|
9 | };
|
10 | var __generator = (this && this.__generator) || function (thisArg, body) {
|
11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
13 | function verb(n) { return function (v) { return step([n, v]); }; }
|
14 | function step(op) {
|
15 | if (f) throw new TypeError("Generator is already executing.");
|
16 | while (_) try {
|
17 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
18 | if (y = 0, t) op = [op[0] & 2, t.value];
|
19 | switch (op[0]) {
|
20 | case 0: case 1: t = op; break;
|
21 | case 4: _.label++; return { value: op[1], done: false };
|
22 | case 5: _.label++; y = op[1]; op = [0]; continue;
|
23 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
24 | default:
|
25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
29 | if (t[2]) _.ops.pop();
|
30 | _.trys.pop(); continue;
|
31 | }
|
32 | op = body.call(thisArg, _);
|
33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
35 | }
|
36 | };
|
37 | Object.defineProperty(exports, "__esModule", { value: true });
|
38 | // 根据架构设计约定自动识别各种运行环境
|
39 | if (process.env['NODE_ENV']) {
|
40 | global['__env__'] = process.env['NODE_ENV'];
|
41 | }
|
42 | else {
|
43 | if (process.argv.length >= 3 && process.argv[2] == 'https') {
|
44 | // 预发环境配置
|
45 | global['__env__'] = 'pre';
|
46 | }
|
47 | else if (process.argv.length >= 3 && process.argv[2] == 'http') {
|
48 | // 日常环境配置
|
49 | global['__env__'] = 'daily';
|
50 | }
|
51 | else {
|
52 | // 本地环境配置
|
53 | global['__env__'] = 'local';
|
54 | }
|
55 | }
|
56 | if (global['__env__'] != 'local') {
|
57 | require('source-map-support').install();
|
58 | }
|
59 | require('tsconfig-paths').register();
|
60 | var express = require('express');
|
61 | var app = express();
|
62 | var _ = require('lodash');
|
63 | var path = require('path');
|
64 | var http = require('http');
|
65 | var https = require('https');
|
66 | var fs = require('fs');
|
67 | var init_1 = require("./init");
|
68 | // 端口号启动参数配置
|
69 | var PORT = parseInt(process.argv[2]);
|
70 | if (!PORT) {
|
71 | PORT = 8888;
|
72 | }
|
73 | // 设置静态资源路径(必须要使用绝对路径)
|
74 | app.use(express.static(path.join(init_1.get_root_path_prefix(), './static')));
|
75 | // 引入json解析中间件
|
76 | var bodyParser = require('body-parser');
|
77 | app.use(function (req, res, next) {
|
78 | var rawBody = [];
|
79 | var size = 0;
|
80 | req.on('data', function (data) {
|
81 | rawBody.push(data);
|
82 | size += data.length;
|
83 | });
|
84 | req.on('end', function () {
|
85 | // 获取原始的content-type对应的body内容自行解析处理,解决本地环境和线上环境不兼容问题。
|
86 | req.rawBody = Buffer.concat(rawBody, size).toString();
|
87 | });
|
88 | next();
|
89 | });
|
90 | // 添加json解析
|
91 | app.use(bodyParser.json());
|
92 | app.use(bodyParser.urlencoded({ extended: false }));
|
93 | // 允许所有的请求形式
|
94 | app.use(function (req, res, next) {
|
95 | if (req.headers.origin) {
|
96 | // 获取源站动态允许请求跨域 (FIXME 需要进行安全限制对来源服务器网址合法性进行安全限制,本地开发调试全部放开请求)
|
97 | res.header("Access-Control-Allow-Origin", req.headers.origin);
|
98 | }
|
99 | // res.header("Access-Control-Allow-Origin", "*")
|
100 | res.header("Access-Control-Allow-Credentials", "true");
|
101 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
102 | res.header('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
103 | next();
|
104 | });
|
105 | // 本地调试使用路由重定向映射功能模拟
|
106 | var routes = require(path.join(init_1.get_root_path_prefix(), './app/entries/route.map')).default;
|
107 | var _loop_1 = function (k) {
|
108 | app.all(k, function (req, res) {
|
109 | return __awaiter(this, void 0, void 0, function () {
|
110 | return __generator(this, function (_a) {
|
111 | req.headers.__api__ = routes[k].path;
|
112 | callback(req, res);
|
113 | return [2 /*return*/];
|
114 | });
|
115 | });
|
116 | });
|
117 | };
|
118 | for (var k in routes) {
|
119 | _loop_1(k);
|
120 | }
|
121 | // TODO SLS日志机制云端对接、监控机制的业务对接、性能测试的对接、ACM配置机制的对接、报错机制信息的正确解析处理、
|
122 | // TODO COOKIE解析、BUC登录验证、 会议室信息的JAVA接口联调、梅丽莎多语言对接、ACM的配置发布
|
123 | // TODO 错误页处理、SESSION模拟基于TableStore的实现、REST的实现、更多PAAS中间件根据业务需要对接集成进来、实际业务问题更多的优化扩展。。。
|
124 | // 匹配不包含.的所有路由进行处理,否则表示文件静态资源需要单独处理。(API网关不返回任何静态资源,静态资源需要全部上传到CDN上,API网关只处理一个网站图标请求)
|
125 | app.all(/^((?!\.).)*$/, callback);
|
126 | // TODO 通过请求的域名和入口文件的位置,自动区分:本地、日常、预发、线上,四种环境。(灰度通过线上版本位置自动区分)
|
127 | // 加载配置文件信息。。。比较特殊需要在每次请求的时候单独处理。
|
128 | // 日常与预发在一台机器上需要分别启动两个独立nodejs进程进行处理,确保二者内部运行太壮数据的隔离性。
|
129 | // nodejs服务支持证书配置问题,使用正式的域名证书进行绑定配置,测试的时候本地配置域名链接确保正常。
|
130 | if (process.argv[2] == 'https') {
|
131 | // 预发绑定HTTPS服务(对应AONE的日常机器)
|
132 | var privateKey = fs.readFileSync(path.join(init_1.get_root_path_prefix(), './secrete/private.pem'), 'utf8');
|
133 | var certificate = fs.readFileSync(path.join(init_1.get_root_path_prefix(), './secrete/public.crt'), 'utf8');
|
134 | var credentials = { key: privateKey, cert: certificate };
|
135 | var httpsServer = https.createServer(credentials, app);
|
136 | httpsServer.listen(443, function () {
|
137 | // console.log('fc pre-release-test app listening on port 443!')
|
138 | process.send('fc pre-release-test app listening on port 443!');
|
139 | });
|
140 | }
|
141 | else if (process.argv[2] == 'http') {
|
142 | // 日常绑定HTTP服务(对应AONE的日常机器,一台机器同时配置HTTP日常和HTTPS预发)
|
143 | var httpServer = http.createServer(app);
|
144 | httpServer.listen(80, function () {
|
145 | // console.log('fc test app listening on port 80!')
|
146 | process.send('fc test app listening on port 80!');
|
147 | });
|
148 | }
|
149 | else {
|
150 | // 本地开发环境HTTP协议绑定指定的端口号
|
151 | var httpServer = http.createServer(app);
|
152 | httpServer.listen(PORT, function () {
|
153 | process.send("fc test app listening on port " + PORT + ", visit http://127.0.0.1:" + PORT);
|
154 | // console.log(`fc test app listening on port ${PORT}, visit http://127.0.0.1:${PORT}`)
|
155 | });
|
156 | }
|
157 | function callback(req, res) {
|
158 | return __awaiter(this, void 0, void 0, function () {
|
159 | var err_1;
|
160 | return __generator(this, function (_a) {
|
161 | switch (_a.label) {
|
162 | case 0:
|
163 | _a.trys.push([0, 2, , 3]);
|
164 | return [4 /*yield*/, __callback__(req, res)];
|
165 | case 1:
|
166 | _a.sent();
|
167 | return [3 /*break*/, 3];
|
168 | case 2:
|
169 | err_1 = _a.sent();
|
170 | return [3 /*break*/, 3];
|
171 | case 3:
|
172 | // 按照CGI模式每个子进程处理一个唯一的请求确保全局变量的唯一性简化代码实现避免潜在的并发问题
|
173 | process.exit();
|
174 | return [2 /*return*/];
|
175 | }
|
176 | });
|
177 | });
|
178 | }
|
179 | function __callback__(req, res) {
|
180 | return __awaiter(this, void 0, void 0, function () {
|
181 | var param, __api__, __url__, __header__, out, err_2;
|
182 | return __generator(this, function (_a) {
|
183 | switch (_a.label) {
|
184 | case 0:
|
185 | param = req.query || {};
|
186 | __api__ = req.headers.__api__ ? req.headers.__api__ : req.path;
|
187 | __url__ = req.protocol + '://' + req.get('host') + req.originalUrl;
|
188 | _a.label = 1;
|
189 | case 1:
|
190 | _a.trys.push([1, 3, , 5]);
|
191 | // 请求参数的自动格式化处理兼容get和post协议方便开发调试
|
192 | switch (req.method) {
|
193 | case 'GET':
|
194 | break;
|
195 | case 'POST':
|
196 | param = xassign(param, init_1.parse_post_param(req.headers, req.rawBody));
|
197 | break;
|
198 | case 'OPTIONS':
|
199 | // 仅仅支持本地开发跨域调试,线上需要禁止此方法的调用应该同域请求处理。
|
200 | res.sendStatus(200);
|
201 | return [2 /*return*/];
|
202 | default:
|
203 | // 400 Bad Request 客户端请求的语法错误,服务器无法理解
|
204 | res.sendStatus(400);
|
205 | return [2 /*return*/];
|
206 | }
|
207 | __header__ = req.headers;
|
208 | param = xassign(param, { __api__: __api__, __url__: __url__, __header__: __header__ });
|
209 | // 解析请求中的cookies数据并转换为JSON对象存储到全局变量中便于后续应用xcookie接口使用
|
210 | global['__request_cookies__'] = require('cookie').parse(_.get(req.headers, 'cookie', ''));
|
211 | // 获取use-agent请求头部信息
|
212 | global['__user_agent__'] = req['headers'] ? (req['headers']['user-agent'] ? req['headers']['user-agent'] : '') : '';
|
213 | // 取到客户端的IP地址信息
|
214 | global['__client_ip__'] = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
215 | return [4 /*yield*/, init_1.request_process(__api__, param)
|
216 | // 处理response的cookies设置
|
217 | ];
|
218 | case 2:
|
219 | out = _a.sent();
|
220 | // 处理response的cookies设置
|
221 | if (!_.isEmpty(global['__respond_cookies__'])) {
|
222 | res.setHeader('Set-Cookie', _.values(global['__respond_cookies__']));
|
223 | }
|
224 | if (global['__redirect_url__']) {
|
225 | res.redirect(302, global['__redirect_url__']);
|
226 | global['__redirect_url__'] = undefined;
|
227 | return [2 /*return*/];
|
228 | }
|
229 | res.send(out);
|
230 | return [3 /*break*/, 5];
|
231 | case 3:
|
232 | err_2 = _a.sent();
|
233 | // 通知框架自身实现逻辑的意外报错(框架自身不论何种情况都应该正常工作,一旦出现此问题大多数情况是框架自身问题或者流量引发的运维问题)
|
234 | return [4 /*yield*/, xwarn({
|
235 | __api__: __api__, __url__: __url__, param: param, message: err_2.message, stack: xstack(err_2)
|
236 | })
|
237 | // 500 Internal Server Error 服务器内部错误,无法完成请求
|
238 | ];
|
239 | case 4:
|
240 | // 通知框架自身实现逻辑的意外报错(框架自身不论何种情况都应该正常工作,一旦出现此问题大多数情况是框架自身问题或者流量引发的运维问题)
|
241 | _a.sent();
|
242 | // 500 Internal Server Error 服务器内部错误,无法完成请求
|
243 | res.sendStatus(500);
|
244 | return [3 /*break*/, 5];
|
245 | case 5: return [2 /*return*/];
|
246 | }
|
247 | });
|
248 | });
|
249 | }
|
250 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0ZXN0LWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEscUJBQXFCO0FBQ3JCLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsRUFBRTtJQUN6QixNQUFNLENBQUMsU0FBUyxDQUFDLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQTtDQUM5QztLQUFNO0lBQ0gsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxPQUFPLEVBQUU7UUFDeEQsU0FBUztRQUNULE1BQU0sQ0FBQyxTQUFTLENBQUMsR0FBRyxLQUFLLENBQUE7S0FDNUI7U0FBTSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLE1BQU0sRUFBRTtRQUM5RCxTQUFTO1FBQ1QsTUFBTSxDQUFDLFNBQVMsQ0FBQyxHQUFHLE9BQU8sQ0FBQTtLQUM5QjtTQUNJO1FBQ0QsU0FBUztRQUNULE1BQU0sQ0FBQyxTQUFTLENBQUMsR0FBRyxPQUFPLENBQUE7S0FDOUI7Q0FDSjtBQUNELElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLE9BQU8sRUFBRTtJQUM5QixPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQTtDQUMxQztBQUNELE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFBO0FBQ3BDLElBQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQTtBQUNsQyxJQUFNLEdBQUcsR0FBRyxPQUFPLEVBQUUsQ0FBQTtBQUNyQixJQUFNLENBQUMsR0FBRyxPQUFP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Q0FBQyxhQUFhLENBQUE7b0JBRzlFLHFCQUFNLHNCQUFlLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQzt3QkFFL0MsdUJBQXVCO3NCQUZ3Qjs7b0JBQTNDLEdBQUcsR0FBRyxTQUFxQztvQkFFL0MsdUJBQXVCO29CQUN2QixJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMscUJBQXFCLENBQUMsQ0FBQyxFQUFFO3dCQUMzQyxHQUFHLENBQUMsU0FBUyxDQUFDLFlBQVksRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsQ0FBQTtxQkFDdkU7b0JBRUQsSUFBSSxNQUFNLENBQUMsa0JBQWtCLENBQUMsRUFBRTt3QkFDNUIsR0FBRyxDQUFDLFFBQVEsQ0FBQyxHQUFHLEVBQUUsTUFBTSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQTt3QkFDN0MsTUFBTSxDQUFDLGtCQUFrQixDQUFDLEdBQUcsU0FBUyxDQUFBO3dCQUN0QyxzQkFBTTtxQkFDVDtvQkFDRCxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBOzs7O29CQUViLG9FQUFvRTtvQkFDcEUscUJBQU0sS0FBSyxDQUFDOzRCQUNSLE9BQU8sU0FBQSxFQUFFLE9BQU8sU0FBQSxFQUFFLEtBQUssT0FBQSxFQUFFLE9BQU8sRUFBRSxLQUFHLENBQUMsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsS0FBRyxDQUFDO3lCQUNwRSxDQUFDO3dCQUNGLDJDQUEyQztzQkFEekM7O29CQUhGLG9FQUFvRTtvQkFDcEUsU0FFRSxDQUFBO29CQUNGLDJDQUEyQztvQkFDM0MsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQTs7Ozs7O0NBRTFCIiwic291cmNlc0NvbnRlbnQiOlsiLy8g5qC55o2u5p625p6E6K6+6K6h57qm5a6a6Ieq5Yqo6K+G5Yir5ZCE56eN6L+Q6KGM546v5aKDXG5pZiAocHJvY2Vzcy5lbnZbJ05PREVfRU5WJ10pIHtcbiAgICBnbG9iYWxbJ19fZW52X18nXSA9IHByb2Nlc3MuZW52WydOT0RFX0VOViddXG59IGVsc2Uge1xuICAgIGlmIChwcm9jZXNzLmFyZ3YubGVuZ3RoID49IDMgJiYgcHJvY2Vzcy5hcmd2WzJdID09ICdodHRwcycpIHtcbiAgICAgICAgLy8g6aKE5Y+R546v5aKD6YWN572uXG4gICAgICAgIGdsb2JhbFsnX19lbnZfXyddID0gJ3ByZSdcbiAgICB9IGVsc2UgaWYgKHByb2Nlc3MuYXJndi5sZW5ndGggPj0gMyAmJiBwcm9jZXNzLmFyZ3ZbMl0gPT0gJ2h0dHAnKSB7XG4gICAgICAgIC8vIOaXpeW4uOeOr+Wig+mFjee9rlxuICAgICAgICBnbG9iYWxbJ19fZW52X18nXSA9ICdkYWlseSdcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIC8vIOacrOWcsOeOr+Wig+mFjee9rlxuICAgICAgICBnbG9iYWxbJ19fZW52X18nXSA9ICdsb2NhbCdcbiAgICB9XG59XG5pZiAoZ2xvYmFsWydfX2Vudl9fJ10gIT0gJ2xvY2FsJykge1xuICAgIHJlcXVpcmUoJ3NvdXJjZS1tYXAtc3VwcG9ydCcpLmluc3RhbGwoKVxufVxucmVxdWlyZSgndHNjb25maWctcGF0aHMnKS5yZWdpc3RlcigpXG5jb25zdCBleHByZXNzID0gcmVxdWlyZSgnZXhwcmVzcycpXG5jb25zdCBhcHAgPSBleHByZXNzKClcbmNvbnN0IF8gPSByZXF1aXJlKCdsb2Rhc2gnKVxuY29uc3QgcGF0aCA9IHJlcXVpcmUoJ3BhdGgnKVxuY29uc3QgaHR0cCA9IHJlcXVpcmUoJ2h0dHAnKVxuY29uc3QgaHR0cHMgPSByZXF1aXJlKCdodHRwcycpXG5jb25zdCBmcyA9IHJlcXVpcmUoJ2ZzJylcbmltcG9ydCB7Z2V0X3Jvb3RfcGF0aF9wcmVmaXgsIHJlcXVlc3RfcHJvY2VzcywgcGFyc2VfcG9zdF9wYXJhbX0gZnJvbSAnLi9pbml0J1xuXG4vLyDnq6/lj6Plj7flkK/liqjlj4LmlbDphY3nva5cbmxldCBQT1JUOiBhbnkgPSBwYXJzZUludChwcm9jZXNzLmFyZ3ZbMl0pXG5pZiAoIVBPUlQpIHtcbiAgICBQT1JUID0gODg4OFxufVxuXG4vLyDorr7nva7pnZnmgIHotYTmupDot6/lvoTvvIjlv4XpobvopoHkvb/nlKjnu53lr7not6/lvoTvvIlcbmFwcC51c2UoZXhwcmVzcy5zdGF0aWMocGF0aC5qb2luKGdldF9yb290X3BhdGhfcHJlZml4KCksICcuL3N0YXRpYycpKSlcblxuLy8g5byV5YWlanNvbuino+aekOS4remXtOS7tlxuY29uc3QgYm9keVBhcnNlciA9IHJlcXVpcmUoJ2JvZHktcGFyc2VyJylcbmFwcC51c2UoZnVuY3Rpb24gKHJlcSwgcmVzLCBuZXh0KSB7XG4gICAgdmFyIHJhd0JvZHkgPSBbXVxuICAgIHZhciBzaXplID0gMFxuICAgIHJlcS5vbignZGF0YScsIGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgIHJhd0JvZHkucHVzaChkYXRhKVxuICAgICAgICBzaXplICs9IGRhdGEubGVuZ3RoXG4gICAgfSlcbiAgICByZXEub24oJ2VuZCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8g6I635Y+W5Y6f5aeL55qEY29udGVudC10eXBl5a+55bqU55qEYm9keeWGheWuueiHquihjOino+aekOWkhOeQhu+8jOino+WGs+acrOWcsOeOr+Wig+WSjOe6v+S4iueOr+Wig+S4jeWFvOWuuemXrumimOOAglxuICAgICAgICByZXEucmF3Qm9keSA9IEJ1ZmZlci5jb25jYXQocmF3Qm9keSwgc2l6ZSkudG9TdHJpbmcoKVxuICAgIH0pXG4gICAgbmV4dCgpXG59KVxuLy8g5re75YqganNvbuino+aekFxuYXBwLnVzZShib2R5UGFyc2VyLmpzb24oKSlcbmFwcC51c2UoYm9keVBhcnNlci51cmxlbmNvZGVkKHtleHRlbmRlZDogZmFsc2V9KSlcbi8vIOWFgeiuuOaJgOacieeahOivt+axguW9ouW8j1xuYXBwLnVzZShmdW5jdGlvbiAocmVxLCByZXMsIG5leHQpIHtcbiAgICBpZiAocmVxLmhlYWRlcnMub3JpZ2luKSB7XG4gICAgICAgIC8vIOiOt+WPlua6kOermeWKqOaAgeWFgeiuuOivt+axgui3qOWfnyAoRklYTUUg6ZyA6KaB6L+b6KGM5a6J5YWo6ZmQ5Yi25a+55p2l5rqQ5pyN5Yqh5Zmo572R5Z2A5ZCI5rOV5oCn6L+b6KGM5a6J5YWo6ZmQ5Yi277yM5pys5Zyw5byA5Y+R6LCD6K+V5YWo6YOo5pS+5byA6K+35rGCKVxuICAgICAgICByZXMuaGVhZGVyKFwiQWNjZXNzLUNvbnRyb2wtQWxsb3ctT3JpZ2luXCIsIHJlcS5oZWFkZXJzLm9yaWdpbik7XG4gICAgfVxuICAgIC8vIHJlcy5oZWFkZXIoXCJBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW5cIiwgXCIqXCIpXG4gICAgcmVzLmhlYWRlcihcIkFjY2Vzcy1Db250cm9sLUFsbG93LUNyZWRlbnRpYWxzXCIsIFwidHJ1ZVwiKVxuICAgIHJlcy5oZWFkZXIoXCJBY2Nlc3MtQ29udHJvbC1BbGxvdy1IZWFkZXJzXCIsIFwiT3JpZ2luLCBYLVJlcXVlc3RlZC1XaXRoLCBDb250ZW50LVR5cGUsIEFjY2VwdFwiKVxuICAgIHJlcy5oZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHMnLCAnUE9TVCwgT1BUSU9OUycpXG4gICAgbmV4dCgpXG59KVxuXG4vLyDmnKzlnLDosIPor5Xkvb/nlKjot6/nlLHph43lrprlkJHmmKDlsITlip/og73mqKHmi59cbmNvbnN0IHJvdXRlcyA9IHJlcXVpcmUocGF0aC5qb2luKGdldF9yb290X3BhdGhfcHJlZml4KCksICcuL2FwcC9lbnRyaWVzL3JvdXRlLm1hcCcpKS5kZWZhdWx0XG5mb3IgKGxldCBrIGluIHJvdXRlcykge1xuICAgIGFwcC5hbGwoaywgYXN5bmMgZnVuY3Rpb24gKHJlcSwgcmVzKSB7XG4gICAgICAgIHJlcS5oZWFkZXJzLl9fYXBpX18gPSByb3V0ZXNba10ucGF0aFxuICAgICAgICBjYWxsYmFjayhyZXEsIHJlcylcbiAgICB9KVxufVxuXG4vLyBUT0RPIFNMU+aXpeW/l+acuuWItuS6keerr+WvueaOpeOAgeebkeaOp+acuuWItueahOS4muWKoeWvueaOpeOAgeaAp+iDvea1i+ivleeahOWvueaOpeOAgUFDTemFjee9ruacuuWItueahOWvueaOpeOAgeaKpemUmeacuuWItuS/oeaBr+eahOato+ehruino+aekOWkhOeQhuOAgVxuLy8gVE9ETyBDT09LSUXop6PmnpDjgIFCVUPnmbvlvZXpqozor4HjgIEgICDkvJrorq7lrqTkv6Hmga/nmoRKQVZB5o6l5Y+j6IGU6LCD44CB5qKF5Li96I6O5aSa6K+t6KiA5a+55o6l44CBQUNN55qE6YWN572u5Y+R5biDXG4vLyBUT0RPIOmUmeivr+mhteWkhOeQhuOAgVNFU1NJT07mqKHmi5/ln7rkuo5UYWJsZVN0b3Jl55qE5a6e546w44CBUkVTVOeahOWunueOsOOAgeabtOWkmlBBQVPkuK3pl7Tku7bmoLnmja7kuJrliqHpnIDopoHlr7nmjqXpm4bmiJDov5vmnaXjgIHlrp7pmYXkuJrliqHpl67popjmm7TlpJrnmoTkvJjljJbmianlsZXjgILjgILjgIJcbi8vIOWMuemFjeS4jeWMheWQqy7nmoTmiYDmnInot6/nlLHov5vooYzlpITnkIbvvIzlkKbliJnooajnpLrmlofku7bpnZnmgIHotYTmupDpnIDopoHljZXni6zlpITnkIbjgILvvIhBUEnnvZHlhbPkuI3ov5Tlm57ku7vkvZXpnZnmgIHotYTmupDvvIzpnZnmgIHotYTmupDpnIDopoHlhajpg6jkuIrkvKDliLBDRE7kuIrvvIxBUEnnvZHlhbPlj6rlpITnkIbkuIDkuKrnvZHnq5nlm77moIfor7fmsYLvvIlcbmFwcC5hbGwoL14oKD8hXFwuKS4pKiQvLCBjYWxsYmFjaylcblxuLy8gVE9ETyDpgJrov4for7fmsYLnmoTln5/lkI3lkozlhaXlj6Pmlofku7bnmoTkvY3nva7vvIzoh6rliqjljLrliIbvvJrmnKzlnLDjgIHml6XluLjjgIHpooTlj5HjgIHnur/kuIrvvIzlm5vnp43njq/looPjgILvvIjngbDluqbpgJrov4fnur/kuIrniYjmnKzkvY3nva7oh6rliqjljLrliIbvvIlcbi8vIOWKoOi9vemFjee9ruaWh+S7tuS/oeaBr+OAguOAguOAguavlOi+g+eJueauiumcgOimgeWcqOavj+asoeivt+axgueahOaXtuWAmeWNleeLrOWkhOeQhuOAglxuLy8g5pel5bi45LiO6aKE5Y+R5Zyo5LiA5Y+w5py65Zmo5LiK6ZyA6KaB5YiG5Yir5ZCv5Yqo5Lik5Liq54us56uLbm9kZWpz6L+b56iL6L+b6KGM5aSE55CG77yM56Gu5L+d5LqM6ICF5YaF6YOo6L+Q6KGM5aSq5aOu5pWw5o2u55qE6ZqU56a75oCn44CCXG5cbi8vIG5vZGVqc+acjeWKoeaUr+aMgeivgeS5pumFjee9rumXrumimO+8jOS9v+eUqOato+W8j+eahOWfn+WQjeivgeS5pui/m+ihjOe7keWumumFjee9ru+8jOa1i+ivleeahOaXtuWAmeacrOWcsOmFjee9ruWfn+WQjemTvuaOpeehruS/neato+W4uOOAglxuaWYgKHByb2Nlc3MuYXJndlsyXSA9PSAnaHR0cHMnKSB7XG4gICAgLy8g6aKE5Y+R57uR5a6aSFRUUFPmnI3liqHvvIjlr7nlupRBT05F55qE5pel5bi45py65Zmo77yJXG4gICAgY29uc3QgcHJpdmF0ZUtleSA9IGZzLnJlYWRGaWxlU3luYyhcbiAgICAgICAgcGF0aC5qb2luKGdldF9yb290X3BhdGhfcHJlZml4KCksICcuL3NlY3JldGUvcHJpdmF0ZS5wZW0nKSwgJ3V0ZjgnKVxuICAgIGNvbnN0IGNlcnRpZmljYXRlID0gZnMucmVhZEZpbGVTeW5jKFxuICAgICAgICBwYXRoLmpvaW4oZ2V0X3Jvb3RfcGF0aF9wcmVmaXgoKSwgJy4vc2VjcmV0ZS9wdWJsaWMuY3J0JyksICd1dGY4JylcbiAgICBjb25zdCBjcmVkZW50aWFscyA9IHtrZXk6IHByaXZhdGVLZXksIGNlcnQ6IGNlcnRpZmljYXRlfVxuICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKGNyZWRlbnRpYWxzLCBhcHApXG4gICAgaHR0cHNTZXJ2ZXIubGlzdGVuKDQ0MywgZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyBjb25zb2xlLmxvZygnZmMgcHJlLXJlbGVhc2UtdGVzdCBhcHAgbGlzdGVuaW5nIG9uIHBvcnQgNDQzIScpXG4gICAgICAgIHByb2Nlc3Muc2VuZCgnZmMgcHJlLXJlbGVhc2UtdGVzdCBhcHAgbGlzdGVuaW5nIG9uIHBvcnQgNDQzIScpXG4gICAgfSlcbn0gZWxzZSBpZiAocHJvY2Vzcy5hcmd2WzJdID09ICdodHRwJykge1xuICAgIC8vIOaXpeW4uOe7keWumkhUVFDmnI3liqHvvIjlr7nlupRBT05F55qE5pel5bi45py65Zmo77yM5LiA5Y+w5py65Zmo5ZCM5pe26YWN572uSFRUUOaXpeW4uOWSjEhUVFBT6aKE5Y+R77yJXG4gICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcClcbiAgICBodHRwU2VydmVyLmxpc3Rlbig4MCwgZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyBjb25zb2xlLmxvZygnZmMgdGVzdCBhcHAgbGlzdGVuaW5nIG9uIHBvcnQgODAhJylcbiAgICAgICAgcHJvY2Vzcy5zZW5kKCdmYyB0ZXN0IGFwcCBsaXN0ZW5pbmcgb24gcG9ydCA4MCEnKVxuICAgIH0pXG59IGVsc2Uge1xuICAgIC8vIOacrOWcsOW8gOWPkeeOr+Wig0hUVFDljY/orq7nu5HlrprmjIflrprnmoTnq6/lj6Plj7dcbiAgICBjb25zdCBodHRwU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoYXBwKVxuICAgIGh0dHBTZXJ2ZXIubGlzdGVuKFBPUlQsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcHJvY2Vzcy5zZW5kKGBmYyB0ZXN0IGFwcCBsaXN0ZW5pbmcgb24gcG9ydCAke1BPUlR9LCB2aXNpdCBodHRwOi8vMTI3LjAuMC4xOiR7UE9SVH1gKVxuICAgICAgICAvLyBjb25zb2xlLmxvZyhgZmMgdGVzdCBhcHAgbGlzdGVuaW5nIG9uIHBvcnQgJHtQT1JUfSwgdmlzaXQgaHR0cDovLzEyNy4wLjAuMToke1BPUlR9YClcbiAgICB9KVxufVxuXG5hc3luYyBmdW5jdGlvbiBjYWxsYmFjayhyZXEsIHJlcykge1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IF9fY2FsbGJhY2tfXyhyZXEsIHJlcylcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgLy8gaWdub3JlIGVycm9yXG4gICAgfVxuICAgIC8vIOaMieeFp0NHSeaooeW8j+avj+S4quWtkOi/m+eoi+WkhOeQhuS4gOS4quWUr+S4gOeahOivt+axguehruS/neWFqOWxgOWPmOmHj+eahOWUr+S4gOaAp+eugOWMluS7o+eggeWunueOsOmBv+WFjea9nOWcqOeahOW5tuWPkemXrumimFxuICAgIHByb2Nlc3MuZXhpdCgpXG59XG5cbmFzeW5jIGZ1bmN0aW9uIF9fY2FsbGJhY2tfXyhyZXEsIHJlcykge1xuICAgIGxldCBwYXJhbSA9IHJlcS5xdWVyeSB8fCB7fVxuICAgIC8vIOihpeS4iuahhuaetumihOWumuS5ieeahOe8uuecgeWPguaVsF9fdXJsX18o5YW25LuW5pu05aSa5qGG5p625Y+C5pWwX19hcGlfXyBfX2RlYnVnX18gX19tb2NrX18gVE9ETyBfX3BhcmFtX18g5ZyoZ2V05LitanNvbuWtl+espuS4sueahGJhc2U2NOagvOW8jylcbiAgICBsZXQgX19hcGlfXyA9IHJlcS5oZWFkZXJzLl9fYXBpX18gPyByZXEuaGVhZGVycy5fX2FwaV9fIDogcmVxLnBhdGhcbiAgICBsZXQgX191cmxfXyA9IHJlcS5wcm90b2NvbCArICc6Ly8nICsgcmVxLmdldCgnaG9zdCcpICsgcmVxLm9yaWdpbmFsVXJsXG5cbiAgICB0cnkge1xuICAgICAgICAvLyDor7fmsYLlj4LmlbDnmoToh6rliqjmoLzlvI/ljJblpITnkIblhbzlrrlnZXTlkoxwb3N05Y2P6K6u5pa55L6/5byA5Y+R6LCD6K+VXG4gICAgICAgIHN3aXRjaCAocmVxLm1ldGhvZCkge1xuICAgICAgICAgICAgY2FzZSAnR0VUJzpcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgY2FzZSAnUE9TVCc6XG4gICAgICAgICAgICAgICAgcGFyYW0gPSB4YXNzaWduKHBhcmFtLCBwYXJzZV9wb3N0X3BhcmFtKHJlcS5oZWFkZXJzLCByZXEucmF3Qm9keSkpXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgJ09QVElPTlMnOlxuICAgICAgICAgICAgICAgIC8vIOS7heS7heaUr+aMgeacrOWcsOW8gOWPkei3qOWfn+iwg+ivle+8jOe6v+S4iumcgOimgeemgeatouatpOaWueazleeahOiwg+eUqOW6lOivpeWQjOWfn+ivt+axguWkhOeQhuOAglxuICAgICAgICAgICAgICAgIHJlcy5zZW5kU3RhdHVzKDIwMClcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgLy8gNDAwICBCYWQgUmVxdWVzdFx05a6i5oi356uv6K+35rGC55qE6K+t5rOV6ZSZ6K+v77yM5pyN5Yqh5Zmo5peg5rOV55CG6KejXG4gICAgICAgICAgICAgICAgcmVzLnNlbmRTdGF0dXMoNDAwKVxuICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIOihpeS4iuivt+axgueahEhFQURFUuWPguaVsOWIsOW6lOeUqOS4rei/m+ihjOiHquihjOWkhOeQhuS4muWKoemAu+i+keWumuWItueahOivt+axguWktOmDqOS/oeaBr1xuICAgICAgICBsZXQgX19oZWFkZXJfXyA9IHJlcS5oZWFkZXJzXG4gICAgICAgIHBhcmFtID0geGFzc2lnbihwYXJhbSwge19fYXBpX18sIF9fdXJsX18sIF9faGVhZGVyX199KVxuXG4gICAgICAgIC8vIOino+aekOivt+axguS4reeahGNvb2tpZXPmlbDmja7lubbovazmjaLkuLpKU09O5a+56LGh5a2Y5YKo5Yiw5YWo5bGA5Y+Y6YeP5Lit5L6/5LqO5ZCO57ut5bqU55SoeGNvb2tpZeaOpeWPo+S9v+eUqFxuICAgICAgICBnbG9iYWxbJ19fcmVxdWVzdF9jb29raWVzX18nXSA9IHJlcXVpcmUoJ2Nvb2tpZScpLnBhcnNlKF8uZ2V0KHJlcS5oZWFkZXJzLCAnY29va2llJywgJycpKVxuXG4gICAgICAgIC8vIOiOt+WPlnVzZS1hZ2VudOivt+axguWktOmDqOS/oeaBr1xuICAgICAgICBnbG9iYWxbJ19fdXNlcl9hZ2VudF9fJ10gPSByZXFbJ2hlYWRlcnMnXSA/IChyZXFbJ2hlYWRlcnMnXVsndXNlci1hZ2VudCddID8gcmVxWydoZWFkZXJzJ11bJ3VzZXItYWdlbnQnXSA6ICcnKSA6ICcnXG5cbiAgICAgICAgLy8g5Y+W5Yiw5a6i5oi356uv55qESVDlnLDlnYDkv6Hmga9cbiAgICAgICAgZ2xvYmFsWydfX2NsaWVudF9pcF9fJ10gPSByZXEuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxLmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xuXG4gICAgICAgIC8vIOivt+axguWkhOeQhlxuICAgICAgICBsZXQgb3V0ID0gYXdhaXQgcmVxdWVzdF9wcm9jZXNzKF9fYXBpX18sIHBhcmFtKVxuXG4gICAgICAgIC8vIOWkhOeQhnJlc3BvbnNl55qEY29va2llc+iuvue9rlxuICAgICAgICBpZiAoIV8uaXNFbXB0eShnbG9iYWxbJ19fcmVzcG9uZF9jb29raWVzX18nXSkpIHtcbiAgICAgICAgICAgIHJlcy5zZXRIZWFkZXIoJ1NldC1Db29raWUnLCBfLnZhbHVlcyhnbG9iYWxbJ19fcmVzcG9uZF9jb29raWVzX18nXSkpXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZ2xvYmFsWydfX3JlZGlyZWN0X3VybF9fJ10pIHtcbiAgICAgICAgICAgIHJlcy5yZWRpcmVjdCgzMDIsIGdsb2JhbFsnX19yZWRpcmVjdF91cmxfXyddKVxuICAgICAgICAgICAgZ2xvYmFsWydfX3JlZGlyZWN0X3VybF9fJ10gPSB1bmRlZmluZWRcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIHJlcy5zZW5kKG91dClcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgLy8g6YCa55+l5qGG5p626Ieq6Lqr5a6e546w6YC76L6R55qE5oSP5aSW5oql6ZSZ77yI5qGG5p626Ieq6Lqr5LiN6K665L2V56eN5oOF5Ya16YO95bqU6K+l5q2j5bi45bel5L2c77yM5LiA5pem5Ye6546w5q2k6Zeu6aKY5aSn5aSa5pWw5oOF5Ya15piv5qGG5p626Ieq6Lqr6Zeu6aKY5oiW6ICF5rWB6YeP5byV5Y+R55qE6L+Q57u06Zeu6aKY77yJXG4gICAgICAgIGF3YWl0IHh3YXJuKHtcbiAgICAgICAgICAgIF9fYXBpX18sIF9fdXJsX18sIHBhcmFtLCBtZXNzYWdlOiBlcnIubWVzc2FnZSwgc3RhY2s6IHhzdGFjayhlcnIpXG4gICAgICAgIH0pXG4gICAgICAgIC8vIDUwMFx0SW50ZXJuYWwgU2VydmVyIEVycm9yXHTmnI3liqHlmajlhoXpg6jplJnor6/vvIzml6Dms5XlrozmiJDor7fmsYJcbiAgICAgICAgcmVzLnNlbmRTdGF0dXMoNTAwKVxuICAgIH1cbn0iXX0= |
\ | No newline at end of file |