main.ts 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. import * as readline from "node:readline";
  2. import ModuleManager from "./ModuleManager";
  3. import LogBook from "./LogBook";
  4. import JobQueue from "./JobQueue";
  5. import JobStatistics from "./JobStatistics";
  6. const logBook = LogBook.getPrimaryInstance();
  7. process.removeAllListeners("uncaughtException");
  8. process.on("uncaughtException", err => {
  9. if (err.name === "ECONNREFUSED" || err.name === "UNCERTAIN_STATE") return;
  10. logBook.log({
  11. message: err.message,
  12. type: "error",
  13. category: "uncaught-exceptions",
  14. data: {
  15. error: err.message
  16. ? {
  17. cause: err.cause,
  18. name: err.name,
  19. stack: err.stack
  20. }
  21. : err
  22. }
  23. });
  24. });
  25. const moduleManager = ModuleManager.getPrimaryInstance();
  26. const jobQueue = JobQueue.getPrimaryInstance();
  27. moduleManager.startup();
  28. // TOOD remove, or put behind debug option
  29. // eslint-disable-next-line
  30. // @ts-ignore
  31. global.moduleManager = moduleManager;
  32. // eslint-disable-next-line
  33. // @ts-ignore
  34. global.jobQueue = jobQueue;
  35. // eslint-disable-next-line
  36. // @ts-ignore
  37. global.rs = () => {
  38. process.exit();
  39. };
  40. setTimeout(async () => {
  41. const Model = await jobQueue.runJob("data", "getModel", { name: "abc" });
  42. // console.log("Model", Model);
  43. const abcs = await Model.find({});
  44. console.log("Abcs", abcs);
  45. console.log(
  46. "getData",
  47. await Model.getData({
  48. page: 1,
  49. pageSize: 3,
  50. properties: [
  51. "title",
  52. "markdown",
  53. "status",
  54. "showToNewUsers",
  55. "createdBy"
  56. ],
  57. sort: {},
  58. queries: [
  59. {
  60. data: "v7",
  61. filter: { property: "title" },
  62. filterType: "contains"
  63. }
  64. ],
  65. operator: "and"
  66. })
  67. );
  68. // Model.create({
  69. // name: "Test name",
  70. // someNumbers: [1, 2, 3, 4],
  71. // songs: [],
  72. // aNumber: 941
  73. // });
  74. // Events schedule (was notifications)
  75. const now = Date.now();
  76. await jobQueue.runJob("events", "schedule", {
  77. channel: "test",
  78. time: 30000
  79. });
  80. await jobQueue.runJob("events", "subscribe", {
  81. channel: "test",
  82. type: "schedule",
  83. callback: () => {
  84. console.log(`SCHEDULED: ${now} :: ${Date.now()}`);
  85. }
  86. });
  87. // Events (was cache pub/sub)
  88. await jobQueue.runJob("events", "subscribe", {
  89. channel: "test",
  90. callback: value => {
  91. console.log(`PUBLISHED: ${value}`);
  92. }
  93. });
  94. await jobQueue.runJob("events", "publish", {
  95. channel: "test",
  96. value: "a value!"
  97. });
  98. }, 100);
  99. // setTimeout(async () => {
  100. // const start = Date.now();
  101. // const x = [];
  102. // while (x.length < 1) {
  103. // x.push(jobQueue.runJob("stations", "addC", {}).catch(() => {}));
  104. // }
  105. // const y = await Promise.all(x);
  106. // console.log(y);
  107. // // const a = await jobQueue.runJob("stations", "addC", {}).catch(() => {});
  108. // // console.log(555, a);
  109. // const difference = Date.now() - start;
  110. // console.log({ difference });
  111. // }, 100);
  112. // setTimeout(() => {
  113. // clearTimeout(interval);
  114. // }, 3000);
  115. // setTimeout(async () => {
  116. // const _id = "6371212daf4e9f8fb14444b2";
  117. // logBook.log("Find with no projection");
  118. // await moduleManager
  119. // .runJob("data", "find", {
  120. // collection: "abc",
  121. // filter: {
  122. // _id
  123. // }
  124. // })
  125. // .then(console.log)
  126. // .catch(console.error);
  127. // logBook.log("Find with no projection, and a more advanced filter");
  128. // await moduleManager
  129. // .runJob("data", "find", {
  130. // collection: "abc",
  131. // filter: {
  132. // "autofill.enabled": true
  133. // }
  134. // })
  135. // .then(console.log)
  136. // .catch(console.error);
  137. // logBook.log("Find with array projection");
  138. // await moduleManager
  139. // .runJob("data", "find", {
  140. // collection: "abc",
  141. // filter: {
  142. // _id
  143. // },
  144. // projection: ["name"]
  145. // })
  146. // .then(console.log)
  147. // .catch(console.error);
  148. // logBook.log("Find with object boolean projection");
  149. // await moduleManager
  150. // .runJob("data", "find", {
  151. // collection: "abc",
  152. // filter: {
  153. // _id
  154. // },
  155. // projection: { name: true },
  156. // limit: 1
  157. // })
  158. // .then(console.log)
  159. // .catch(console.error);
  160. // logBook.log("Find with object number projection");
  161. // await moduleManager
  162. // .runJob("data", "find", {
  163. // collection: "abc",
  164. // filter: {
  165. // _id
  166. // },
  167. // projection: { name: 1 }
  168. // })
  169. // .then(console.log)
  170. // .catch(console.error);
  171. // logBook.log("Find with object number projection");
  172. // await moduleManager
  173. // .runJob("data", "find", {
  174. // collection: "abc",
  175. // filter: {
  176. // _id
  177. // },
  178. // projection: { "autofill.enabled": true }
  179. // })
  180. // .then(console.log)
  181. // .catch(console.error);
  182. // logBook.log("Find for testing casting");
  183. // await moduleManager
  184. // .runJob("data", "find", {
  185. // collection: "abc",
  186. // filter: {
  187. // // "songs._id": "6371212daf4e9f8fb14444b0"
  188. // // "songs._id": "6371212daf4e9f8fb14444b2"
  189. // // songs: {
  190. // // _id: "6371212daf4e9f8fb14444b0"
  191. // // }
  192. // // songs: {
  193. // // randomProperty: "6371212daf4e9f8fb14444b0"
  194. // // }
  195. // "songs.obj.test": "6371212daf4e9f8fb14444b0"
  196. // },
  197. // // projection: {
  198. // // // songs: true,
  199. // // // someNumbers: false
  200. // // },
  201. // limit: 1
  202. // })
  203. // .then(console.log)
  204. // .catch(console.error);
  205. // logBook.log("Find for testing with $in");
  206. // await moduleManager
  207. // .runJob("data", "find", {
  208. // collection: "abc",
  209. // filter: {
  210. // _id
  211. // },
  212. // allowedRestricted: true,
  213. // // projection: {
  214. // // // songs: true,
  215. // // // someNumbers: false
  216. // // },
  217. // limit: 1
  218. // })
  219. // .then(console.log)
  220. // .catch(console.error);
  221. // logBook.log("Find for testing with $in");
  222. // await moduleManager
  223. // .runJob("data", "find", {
  224. // collection: "abc",
  225. // filter: {
  226. // "songs._id": "6371212daf4e9f8fb14444b0"
  227. // },
  228. // allowedRestricted: true,
  229. // // projection: {
  230. // // // songs: true,
  231. // // // someNumbers: false
  232. // // },
  233. // limit: 1
  234. // })
  235. // .then(console.log)
  236. // .catch(console.error);
  237. // logBook.log("Find for testing with $in with numbers");
  238. // await moduleManager
  239. // .runJob("data", "find", {
  240. // collection: "abc",
  241. // filter: {
  242. // someNumbers: { $in: [54, 84] }
  243. // },
  244. // limit: 1,
  245. // useCache: false
  246. // })
  247. // .then(console.log)
  248. // .catch(console.error);
  249. // moduleManager
  250. // .runJob("data", "find", {
  251. // collection: "abc",
  252. // filter: { _id: new ObjectId(_id) },
  253. // limit: 1
  254. // })
  255. // .then(console.log)
  256. // .catch(console.error);
  257. // }, 0);
  258. const rl = readline.createInterface({
  259. input: process.stdin,
  260. output: process.stdout,
  261. completer: (command: string) => {
  262. const parts = command.split(" ");
  263. const commands = ["eval "];
  264. if (parts.length === 1) {
  265. const hits = commands.filter(c => c.startsWith(parts[0]));
  266. return [hits.length ? hits : commands, command];
  267. }
  268. return [];
  269. },
  270. removeHistoryDuplicates: true
  271. });
  272. const shutdown = async () => {
  273. if (rl) {
  274. rl.removeAllListeners();
  275. rl.close();
  276. }
  277. await moduleManager.shutdown().catch(() => process.exit(1));
  278. process.exit(0);
  279. };
  280. process.on("SIGINT", shutdown);
  281. process.on("SIGQUIT", shutdown);
  282. process.on("SIGTERM", shutdown);
  283. const runCommand = (line: string) => {
  284. const [command, ...args] = line.split(" ");
  285. switch (command) {
  286. case "help": {
  287. console.log("Commands:");
  288. console.log("status - Show module manager and job queue status");
  289. console.log("stats - Shows jobs stats");
  290. console.log("queue - Shows a table of all jobs in the queue");
  291. console.log("active - Shows a table of all jobs currently running");
  292. console.log("jobinfo <jobId> - Print all info about a job");
  293. console.log("eval - Run a command");
  294. console.log("debug");
  295. console.log("log - Change LogBook settings");
  296. break;
  297. }
  298. case "status": {
  299. console.log("Module Manager Status:");
  300. console.table(moduleManager.getStatus());
  301. console.log("Job Queue Status:");
  302. console.table(jobQueue.getStatus());
  303. break;
  304. }
  305. case "stats": {
  306. console.log("Job Queue Stats:");
  307. console.table(JobStatistics.getPrimaryInstance().getStats());
  308. break;
  309. }
  310. case "queue": {
  311. const queueStatus = jobQueue.getQueueStatus().queue;
  312. if (queueStatus.length === 0)
  313. console.log("There are no jobs in the queue.");
  314. else
  315. console.log(
  316. `There are ${queueStatus.length} jobs in the queue.`
  317. );
  318. console.table(queueStatus);
  319. break;
  320. }
  321. case "active": {
  322. const activeStatus = jobQueue.getQueueStatus().active;
  323. if (activeStatus.length === 0)
  324. console.log("There are no active jobs.");
  325. else console.log(`There are ${activeStatus.length} active jobs.`);
  326. console.table(activeStatus);
  327. break;
  328. }
  329. case "jobinfo": {
  330. if (args.length === 0) console.log("Please specify a jobId");
  331. else {
  332. const jobId = args[0];
  333. const job = jobQueue.getJob(jobId);
  334. if (!job) console.log("Job not found");
  335. else {
  336. console.table(job.toJSON());
  337. }
  338. }
  339. break;
  340. }
  341. case "eval": {
  342. const evalCommand = args.join(" ");
  343. console.log(`Running eval command: ${evalCommand}`);
  344. // eslint-disable-next-line no-eval
  345. const response = eval(evalCommand);
  346. console.log(`Eval response: `, response);
  347. break;
  348. }
  349. case "debug": {
  350. // eslint-disable-next-line no-debugger
  351. debugger;
  352. break;
  353. }
  354. case "log": {
  355. const [output, key, action, ...values] = args;
  356. if (
  357. output === undefined ||
  358. key === undefined ||
  359. action === undefined
  360. ) {
  361. console.log(
  362. `Missing required parameters (log <output> <key> <action> [values])`
  363. );
  364. break;
  365. }
  366. let value: any[] | undefined;
  367. if (values !== undefined && values.length >= 1) {
  368. value = values.map(_filter => JSON.parse(_filter));
  369. if (value.length === 1) [value] = value;
  370. }
  371. logBook
  372. // eslint-disable-next-line
  373. // @ts-ignore
  374. .updateOutput(output, key, action, value)
  375. .then(() => console.log("Successfully updated outputs"))
  376. .catch((err: Error) =>
  377. console.log(`Error updating outputs "${err.message}"`)
  378. );
  379. break;
  380. }
  381. default: {
  382. if (!/^\s*$/.test(command))
  383. console.log(`Command "${command}" not found`);
  384. }
  385. }
  386. };
  387. rl.on("line", runCommand);