MagicJS3.js 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678
  1. function MagicJS(scriptName = "MagicJS", logLevel = "INFO") {
  2. const MagicEnvironment = () => {
  3. const isLoon = typeof $loon !== "undefined";
  4. const isQuanX = typeof $task !== "undefined";
  5. const isNode = typeof module !== "undefined";
  6. const isSurge = typeof $httpClient !== "undefined" && !isLoon;
  7. const isStorm = typeof $storm !== "undefined";
  8. const isStash = typeof $environment !== "undefined" && typeof $environment["stash-build"] !== "undefined";
  9. const isSurgeLike = isSurge || isLoon || isStorm || isStash;
  10. const isScriptable = typeof importModule !== "undefined";
  11. return {
  12. isLoon: isLoon,
  13. isQuanX: isQuanX,
  14. isNode: isNode,
  15. isSurge: isSurge,
  16. isStorm: isStorm,
  17. isStash: isStash,
  18. isSurgeLike: isSurgeLike,
  19. isScriptable: isScriptable,
  20. get name() {
  21. if (isLoon) {
  22. return "Loon";
  23. } else if (isQuanX) {
  24. return "QuantumultX";
  25. } else if (isNode) {
  26. return "NodeJS";
  27. } else if (isSurge) {
  28. return "Surge";
  29. } else if (isScriptable) {
  30. return "Scriptable";
  31. } else {
  32. return "unknown";
  33. }
  34. },
  35. get build() {
  36. if (isSurge) {
  37. return $environment["surge-build"];
  38. } else if (isStash) {
  39. return $environment["stash-build"];
  40. } else if (isStorm) {
  41. return $storm.buildVersion;
  42. }
  43. },
  44. get language() {
  45. if (isSurge || isStash) {
  46. return $environment["language"];
  47. }
  48. },
  49. get version() {
  50. if (isSurge) {
  51. return $environment["surge-version"];
  52. } else if (isStash) {
  53. return $environment["stash-version"];
  54. } else if (isStorm) {
  55. return $storm.appVersion;
  56. } else if (isNode) {
  57. return process.version;
  58. }
  59. },
  60. get system() {
  61. if (isSurge) {
  62. return $environment["system"];
  63. } else if (isNode) {
  64. return process.platform;
  65. }
  66. },
  67. get systemVersion() {
  68. if (isStorm) {
  69. return $storm.systemVersion;
  70. }
  71. },
  72. get deviceName() {
  73. if (isStorm) {
  74. return $storm.deviceName;
  75. }
  76. }
  77. };
  78. };
  79. const MagicLogger = (scriptName, logLevel = "INFO") => {
  80. let _level = logLevel;
  81. const logLevels = {
  82. SNIFFER: 6,
  83. DEBUG: 5,
  84. INFO: 4,
  85. NOTIFY: 3,
  86. WARNING: 2,
  87. ERROR: 1,
  88. CRITICAL: 0,
  89. NONE: -1
  90. };
  91. const logEmoji = {
  92. SNIFFER: "",
  93. DEBUG: "",
  94. INFO: "",
  95. NOTIFY: "",
  96. WARNING: "❗ ",
  97. ERROR: "❌ ",
  98. CRITICAL: "❌ ",
  99. NONE: ""
  100. };
  101. const _log = (msg, level = "INFO") => {
  102. if (!(logLevels[_level] < logLevels[level.toUpperCase()])) console.log(`██[${scriptName}][${level}]`+''+`${logEmoji[level.toUpperCase()]}${msg}`+'\n'+``);
  103. };
  104. const setLevel = logLevel => {
  105. _level = logLevel;
  106. };
  107. return {
  108. getLevel: () => {
  109. return _level;
  110. },
  111. setLevel: setLevel,
  112. sniffer: msg => {
  113. _log(msg, "SNIFFER");
  114. },
  115. log: msg => {
  116. console.log(`██[${scriptName}]`+''+`${msg}`+'\n'+``);
  117. },
  118. debug: msg => {
  119. _log(msg, "DEBUG");
  120. },
  121. info: msg => {
  122. _log(msg, "INFO");
  123. },
  124. notify: msg => {
  125. _log(msg, "NOTIFY");
  126. },
  127. warning: msg => {
  128. _log(msg, "WARNING");
  129. },
  130. error: msg => {
  131. _log(msg, "ERROR");
  132. },
  133. retry: msg => {
  134. _log(msg, "RETRY");
  135. }
  136. };
  137. };
  138. return new class {
  139. constructor(scriptName, logLevel) {
  140. this._startTime = Date.now();
  141. this.version = "3.0.0";
  142. this.scriptName = scriptName;
  143. this.env = MagicEnvironment();
  144. this.logger = MagicLogger(scriptName, logLevel);
  145. this.http = typeof MagicHttp === "function" ? MagicHttp(this.env, this.logger) : undefined;
  146. this.data = typeof MagicData === "function" ? MagicData(this.env, this.logger) : undefined;
  147. this.notification = typeof MagicNotification === "function" ? MagicNotification(this.scriptName, this.env, this.logger, this.http) : undefined;
  148. this.utils = typeof MagicUtils === "function" ? MagicUtils(this.env, this.logger) : undefined;
  149. this.qinglong = typeof MagicQingLong === "function" ? MagicQingLong(this.env, this.data, this.logger) : undefined;
  150. if (typeof this.data !== "undefined") {
  151. let magicLoglevel = this.data.read("magic_loglevel");
  152. const barkUrl = this.data.read("magic_bark_url");
  153. if (magicLoglevel) {
  154. this.logger.setLevel(magicLoglevel.toUpperCase());
  155. }
  156. if (barkUrl) {
  157. this.notification.setBark(barkUrl);
  158. }
  159. }
  160. this.logger.info(`${scriptName}, 开始执行!`);
  161. // this.checkRecordRequestBody();
  162. }
  163. get isRequest() {
  164. return typeof $request !== "undefined";
  165. }
  166. get isStrictRequest() {
  167. return typeof $request !== "undefined" && typeof $response === "undefined";
  168. }
  169. get isResponse() {
  170. return typeof $response !== "undefined";
  171. }
  172. get isDebug() {
  173. return this.logger.level === "DEBUG";
  174. }
  175. get request() {
  176. return typeof $request !== "undefined" ? $request : undefined;
  177. }
  178. get response() {
  179. if (typeof $response !== "undefined") {
  180. if ($response.hasOwnProperty("status")) $response["statusCode"] = $response["status"];
  181. if ($response.hasOwnProperty("statusCode")) $response["status"] = $response["statusCode"];
  182. return $response;
  183. } else {
  184. return undefined;
  185. }
  186. }
  187. checkRecordRequestBody() {
  188. if (!this.isRequest) {
  189. return;
  190. }
  191. const reqBody = $request.body;
  192. if (!reqBody) {
  193. return;
  194. }
  195. const env = this.env;
  196. const path = $request.path;
  197. let cacheKey = this.scriptName + "#" + path.replace("/", "_");
  198. cacheKey = cacheKey.replace("?", "#");
  199. if (env.isQuanX) $prefs.setValueForKey(reqBody, cacheKey);
  200. if (env.isLoon || env.isSurge) $persistentStore.write(reqBody, cacheKey);
  201. if (env.isNode) {
  202. const node_fs = require("fs");
  203. node_fs.writeFileSync(
  204. `${cacheKey}.json`,
  205. reqBody,
  206. {
  207. flag: "w"
  208. },
  209. (err) => console.log(err)
  210. );
  211. }
  212. }
  213. getRequestBody() {
  214. const env = this.env;
  215. const path = $request.path;
  216. let cacheKey = this.scriptName + "#" + path.replace("/", "_");
  217. cacheKey = cacheKey.replace("?", "#");
  218. if (env.isSurge || env.isLoon) {
  219. return $persistentStore.read(cacheKey);
  220. }
  221. if (env.isQuanX) {
  222. return $prefs.valueForKey(cacheKey);
  223. }
  224. if (env.isNode) {
  225. const fpath = `${cacheKey}.json`;
  226. const node_fs = require("fs");
  227. if (!node_fs.existsSync(fpath)) {
  228. return JSON.parse(
  229. node_fs.readFileSync(fpath)
  230. );
  231. }
  232. }
  233. }
  234. getResponseBody() {
  235. if ($response) {
  236. return $response.body;
  237. }
  238. }
  239. objToQueryStr(obj, encode) {
  240. let str = '';
  241. for (const key in obj) {
  242. let value = obj[key];
  243. if (value != null && value !== '') {
  244. if (typeof value === 'object') {
  245. value = JSON.stringify(value);
  246. } else if (encode) {
  247. value = encodeURIComponent(value);
  248. }
  249. str += `${key}=${value}&`;
  250. }
  251. }
  252. str = str.substring(0, str.length - 1);
  253. return str;
  254. }
  255. parseQueryStr(str) {
  256. let obj = {};
  257. if (str.indexOf("?") > -1) {
  258. str = str.split("?")[1];
  259. }
  260. let arr = str.split("&");
  261. for (let i = 0; i < arr.length; i++) {
  262. let kv = arr[i].split("=");
  263. obj[kv[0]] = kv[1];
  264. }
  265. return obj;
  266. }
  267. deepClone(obj, newObj) {
  268. newObj = newObj || {};
  269. for (let key in obj) {
  270. if (typeof obj[key] == 'object') {
  271. newObj[key] = (obj[key].constructor === Array) ? [] : {};
  272. this.deepClone(obj[key], newObj[key]);
  273. } else {
  274. newObj[key] = obj[key];
  275. }
  276. }
  277. return newObj;
  278. }
  279. /**
  280. * formatDate y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒
  281. */
  282. formatDate(date, format) {
  283. let o = {
  284. 'M+': date.getMonth() + 1,
  285. 'd+': date.getDate(),
  286. 'H+': date.getHours(),
  287. 'm+': date.getMinutes(),
  288. 's+': date.getSeconds(),
  289. 'q+': Math.floor((date.getMonth() + 3) / 3),
  290. 'S': date.getMilliseconds()
  291. }
  292. if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
  293. for (let k in o)
  294. if (new RegExp('(' + k + ')').test(format))
  295. format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
  296. return format
  297. }
  298. /**
  299. * parseDate 字符串格式,默认'yyyy-MM-dd',支持如下:y、M、d、H、m、s、S,不支持w和q
  300. */
  301. parseDate(str, format) {
  302. format = format || 'yyyy-MM-dd';
  303. let obj = { y: 0, M: 1, d: 0, H: 0, h: 0, m: 0, s: 0, S: 0 };
  304. format.replace(/([^yMdHmsS]*?)(([yMdHmsS])\3*)([^yMdHmsS]*?)/g, function (m, $1, $2, $3, $4, idx, old) {
  305. str = str.replace(new RegExp($1 + '(\\d{' + $2.length + '})' + $4), function (_m, _$1) {
  306. obj[$3] = parseInt(_$1);
  307. return '';
  308. });
  309. return '';
  310. });
  311. obj.M--; // 月份是从0开始的,所以要减去1
  312. let date = new Date(obj.y, obj.M, obj.d, obj.H, obj.m, obj.s);
  313. if (obj.S !== 0) date.setMilliseconds(obj.S); // 如果设置了毫秒
  314. return date;
  315. }
  316. costTime() {
  317. let info = `${this.scriptName}执行完毕!`
  318. // if (this.isNode && this.isExecComm) {
  319. // info = `指令【${this.comm[1]}】执行完毕!`
  320. // }
  321. this._endTime = new Date().getTime();
  322. const ms = this._endTime - this._startTime;
  323. const costTime = ms / 1000;
  324. this.logger.info(`${info}耗时【${costTime}】秒`);
  325. }
  326. done = (value = {}) => {
  327. // this._endTime = Date.now();
  328. // let span = (this._endTime - this._startTime) / 1e3;
  329. // this.logger.info(`SCRIPT COMPLETED: ${span} S.`);
  330. this.costTime();
  331. if (typeof $done !== "undefined") {
  332. $done(value);
  333. }
  334. };
  335. }(scriptName, logLevel);
  336. }
  337. function MagicHttp(env, logger) {
  338. const phoneUA = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1";
  339. const computerUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 Edg/84.0.522.59";
  340. let axiosInstance;
  341. if (env.isNode) {
  342. const axios = require("axios");
  343. axiosInstance = axios.create();
  344. }
  345. class InterceptorManager {
  346. constructor(isRequest = true) {
  347. this.handlers = [];
  348. this.isRequest = isRequest;
  349. }
  350. use(fulfilled, rejected, options) {
  351. if (typeof fulfilled === "function") {
  352. logger.debug(`Register fulfilled ${fulfilled.name}`);
  353. }
  354. if (typeof rejected === "function") {
  355. logger.debug(`Register rejected ${rejected.name}`);
  356. }
  357. this.handlers.push({
  358. fulfilled: fulfilled,
  359. rejected: rejected,
  360. synchronous: options && typeof options.synchronous === "boolean" ? options.synchronous : false,
  361. runWhen: options ? options.runWhen : null
  362. });
  363. return this.handlers.length - 1;
  364. }
  365. eject(id) {
  366. if (this.handlers[id]) {
  367. this.handlers[id] = null;
  368. }
  369. }
  370. forEach(fn) {
  371. this.handlers.forEach(element => {
  372. if (element !== null) {
  373. fn(element);
  374. }
  375. });
  376. }
  377. }
  378. function paramsToQueryString(config) {
  379. let _config = {
  380. ...config
  381. };
  382. if (!!_config.params) {
  383. if (!env.isNode) {
  384. let qs = Object.keys(_config.params).map(key => {
  385. const encodeKey = encodeURIComponent(key);
  386. _config.url = _config.url.replace(new RegExp(`${key}=[^&]*`, "ig"), "");
  387. _config.url = _config.url.replace(new RegExp(`${encodeKey}=[^&]*`, "ig"), "");
  388. return `${encodeKey}=${encodeURIComponent(_config.params[key])}`;
  389. }).join("&");
  390. if (_config.url.indexOf("?") < 0) _config.url += "?";
  391. if (!/(&|\?)$/g.test(_config.url)) {
  392. _config.url += "&";
  393. }
  394. _config.url += qs;
  395. delete _config.params;
  396. logger.debug(`Params to QueryString: ${_config.url}`);
  397. }
  398. }
  399. return _config;
  400. }
  401. const mergeConfig = (method, configOrUrl) => {
  402. let config = typeof configOrUrl === "object" ? {
  403. headers: {},
  404. ...configOrUrl
  405. } : {
  406. url: configOrUrl,
  407. headers: {}
  408. };
  409. if (!config.method) {
  410. config["method"] = method;
  411. }
  412. config = paramsToQueryString(config);
  413. if (config["rewrite"] === true) {
  414. if (env.isSurge) {
  415. config.headers["X-Surge-Skip-Scripting"] = false;
  416. delete config["rewrite"];
  417. } else if (env.isQuanX) {
  418. config["hints"] = false;
  419. delete config["rewrite"];
  420. }
  421. }
  422. if (env.isSurgeLike) {
  423. const contentType = config.headers["content-type"] || config.headers["Content-Type"];
  424. if (config["method"] !== "GET" && contentType && contentType.indexOf("application/json") >= 0 && config.body instanceof Array) {
  425. config.body = JSON.stringify(config.body);
  426. logger.debug(`Convert Array object to String: ${config.body}`);
  427. }
  428. } else if (env.isQuanX) {
  429. if (config.hasOwnProperty("body") && typeof config["body"] !== "string") config["body"] = JSON.stringify(config["body"]);
  430. config["method"] = method;
  431. } else if (env.isNode) {
  432. if (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE") {
  433. config.data = config.data || config.body;
  434. } else if (method === "GET") {
  435. config.params = config.params || config.body;
  436. }
  437. delete config.body;
  438. }
  439. return config;
  440. };
  441. const modifyResponse = (resp, config = null) => {
  442. if (resp) {
  443. let _resp = {
  444. ...resp,
  445. config: resp.config || config,
  446. status: resp.statusCode || resp.status,
  447. body: resp.body || resp.data,
  448. headers: resp.headers || resp.header
  449. };
  450. if (typeof _resp.body === "string") {
  451. try {
  452. _resp.body = JSON.parse(_resp.body);
  453. } catch {}
  454. }
  455. delete _resp.data;
  456. return _resp;
  457. } else {
  458. return resp;
  459. }
  460. };
  461. const convertHeadersToLowerCase = headers => {
  462. return Object.keys(headers).reduce((acc, key) => {
  463. acc[key.toLowerCase()] = headers[key];
  464. return acc;
  465. }, {});
  466. };
  467. const convertHeadersToCamelCase = headers => {
  468. return Object.keys(headers).reduce((acc, key) => {
  469. const newKey = key.split("-").map(word => word[0].toUpperCase() + word.slice(1)).join("-");
  470. acc[newKey] = headers[key];
  471. return acc;
  472. }, {});
  473. };
  474. const raiseExceptionByStatusCode = (resp, config = null) => {
  475. if (!!resp && resp.status >= 400) {
  476. logger.debug(`Raise exception when status code is ${resp.status}`);
  477. return {
  478. name: "RequestException",
  479. message: `Request failed with status code ${resp.status}`,
  480. config: config || resp.config,
  481. response: resp
  482. };
  483. }
  484. };
  485. const interceptors = {
  486. request: new InterceptorManager(),
  487. response: new InterceptorManager(false)
  488. };
  489. let requestInterceptorChain = [];
  490. let responseInterceptorChain = [];
  491. let synchronousRequestInterceptors = true;
  492. function interceptConfig(config) {
  493. config = paramsToQueryString(config);
  494. logger.debug(`HTTP ${config["method"].toUpperCase()}:`+'\n'+`${JSON.stringify(config)}`);
  495. return config;
  496. }
  497. function interceptResponse(resp) {
  498. try {
  499. resp = !!resp ? modifyResponse(resp) : resp;
  500. logger.sniffer(`HTTP ${resp.config["method"].toUpperCase()}:`+'\n'+`${JSON.stringify(resp.config)}`+'\n'+`STATUS CODE:`+'\n'+`${resp.status}`+'\n'+`RESPONSE:`+'\n'+`${typeof resp.body === "object" ? JSON.stringify(resp.body) : resp.body}`);
  501. const err = raiseExceptionByStatusCode(resp);
  502. if (!!err) {
  503. return Promise.reject(err);
  504. }
  505. return resp;
  506. } catch (err) {
  507. logger.error(err);
  508. return resp;
  509. }
  510. }
  511. const registerInterceptors = config => {
  512. try {
  513. requestInterceptorChain = [];
  514. responseInterceptorChain = [];
  515. interceptors.request.forEach(interceptor => {
  516. if (typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false) {
  517. return;
  518. }
  519. synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
  520. requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  521. });
  522. interceptors.response.forEach(interceptor => {
  523. responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  524. });
  525. } catch (err) {
  526. logger.error(`Failed to register interceptors: ${err}.`);
  527. }
  528. };
  529. const request = (method, config) => {
  530. let dispatchRequest;
  531. const _method = method.toUpperCase();
  532. config = mergeConfig(_method, config);
  533. if (env.isNode) {
  534. dispatchRequest = axiosInstance;
  535. } else {
  536. if (env.isSurgeLike) {
  537. dispatchRequest = config => {
  538. return new Promise((resolve, reject) => {
  539. $httpClient[method.toLowerCase()](config, (err, resp, body) => {
  540. if (err) {
  541. let newErr = {
  542. name: err.name || err,
  543. message: err.message || err,
  544. stack: err.stack || err,
  545. config: config,
  546. response: modifyResponse(resp)
  547. };
  548. reject(newErr);
  549. } else {
  550. resp.config = config;
  551. resp.body = body;
  552. resolve(resp);
  553. }
  554. });
  555. });
  556. };
  557. } else {
  558. dispatchRequest = config => {
  559. return new Promise((resolve, reject) => {
  560. $task.fetch(config).then(resp => {
  561. resp = modifyResponse(resp, config);
  562. const err = raiseExceptionByStatusCode(resp, config);
  563. if (err) {
  564. return Promise.reject(err);
  565. }
  566. resolve(resp);
  567. }).catch(err => {
  568. let newErr = {
  569. name: err.message || err.error,
  570. message: err.message || err.error,
  571. stack: err.error,
  572. config: config,
  573. response: !!err.response ? modifyResponse(err.response) : null
  574. };
  575. reject(newErr);
  576. });
  577. });
  578. };
  579. }
  580. }
  581. let promise;
  582. registerInterceptors(config);
  583. const defaultRequestInterceptors = [ interceptConfig, undefined ];
  584. const defaultResponseInterceptors = [ interceptResponse, undefined ];
  585. if (!synchronousRequestInterceptors) {
  586. logger.debug("Interceptors are executed in asynchronous mode");
  587. let chain = [ dispatchRequest, undefined ];
  588. Array.prototype.unshift.apply(chain, defaultRequestInterceptors);
  589. Array.prototype.unshift.apply(chain, requestInterceptorChain);
  590. chain = chain.concat(defaultResponseInterceptors);
  591. chain = chain.concat(responseInterceptorChain);
  592. promise = Promise.resolve(config);
  593. while (chain.length) {
  594. try {
  595. let onFulfilled = chain.shift();
  596. let onRejected = chain.shift();
  597. if (!env.isNode && config["timeout"] && onFulfilled === dispatchRequest) {
  598. onFulfilled = requestTimeout;
  599. }
  600. if (typeof onFulfilled === "function") {
  601. logger.debug(`Executing request fulfilled ${onFulfilled.name}`);
  602. }
  603. if (typeof onRejected === "function") {
  604. logger.debug(`Executing request rejected ${onRejected.name}`);
  605. }
  606. promise = promise.then(onFulfilled, onRejected);
  607. } catch (err) {
  608. logger.error(`request exception: ${err}`);
  609. }
  610. }
  611. return promise;
  612. } else {
  613. logger.debug("Interceptors are executed in synchronous mode");
  614. Array.prototype.unshift.apply(requestInterceptorChain, defaultRequestInterceptors);
  615. requestInterceptorChain = requestInterceptorChain.concat([ interceptConfig, undefined ]);
  616. while (requestInterceptorChain.length) {
  617. let onFulfilled = requestInterceptorChain.shift();
  618. let onRejected = requestInterceptorChain.shift();
  619. try {
  620. if (typeof onFulfilled === "function") {
  621. logger.debug(`Executing request fulfilled ${onFulfilled.name}`);
  622. }
  623. config = onFulfilled(config);
  624. } catch (error) {
  625. if (typeof onRejected === "function") {
  626. logger.debug(`Executing request rejected ${onRejected.name}`);
  627. }
  628. onRejected(error);
  629. break;
  630. }
  631. }
  632. try {
  633. if (!env.isNode && config["timeout"]) {
  634. promise = requestTimeout(config);
  635. } else {
  636. promise = dispatchRequest(config);
  637. }
  638. } catch (err) {
  639. return Promise.reject(err);
  640. }
  641. Array.prototype.unshift.apply(responseInterceptorChain, defaultResponseInterceptors);
  642. while (responseInterceptorChain.length) {
  643. promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
  644. }
  645. return promise;
  646. }
  647. function requestTimeout(config) {
  648. try {
  649. const timer = new Promise((_, reject) => {
  650. setTimeout(() => {
  651. let err = {
  652. message: `timeout of ${config["timeout"]}ms exceeded.`,
  653. config: config
  654. };
  655. reject(err);
  656. }, config["timeout"]);
  657. });
  658. return Promise.race([ dispatchRequest(config), timer ]);
  659. } catch (err) {
  660. logger.error(`Request Timeout exception: ${err}.`);
  661. }
  662. }
  663. };
  664. return {
  665. request: request,
  666. interceptors: interceptors,
  667. convertHeadersToLowerCase: convertHeadersToLowerCase,
  668. convertHeadersToCamelCase: convertHeadersToCamelCase,
  669. modifyResponse: modifyResponse,
  670. get: configOrUrl => {
  671. return request("GET", configOrUrl);
  672. },
  673. post: configOrUrl => {
  674. return request("POST", configOrUrl);
  675. },
  676. put: configOrUrl => {
  677. return request("PUT", configOrUrl);
  678. },
  679. patch: configOrUrl => {
  680. return request("PATCH", configOrUrl);
  681. },
  682. delete: configOrUrl => {
  683. return request("DELETE", configOrUrl);
  684. },
  685. head: configOrUrl => {
  686. return request("HEAD", configOrUrl);
  687. },
  688. options: configOrUrl => {
  689. return request("OPTIONS", configOrUrl);
  690. }
  691. };
  692. }
  693. function MagicData(env, logger) {
  694. let node = {
  695. fs: undefined,
  696. data: {}
  697. };
  698. if (env.isNode) {
  699. node.fs = require("fs");
  700. try {
  701. node.fs.accessSync("./magic.json", node.fs.constants.R_OK | node.fs.constants.W_OK);
  702. } catch (err) {
  703. node.fs.writeFileSync("./magic.json", "{}", {
  704. encoding: "utf8"
  705. });
  706. }
  707. node.data = require("./magic.json");
  708. }
  709. const defaultValueComparator = (oldVal, newVal) => {
  710. if (typeof newVal === "object") {
  711. return false;
  712. } else {
  713. return oldVal === newVal;
  714. }
  715. };
  716. const _typeConvertor = val => {
  717. if (val === "true") {
  718. return true;
  719. } else if (val === "false") {
  720. return false;
  721. } else if (typeof val === "undefined") {
  722. return null;
  723. } else {
  724. return val;
  725. }
  726. };
  727. const _valConvertor = (val, default_, session, read_no_session) => {
  728. if (session) {
  729. try {
  730. if (typeof val === "string") val = JSON.parse(val);
  731. if (val["magic_session"] === true) {
  732. val = val[session];
  733. } else {
  734. val = null;
  735. }
  736. } catch {
  737. val = null;
  738. }
  739. }
  740. if (typeof val === "string" && val !== "null") {
  741. try {
  742. val = JSON.parse(val);
  743. } catch {}
  744. }
  745. if (read_no_session === false && !!val && val["magic_session"] === true) {
  746. val = null;
  747. }
  748. if ((val === null || typeof val === "undefined") && default_ !== null && typeof default_ !== "undefined") {
  749. val = default_;
  750. }
  751. val = _typeConvertor(val);
  752. return val;
  753. };
  754. const convertToObject = obj => {
  755. if (typeof obj === "string") {
  756. let data = {};
  757. try {
  758. data = JSON.parse(obj);
  759. const type = typeof data;
  760. if (type !== "object" || data instanceof Array || type === "bool" || data === null) {
  761. data = {};
  762. }
  763. } catch {}
  764. return data;
  765. } else if (obj instanceof Array || obj === null || typeof obj === "undefined" || obj !== obj || typeof obj === "boolean") {
  766. return {};
  767. } else {
  768. return obj;
  769. }
  770. };
  771. const readForNode = (key, default_ = null, session = "", read_no_session = false, externalData = null) => {
  772. let data = externalData || node.data;
  773. if (!!data && typeof data[key] !== "undefined" && data[key] !== null) {
  774. val = data[key];
  775. } else {
  776. val = !!session ? {} : null;
  777. }
  778. val = _valConvertor(val, default_, session, read_no_session);
  779. return val;
  780. };
  781. const read = (key, default_ = null, session = "", read_no_session = false, externalData = null) => {
  782. let val = "";
  783. if (externalData || env.isNode) {
  784. val = readForNode(key, default_, session, read_no_session, externalData);
  785. } else {
  786. if (env.isSurgeLike) {
  787. val = $persistentStore.read(key);
  788. } else if (env.isQuanX) {
  789. val = $prefs.valueForKey(key);
  790. }
  791. val = _valConvertor(val, default_, session, read_no_session);
  792. }
  793. logger.debug(`READ DATA [${key}]${!!session ? `[${session}]` : ""} <${typeof val}>`+'\n'+`${JSON.stringify(val)}`);
  794. return val;
  795. };
  796. const writeForNode = (key, val, session = "", externalData = null) => {
  797. let data = externalData || node.data;
  798. data = convertToObject(data);
  799. if (!!session) {
  800. let obj = convertToObject(data[key]);
  801. obj["magic_session"] = true;
  802. obj[session] = val;
  803. data[key] = obj;
  804. } else {
  805. data[key] = val;
  806. }
  807. if (externalData !== null) {
  808. externalData = data;
  809. }
  810. return data;
  811. };
  812. const write = (key, val, session = "", externalData = null) => {
  813. if (typeof val === "undefined" || val !== val) {
  814. return false;
  815. }
  816. if (!env.isNode && (typeof val === "boolean" || typeof val === "number")) {
  817. val = String(val);
  818. }
  819. let data = "";
  820. if (externalData || env.isNode) {
  821. data = writeForNode(key, val, session, externalData);
  822. } else {
  823. if (!session) {
  824. data = val;
  825. } else {
  826. if (env.isSurgeLike) {
  827. data = !!$persistentStore.read(key) ? $persistentStore.read(key) : data;
  828. } else if (env.isQuanX) {
  829. data = !!$prefs.valueForKey(key) ? $prefs.valueForKey(key) : data;
  830. }
  831. data = convertToObject(data);
  832. data["magic_session"] = true;
  833. data[session] = val;
  834. }
  835. }
  836. if (!!data && typeof data === "object") {
  837. data = JSON.stringify(data, null, 4);
  838. }
  839. logger.debug(`WRITE DATA [${key}]${session ? `[${session}]` : ""} <${typeof val}>`+'\n'+`${JSON.stringify(val)}`);
  840. if (!externalData) {
  841. if (env.isSurgeLike) {
  842. return $persistentStore.write(data, key);
  843. } else if (env.isQuanX) {
  844. return $prefs.setValueForKey(data, key);
  845. } else if (env.isNode) {
  846. try {
  847. node.fs.writeFileSync("./magic.json", data);
  848. return true;
  849. } catch (err) {
  850. logger.error(err);
  851. return false;
  852. }
  853. }
  854. }
  855. return true;
  856. };
  857. const update = (key, val, session, comparator = defaultValueComparator, externalData = null) => {
  858. val = _typeConvertor(val);
  859. const oldValue = read(key, null, session, false, externalData);
  860. if (comparator(oldValue, val) === true) {
  861. return false;
  862. } else {
  863. const result = write(key, val, session, externalData);
  864. let newVal = read(key, null, session, false, externalData);
  865. if (comparator === defaultValueComparator && typeof newVal === "object") {
  866. return result;
  867. }
  868. return comparator(val, newVal);
  869. }
  870. };
  871. const delForNode = (key, session, externalData) => {
  872. let data = externalData || node.data;
  873. data = convertToObject(data);
  874. if (!!session) {
  875. obj = convertToObject(data[key]);
  876. delete obj[session];
  877. data[key] = obj;
  878. } else {
  879. delete data[key];
  880. }
  881. if (!!externalData) {
  882. externalData = data;
  883. }
  884. return data;
  885. };
  886. const del = (key, session = "", externalData = null) => {
  887. let data = {};
  888. if (externalData || env.isNode) {
  889. data = delForNode(key, session, externalData);
  890. if (!externalData) {
  891. node.fs.writeFileSync("./magic.json", JSON.stringify(data, null, 4));
  892. } else {
  893. externalData = data;
  894. }
  895. } else {
  896. if (!session) {
  897. if (env.isStorm) {
  898. return $persistentStore.remove(key);
  899. } else if (env.isSurgeLike) {
  900. return $persistentStore.write(null, key);
  901. } else if (env.isQuanX) {
  902. return $prefs.removeValueForKey(key);
  903. }
  904. } else {
  905. if (env.isSurgeLike) {
  906. data = $persistentStore.read(key);
  907. } else if (env.isQuanX) {
  908. data = $prefs.valueForKey(key);
  909. }
  910. data = convertToObject(data);
  911. delete data[session];
  912. const json = JSON.stringify(data, null, 4);
  913. write(key, json);
  914. }
  915. }
  916. logger.debug(`DELETE KEY [${key}]${!!session ? `[${session}]` : ""}`);
  917. };
  918. const allSessionNames = (key, externalData = null) => {
  919. let _sessions = [];
  920. let data = read(key, null, null, true, externalData);
  921. data = convertToObject(data);
  922. if (data["magic_session"] !== true) {
  923. _sessions = [];
  924. } else {
  925. _sessions = Object.keys(data).filter(key => key !== "magic_session");
  926. }
  927. logger.debug(`READ ALL SESSIONS [${key}] <${typeof _sessions}>`+'\n'+`${JSON.stringify(_sessions, null, 4)}`);
  928. return _sessions;
  929. };
  930. const allSessions = (key, externalData = null) => {
  931. let _sessions = {};
  932. let data = read(key, null, null, true, externalData);
  933. data = convertToObject(data);
  934. if (data["magic_session"] === true) {
  935. _sessions = {
  936. ...data
  937. };
  938. delete _sessions["magic_session"];
  939. }
  940. logger.debug(`READ ALL SESSIONS [${key}] <${typeof _sessions}>`+'\n'+`${JSON.stringify(_sessions, null, 4)}`);
  941. return _sessions;
  942. };
  943. return {
  944. read: read,
  945. write: write,
  946. del: del,
  947. update: update,
  948. allSessions: allSessions,
  949. allSessionNames: allSessionNames,
  950. defaultValueComparator: defaultValueComparator,
  951. convertToObject: convertToObject
  952. };
  953. }
  954. function MagicNotification(scriptName, env, logger, http) {
  955. let _barkUrl = null;
  956. let _barkKey = null;
  957. let notifyInfo = []
  958. const setBark = url => {
  959. try {
  960. let _url = url.replace(/\/+$/g, "");
  961. _barkUrl = `${/^https?:\/\/([^/]*)/.exec(_url)[0]}/push`;
  962. _barkKey = /\/([^\/]+)\/?$/.exec(_url)[1];
  963. } catch (ex) {
  964. logger.error(`Bark url error: ${ex}.`);
  965. }
  966. };
  967. function appendNotifyInfo(info, type) {
  968. if (type == 1) {
  969. notifyInfo = info
  970. } else {
  971. notifyInfo.push(info)
  972. }
  973. }
  974. function prependNotifyInfo(info) {
  975. notifyInfo.splice(0, 0, info)
  976. }
  977. function msg(subtitle, message, openUrl, mediaUrl) {
  978. let opts = {};
  979. if(openUrl){
  980. opts["open-url"] = openUrl;
  981. }
  982. if(mediaUrl){
  983. opts["media-url"] = mediaUrl;
  984. }
  985. if(!message || message.length == 0){
  986. if (Array.isArray(notifyInfo)) {
  987. message = notifyInfo.join("\n");
  988. } else {
  989. message = notifyInfo;
  990. }
  991. }
  992. if(message && message.length > 0){
  993. post(scriptName, "", message, opts);
  994. }
  995. }
  996. function post(title = scriptName, subTitle = "", body = "", opts = "") {
  997. const _adaptOpts = _opts => {
  998. try {
  999. let newOpts = {};
  1000. if (typeof _opts === "string") {
  1001. if(_opts.length > 0){
  1002. if (env.isLoon){
  1003. newOpts = {
  1004. openUrl: _opts
  1005. };
  1006. }else if (env.isQuanX){
  1007. newOpts = {
  1008. "open-url": _opts
  1009. };
  1010. }else if (env.isSurge){
  1011. newOpts = {
  1012. url: _opts
  1013. };
  1014. }
  1015. }
  1016. } else if (typeof _opts === "object") {
  1017. if (env.isLoon) {
  1018. newOpts["openUrl"] = !!_opts["open-url"] ? _opts["open-url"] : "";
  1019. newOpts["mediaUrl"] = !!_opts["media-url"] ? _opts["media-url"] : "";
  1020. } else if (env.isQuanX) {
  1021. newOpts = !!_opts["open-url"] || !!_opts["media-url"] ? _opts : {};
  1022. } else if (env.isSurge) {
  1023. let openUrl = _opts["open-url"] || _opts["openUrl"];
  1024. newOpts = openUrl ? {
  1025. url: openUrl
  1026. } : {};
  1027. }
  1028. }
  1029. return newOpts;
  1030. } catch (err) {
  1031. logger.error(`通知选项转换失败${err}`);
  1032. }
  1033. return _opts;
  1034. };
  1035. opts = _adaptOpts(opts);
  1036. if (arguments.length === 1) {
  1037. title = scriptName;
  1038. subTitle = "", body = arguments[0];
  1039. }
  1040. logger.notify('\n'+`title:${title}`+'\n'+`subTitle:${subTitle}`+'\n'+`body:${body}`+'\n'+`options:${typeof opts === "object" ? JSON.stringify(opts) : opts}`);
  1041. if (env.isSurge) {
  1042. $notification.post(title, subTitle, body, opts);
  1043. } else if (env.isLoon) {
  1044. if (!!opts) $notification.post(title, subTitle, body, opts); else $notification.post(title, subTitle, body);
  1045. } else if (env.isQuanX) {
  1046. $notify(title, subTitle, body, opts);
  1047. }
  1048. if (_barkUrl && _barkKey) {
  1049. bark(title, subTitle, body);
  1050. }
  1051. }
  1052. function debug(title = scriptName, subTitle = "", body = "", opts = "") {
  1053. if (logger.getLevel() === "DEBUG") {
  1054. if (arguments.length === 1) {
  1055. title = scriptName;
  1056. subTitle = "";
  1057. body = arguments[0];
  1058. }
  1059. this.post(title, subTitle, body, opts);
  1060. }
  1061. }
  1062. function bark(title = scriptName, subTitle = "", body = "", opts = "") {
  1063. if (typeof http === "undefined" || typeof http.post === "undefined") {
  1064. throw "Bark notification needs to import MagicHttp module.";
  1065. }
  1066. let options = {
  1067. url: _barkUrl,
  1068. headers: {
  1069. "content-type": "application/json; charset=utf-8"
  1070. },
  1071. body: {
  1072. title: title,
  1073. body: subTitle ? `${subTitle}`+'\n'+`${body}` : body,
  1074. device_key: _barkKey
  1075. }
  1076. };
  1077. http.post(options).catch(ex => {
  1078. logger.error(`Bark notify error: ${ex}`);
  1079. });
  1080. }
  1081. return {
  1082. post: post,
  1083. debug: debug,
  1084. bark: bark,
  1085. setBark: setBark,
  1086. appendNotifyInfo: appendNotifyInfo,
  1087. prependNotifyInfo: prependNotifyInfo,
  1088. msg: msg,
  1089. };
  1090. }
  1091. function MagicUtils(env, logger) {
  1092. const retry = (fn, retries = 5, interval = 0, callback = null) => {
  1093. return (...args) => {
  1094. return new Promise((resolve, reject) => {
  1095. function _retry(...args) {
  1096. Promise.resolve().then(() => fn.apply(this, args)).then(result => {
  1097. if (typeof callback === "function") {
  1098. Promise.resolve().then(() => callback(result)).then(() => {
  1099. resolve(result);
  1100. }).catch(ex => {
  1101. if (retries >= 1) {
  1102. if (interval > 0) setTimeout(() => _retry.apply(this, args), interval); else _retry.apply(this, args);
  1103. } else {
  1104. reject(ex);
  1105. }
  1106. retries--;
  1107. });
  1108. } else {
  1109. resolve(result);
  1110. }
  1111. }).catch(ex => {
  1112. logger.error(ex);
  1113. if (retries >= 1 && interval > 0) {
  1114. setTimeout(() => _retry.apply(this, args), interval);
  1115. } else if (retries >= 1) {
  1116. _retry.apply(this, args);
  1117. } else {
  1118. reject(ex);
  1119. }
  1120. retries--;
  1121. });
  1122. }
  1123. _retry.apply(this, args);
  1124. });
  1125. };
  1126. };
  1127. const formatTime = (time, fmt = "yyyy-MM-dd hh:mm:ss") => {
  1128. let o = {
  1129. "M+": time.getMonth() + 1,
  1130. "d+": time.getDate(),
  1131. "h+": time.getHours(),
  1132. "m+": time.getMinutes(),
  1133. "s+": time.getSeconds(),
  1134. "q+": Math.floor((time.getMonth() + 3) / 3),
  1135. S: time.getMilliseconds()
  1136. };
  1137. if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length));
  1138. for (let k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
  1139. return fmt;
  1140. };
  1141. const now = () => {
  1142. return formatTime(new Date(), "yyyy-MM-dd hh:mm:ss");
  1143. };
  1144. const today = () => {
  1145. return formatTime(new Date(), "yyyy-MM-dd");
  1146. };
  1147. const sleep = time => {
  1148. return new Promise(resolve => setTimeout(resolve, time));
  1149. };
  1150. const assert = (val, msg = null) => {
  1151. if (env.isNode) {
  1152. const _assert = require("assert");
  1153. if (msg) _assert(val, msg); else _assert(val);
  1154. } else {
  1155. if (val !== true) {
  1156. let err = `AssertionError: ${msg || "The expression evaluated to a falsy value."}`;
  1157. logger.error(err);
  1158. }
  1159. }
  1160. };
  1161. return {
  1162. retry: retry,
  1163. formatTime: formatTime,
  1164. now: now,
  1165. today: today,
  1166. sleep: sleep,
  1167. assert: assert
  1168. };
  1169. }
  1170. function MagicQingLong(env, data, logger) {
  1171. let qlUrl = "";
  1172. let qlName = "";
  1173. let qlClient = "";
  1174. let qlSecret = "";
  1175. let qlPwd = "";
  1176. let qlToken = "";
  1177. const magicJsonFileName = "magic.json";
  1178. const timeout = 3e3;
  1179. const http = (() => MagicHttp(env, logger))();
  1180. const init = (url, clientId, clientSecret, username, password) => {
  1181. qlUrl = url;
  1182. qlClient = clientId;
  1183. qlSecret = clientSecret;
  1184. qlName = username;
  1185. qlPwd = password;
  1186. };
  1187. function readQingLongConfig(config) {
  1188. qlUrl = qlUrl || data.read("magic_qlurl");
  1189. qlToken = qlToken || data.read("magic_qltoken");
  1190. logger.debug(`QingLong url: ${qlUrl}`+'\n'+`QingLong token: ${qlToken}`);
  1191. return config;
  1192. }
  1193. function setBaseUrlAndTimeout(config) {
  1194. if (!qlUrl) {
  1195. qlUrl = data.read("magic_qlurl");
  1196. }
  1197. if (config.url.indexOf(qlUrl) < 0) {
  1198. config.url = `${qlUrl}${config.url}`;
  1199. }
  1200. return {
  1201. ...config,
  1202. timeout: timeout
  1203. };
  1204. }
  1205. function setTimestamp(config) {
  1206. config.params = {
  1207. ...config.params,
  1208. t: Date.now()
  1209. };
  1210. return config;
  1211. }
  1212. async function setAuthorization(config) {
  1213. qlToken = qlToken || data.read("magic_qltoken", "");
  1214. if (!qlToken) {
  1215. await getToken();
  1216. }
  1217. config.headers["authorization"] = `Bearer ${qlToken}`;
  1218. return config;
  1219. }
  1220. function switchClientMode(config) {
  1221. qlClient = qlClient || data.read("magic_qlclient");
  1222. if (!!qlClient) {
  1223. config.url = config.url.replace("/api/", "/open/");
  1224. }
  1225. return config;
  1226. }
  1227. async function refreshToken(error) {
  1228. try {
  1229. const message = error.message || error.error || JSON.stringify(error);
  1230. if ((message.indexOf("NSURLErrorDomain") >= 0 && message.indexOf("-1012") >= 0 || !!error.response && error.response.status === 401) && !!error.config && error.config.refreshToken !== true) {
  1231. logger.warning(`QingLong Panel token has expired`);
  1232. logger.info("Refreshing the QingLong Panel token");
  1233. await getToken();
  1234. error.config["refreshToken"] = true;
  1235. logger.info("Call the previous method again");
  1236. return await http.request(error.config.method, error.config);
  1237. } else {
  1238. return Promise.reject(error);
  1239. }
  1240. } catch (ex) {
  1241. return Promise.reject(ex);
  1242. }
  1243. }
  1244. http.interceptors.request.use(setBaseUrlAndTimeout, undefined);
  1245. http.interceptors.request.use(switchClientMode, undefined, {
  1246. runWhen: config => {
  1247. return config.url.indexOf("api/user/login") < 0 && config.url.indexOf("open/auth/token") < 0;
  1248. }
  1249. });
  1250. http.interceptors.request.use(setAuthorization, undefined, {
  1251. runWhen: config => {
  1252. return config.url.indexOf("api/user/login") < 0 && config.url.indexOf("open/auth/token") < 0;
  1253. }
  1254. });
  1255. http.interceptors.request.use(setTimestamp, undefined, {
  1256. runWhen: config => {
  1257. return config.url.indexOf("open/auth/token") < 0;
  1258. }
  1259. });
  1260. http.interceptors.request.use(readQingLongConfig, undefined);
  1261. http.interceptors.response.use(undefined, refreshToken);
  1262. async function getToken() {
  1263. qlClient = qlClient || data.read("magic_qlclient");
  1264. qlSecret = qlSecret || data.read("magic_qlsecrt");
  1265. qlName = qlName || data.read("magic_qlname");
  1266. qlPwd = qlPwd || data.read("magic_qlpwd");
  1267. if (qlUrl && qlClient && qlSecret) {
  1268. logger.info("Get token from QingLong Panel");
  1269. await http.get({
  1270. url: `/open/auth/token`,
  1271. headers: {
  1272. "content-type": "application/json"
  1273. },
  1274. params: {
  1275. client_id: qlClient,
  1276. client_secret: qlSecret
  1277. }
  1278. }).then(resp => {
  1279. if (Object.keys(resp.body).length > 0 && resp.body.data && resp.body.data.token) {
  1280. logger.info("Successfully logged in to QingLong Panel");
  1281. qlToken = resp.body.data.token;
  1282. data.write("magic_qltoken", qlToken);
  1283. } else {
  1284. throw new Error("Get QingLong Panel token failed.");
  1285. }
  1286. }).catch(err => {
  1287. logger.error(`Error logging in to QingLong Panel.`+'\n'+`${err.message || err}`);
  1288. });
  1289. } else if (qlUrl && qlName && qlPwd) {
  1290. await http.post({
  1291. url: `/api/user/login`,
  1292. headers: {
  1293. "content-type": "application/json"
  1294. },
  1295. body: {
  1296. username: qlName,
  1297. password: qlPwd
  1298. }
  1299. }).then(resp => {
  1300. logger.info("Successfully logged in to QingLong Panel");
  1301. qlToken = resp.body.data.token;
  1302. data.write("magic_qltoken", qlToken);
  1303. }).catch(err => {
  1304. logger.error(`Error logging in to QingLong Panel.`+'\n'+`${err.message || err}`);
  1305. });
  1306. }
  1307. return qlToken;
  1308. }
  1309. async function setEnv(name, value, id = null) {
  1310. qlUrl = qlUrl || data.read("magic_qlurl");
  1311. if (id === null) {
  1312. let envIds = await setEnvs([ {
  1313. name: name,
  1314. value: value
  1315. } ]);
  1316. if (!!envIds && envIds.length === 1) {
  1317. return envIds[0];
  1318. }
  1319. } else {
  1320. await http.put({
  1321. url: `/api/envs`,
  1322. headers: {
  1323. "content-type": "application/json"
  1324. },
  1325. body: {
  1326. name: name,
  1327. value: value,
  1328. id: id
  1329. }
  1330. }).then(resp => {
  1331. if (resp.body.code === 200) {
  1332. logger.debug(`QINGLONG UPDATE ENV ${name} <${typeof value}> (${id})`+'\n'+`${JSON.stringify(value)}`);
  1333. return true;
  1334. } else {
  1335. logger.error(`Error adding environment variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1336. }
  1337. }).catch(err => {
  1338. logger.error(`Error adding environment variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1339. return false;
  1340. });
  1341. }
  1342. }
  1343. async function setEnvs(envs) {
  1344. let envIds = [];
  1345. await http.post({
  1346. url: `/api/envs`,
  1347. headers: {
  1348. "content-type": "application/json"
  1349. },
  1350. body: envs
  1351. }).then(resp => {
  1352. if (resp.body.code === 200) {
  1353. resp.body.data.forEach(element => {
  1354. logger.debug(`QINGLONG ADD ENV ${element.name} <${typeof element.value}> (${element.id})`+'\n'+`${JSON.stringify(element)}`);
  1355. envIds.push(element.id);
  1356. });
  1357. } else {
  1358. logger.error(`Error adding environments variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1359. }
  1360. }).catch(err => {
  1361. logger.error(`Error adding environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1362. });
  1363. return envIds;
  1364. }
  1365. async function delEnvs(ids) {
  1366. return await http.delete({
  1367. url: `/api/envs`,
  1368. headers: {
  1369. accept: "application/json",
  1370. "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  1371. connection: "keep-alive",
  1372. "content-type": "application/json;charset=UTF-8",
  1373. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30"
  1374. },
  1375. body: ids
  1376. }).then(resp => {
  1377. if (resp.body.code === 200) {
  1378. logger.debug(`QINGLONG DELETE ENV IDS: ${ids}`);
  1379. return true;
  1380. } else {
  1381. logger.error(`Error deleting environments variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1382. return false;
  1383. }
  1384. }).catch(err => {
  1385. logger.error(`Error deleting environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1386. });
  1387. }
  1388. async function getEnvs(name = null, searchValue = "", retired = 0) {
  1389. let envs = [];
  1390. await http.get({
  1391. url: `/api/envs`,
  1392. headers: {
  1393. "content-type": "application/json"
  1394. },
  1395. params: {
  1396. searchValue: searchValue
  1397. }
  1398. }).then(resp => {
  1399. if (resp.body.code === 200) {
  1400. const allEnvs = resp.body.data;
  1401. if (!!name) {
  1402. let _envs = [];
  1403. for (const env of allEnvs) {
  1404. if (env.name === name) {
  1405. envs.push(env);
  1406. }
  1407. }
  1408. envs = _envs;
  1409. }
  1410. envs = allEnvs;
  1411. } else {
  1412. throw new Error(`Error reading environment variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1413. }
  1414. }).catch(err => {
  1415. throw new Error(`Error reading environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1416. });
  1417. return envs;
  1418. }
  1419. async function getEnv(id) {
  1420. let env = null;
  1421. const allEnvs = await getEnvs();
  1422. for (const _env of allEnvs) {
  1423. if (_env.id === id) {
  1424. env = _env;
  1425. break;
  1426. }
  1427. }
  1428. return env;
  1429. }
  1430. async function disableEnvs(ids) {
  1431. let result = false;
  1432. await http.put({
  1433. url: `/api/envs/disable`,
  1434. headers: {
  1435. accept: "application/json",
  1436. "accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  1437. connection: "keep-alive",
  1438. "content-type": "application/json;charset=UTF-8",
  1439. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30"
  1440. },
  1441. body: ids
  1442. }).then(resp => {
  1443. if (resp.body.code === 200) {
  1444. logger.debug(`QINGLONG DISABLED ENV IDS: ${ids}`);
  1445. result = true;
  1446. } else {
  1447. logger.error(`Error disabling environments variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1448. }
  1449. }).catch(err => {
  1450. logger.error(`Error disabling environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1451. });
  1452. return result;
  1453. }
  1454. async function enableEnvs(ids) {
  1455. let result = false;
  1456. await http.put({
  1457. url: `/api/envs/enable`,
  1458. headers: {
  1459. accept: "application/json",
  1460. "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  1461. connection: "keep-alive",
  1462. "content-type": "application/json;charset=UTF-8",
  1463. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30"
  1464. },
  1465. body: ids
  1466. }).then(resp => {
  1467. if (resp.body.code === 200) {
  1468. logger.debug(`QINGLONG ENABLED ENV IDS: ${ids}`);
  1469. result = true;
  1470. } else {
  1471. logger.error(`Error enabling environments variable from Qilong panel.`+'\n'+`${JSON.stringify(resp)}`);
  1472. }
  1473. }).catch(err => {
  1474. logger.error(`Error enabling environments variable from Qilong panel.`+'\n'+`${err.message || err}`);
  1475. });
  1476. return result;
  1477. }
  1478. async function addScript(name, path = "", content = "") {
  1479. let result = false;
  1480. await http.post({
  1481. url: `/api/scripts`,
  1482. headers: {
  1483. "content-type": "application/json"
  1484. },
  1485. body: {
  1486. filename: name,
  1487. path: path,
  1488. content: content
  1489. }
  1490. }).then(resp => {
  1491. if (resp.body.code === 200) {
  1492. result = true;
  1493. } else {
  1494. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1495. }
  1496. }).catch(err => {
  1497. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1498. });
  1499. return result;
  1500. }
  1501. async function getScript(name, path = "") {
  1502. let content = "";
  1503. await http.get({
  1504. url: `/api/scripts/${name}`,
  1505. params: {
  1506. path: path
  1507. }
  1508. }).then(resp => {
  1509. if (resp.body.code === 200) {
  1510. content = resp.body.data;
  1511. } else {
  1512. throw new Error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1513. }
  1514. }).catch(err => {
  1515. throw new Error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1516. });
  1517. return content;
  1518. }
  1519. async function editScript(name, path = "", content = "") {
  1520. let result = false;
  1521. await http.put({
  1522. url: `/api/scripts`,
  1523. headers: {
  1524. "content-type": "application/json"
  1525. },
  1526. body: {
  1527. filename: name,
  1528. path: path,
  1529. content: content
  1530. }
  1531. }).then(resp => {
  1532. if (resp.body.code === 200) {
  1533. result = true;
  1534. } else {
  1535. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1536. }
  1537. }).catch(err => {
  1538. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1539. });
  1540. return result;
  1541. }
  1542. async function delScript(name, path = "") {
  1543. let result = false;
  1544. await http.delete({
  1545. url: `/api/scripts`,
  1546. headers: {
  1547. "content-type": "application/json"
  1548. },
  1549. body: {
  1550. filename: name,
  1551. path: path
  1552. }
  1553. }).then(resp => {
  1554. if (resp.body.code === 200) {
  1555. result = true;
  1556. } else {
  1557. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1558. }
  1559. }).catch(err => {
  1560. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1561. });
  1562. return result;
  1563. }
  1564. async function write(key, val, session = "") {
  1565. let qlContent = await getScript(magicJsonFileName, "");
  1566. let qlData = data.convertToObject(qlContent);
  1567. let writeResult = data.write(key, val, session, qlData);
  1568. qlContent = JSON.stringify(qlData, null, 4);
  1569. let editResult = await editScript(magicJsonFileName, "", qlContent);
  1570. return editResult && writeResult;
  1571. }
  1572. async function batchWrite(...args) {
  1573. let qlContent = await getScript(magicJsonFileName, "");
  1574. let qlData = data.convertToObject(qlContent);
  1575. for (let arg of args) {
  1576. data.write(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", qlData);
  1577. }
  1578. qlContent = JSON.stringify(qlData, null, 4);
  1579. return await editScript(magicJsonFileName, "", qlContent);
  1580. }
  1581. async function update(key, val, session, comparator = data.defaultValueComparator) {
  1582. let qlContent = await getScript(magicJsonFileName, "");
  1583. let qlData = data.convertToObject(qlContent);
  1584. const updateResult = data.update(key, val, session, comparator, qlData);
  1585. let editScriptResult = false;
  1586. if (updateResult === true) {
  1587. qlContent = JSON.stringify(qlData, null, 4);
  1588. editScriptResult = await editScript(magicJsonFileName, "", qlContent);
  1589. }
  1590. return updateResult && editScriptResult;
  1591. }
  1592. async function batchUpdate(...args) {
  1593. let qlContent = await getScript(magicJsonFileName, "");
  1594. let qlData = data.convertToObject(qlContent);
  1595. for (let arg of args) {
  1596. data.update(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", typeof arg[3] !== "undefined" ? arg["comparator"] : data.defaultValueComparator, qlData);
  1597. }
  1598. qlContent = JSON.stringify(qlData, null, 4);
  1599. return await editScript(magicJsonFileName, "", qlContent);
  1600. }
  1601. async function read(key, val, session = "", read_no_session = false) {
  1602. let qlContent = await getScript(magicJsonFileName, "");
  1603. let qlData = data.convertToObject(qlContent);
  1604. return data.read(key, val, session, read_no_session, qlData);
  1605. }
  1606. async function batchRead(...args) {
  1607. let qlContent = await getScript(magicJsonFileName, "");
  1608. let qlData = data.convertToObject(qlContent);
  1609. let results = [];
  1610. for (let arg of args) {
  1611. const result = data.read(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", typeof arg[3] === "boolean" ? arg[3] : false, qlData);
  1612. results.push(result);
  1613. }
  1614. return results;
  1615. }
  1616. async function del(key, session = "") {
  1617. let qlContent = await getScript(magicJsonFileName, "");
  1618. let qlData = data.convertToObject(qlContent);
  1619. const delResult = data.del(key, session, qlData);
  1620. qlContent = JSON.stringify(qlData, null, 4);
  1621. const editResult = await editScript(magicJsonFileName, "", qlContent);
  1622. return delResult && editResult;
  1623. }
  1624. async function batchDel(...args) {
  1625. let qlContent = await getScript(magicJsonFileName, "");
  1626. let qlData = data.convertToObject(qlContent);
  1627. for (let arg of args) {
  1628. data.del(arg[0], typeof arg[1] !== "undefined" ? arg[1] : "", qlData);
  1629. }
  1630. qlContent = JSON.stringify(qlData, null, 4);
  1631. return await editScript(magicJsonFileName, "", qlContent);
  1632. }
  1633. async function allSessionNames(key) {
  1634. let qlContent = await getScript(magicJsonFileName, "");
  1635. let qlData = data.convertToObject(qlContent);
  1636. return data.allSessionNames(key, qlData);
  1637. }
  1638. async function allSessions(key) {
  1639. let qlContent = await getScript(magicJsonFileName, "");
  1640. let qlData = data.convertToObject(qlContent);
  1641. return data.allSessions(key, qlData);
  1642. }
  1643. return {
  1644. url: qlUrl || data.read("magic_qlurl"),
  1645. init: init,
  1646. getToken: getToken,
  1647. setEnv: setEnv,
  1648. setEnvs: setEnvs,
  1649. getEnv: getEnv,
  1650. getEnvs: getEnvs,
  1651. delEnvs: delEnvs,
  1652. disableEnvs: disableEnvs,
  1653. enableEnvs: enableEnvs,
  1654. addScript: addScript,
  1655. getScript: getScript,
  1656. editScript: editScript,
  1657. delScript: delScript,
  1658. write: write,
  1659. read: read,
  1660. del: del,
  1661. update: update,
  1662. batchWrite: batchWrite,
  1663. batchRead: batchRead,
  1664. batchUpdate: batchUpdate,
  1665. batchDel: batchDel,
  1666. allSessions: allSessions,
  1667. allSessionNames: allSessionNames
  1668. };
  1669. }