MagicJS3.js 59 KB

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