UNPKG

32.8 kBJavaScriptView Raw
1"use strict";
2var __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};
10var __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};
37Object.defineProperty(exports, "__esModule", { value: true });
38// 根据架构设计约定自动识别各种运行环境
39if (process.env['NODE_ENV']) {
40 global['__env__'] = process.env['NODE_ENV'];
41}
42else {
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}
56if (global['__env__'] != 'local') {
57 require('source-map-support').install();
58}
59require('tsconfig-paths').register();
60var express = require('express');
61var app = express();
62var _ = require('lodash');
63var path = require('path');
64var http = require('http');
65var https = require('https');
66var fs = require('fs');
67var init_1 = require("./init");
68// 端口号启动参数配置
69var PORT = parseInt(process.argv[2]);
70if (!PORT) {
71 PORT = 8888;
72}
73// 设置静态资源路径(必须要使用绝对路径)
74app.use(express.static(path.join(init_1.get_root_path_prefix(), './static')));
75// 引入json解析中间件
76var bodyParser = require('body-parser');
77app.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解析
91app.use(bodyParser.json());
92app.use(bodyParser.urlencoded({ extended: false }));
93// 允许所有的请求形式
94app.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// 本地调试使用路由重定向映射功能模拟
106var routes = require(path.join(init_1.get_root_path_prefix(), './app/entries/route.map')).default;
107var _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};
118for (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网关只处理一个网站图标请求)
125app.all(/^((?!\.).)*$/, callback);
126// TODO 通过请求的域名和入口文件的位置,自动区分:本地、日常、预发、线上,四种环境。(灰度通过线上版本位置自动区分)
127// 加载配置文件信息。。。比较特殊需要在每次请求的时候单独处理。
128// 日常与预发在一台机器上需要分别启动两个独立nodejs进程进行处理,确保二者内部运行太壮数据的隔离性。
129// nodejs服务支持证书配置问题,使用正式的域名证书进行绑定配置,测试的时候本地配置域名链接确保正常。
130if (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}
141else 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}
149else {
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}
157function 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}
179function __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