BaseModule.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import JobContext from "./JobContext";
  2. import JobQueue from "./JobQueue";
  3. import LogBook, { Log } from "./LogBook";
  4. import ModuleManager from "./ModuleManager";
  5. import { Modules } from "./types/Modules";
  6. export enum ModuleStatus {
  7. LOADED = "LOADED",
  8. STARTING = "STARTING",
  9. STARTED = "STARTED",
  10. STOPPED = "STOPPED",
  11. STOPPING = "STOPPING",
  12. ERROR = "ERROR",
  13. DISABLED = "DISABLED"
  14. }
  15. export default abstract class BaseModule {
  16. protected moduleManager: ModuleManager;
  17. protected logBook: LogBook;
  18. protected jobQueue: JobQueue;
  19. protected name: string;
  20. protected status: ModuleStatus;
  21. protected dependentModules: (keyof Modules)[];
  22. protected jobApiDefault: boolean;
  23. protected jobConfig: Record<
  24. string,
  25. | boolean
  26. | {
  27. api?: boolean;
  28. method?: (context: JobContext, payload?: any) => Promise<any>;
  29. }
  30. >;
  31. protected jobs: Record<
  32. string,
  33. {
  34. api: boolean;
  35. method: (context: JobContext, payload?: any) => Promise<any>;
  36. }
  37. >;
  38. /**
  39. * Base Module
  40. *
  41. * @param name - Module name
  42. */
  43. public constructor(name: string) {
  44. this.moduleManager = ModuleManager.getPrimaryInstance();
  45. this.logBook = LogBook.getPrimaryInstance();
  46. this.jobQueue = JobQueue.getPrimaryInstance();
  47. this.name = name;
  48. this.status = ModuleStatus.LOADED;
  49. this.dependentModules = [];
  50. this.jobApiDefault = true;
  51. this.jobConfig = {};
  52. this.jobs = {};
  53. this.log(`Module (${this.name}) loaded`);
  54. }
  55. /**
  56. * getName - Get module name
  57. *
  58. * @returns name
  59. */
  60. public getName() {
  61. return this.name;
  62. }
  63. /**
  64. * getStatus - Get module status
  65. *
  66. * @returns status
  67. */
  68. public getStatus() {
  69. return this.status;
  70. }
  71. /**
  72. * setStatus - Set module status
  73. *
  74. * @param status - Module status
  75. */
  76. public setStatus(status: ModuleStatus) {
  77. this.status = status;
  78. }
  79. /**
  80. * getDependentModules - Get module dependencies
  81. */
  82. public getDependentModules() {
  83. return this.dependentModules;
  84. }
  85. /**
  86. * _loadJobs - Load jobs available via api module
  87. */
  88. private async _loadJobs() {
  89. this.jobs = {};
  90. const module = Object.getPrototypeOf(this);
  91. await Promise.all(
  92. Object.getOwnPropertyNames(module).map(async property => {
  93. if (
  94. typeof module[property] !== "function" ||
  95. Object.prototype.hasOwnProperty.call(
  96. BaseModule.prototype,
  97. property
  98. )
  99. )
  100. return;
  101. const options = this.jobConfig[property];
  102. let api = this.jobApiDefault;
  103. if (
  104. typeof options === "object" &&
  105. typeof options.api === "boolean"
  106. )
  107. api = options.api;
  108. else if (typeof options === "boolean") api = options;
  109. this.jobs[property] = {
  110. api,
  111. method: module[property]
  112. };
  113. })
  114. );
  115. await Promise.all(
  116. Object.entries(this.jobConfig).map(async ([name, options]) => {
  117. if (
  118. typeof options === "object" &&
  119. typeof options.method === "function"
  120. ) {
  121. if (this.jobs[name])
  122. throw new Error(`Job "${name}" is already defined`);
  123. this.jobs[name] = {
  124. api: options.api ?? this.jobApiDefault,
  125. method: options.method
  126. };
  127. }
  128. })
  129. );
  130. }
  131. /**
  132. * getJob - Get module job
  133. */
  134. public getJob(name: string) {
  135. if (!this.jobs[name]) throw new Error(`Job "${name}" not found.`);
  136. return this.jobs[name];
  137. }
  138. /**
  139. * startup - Startup module
  140. */
  141. public async startup() {
  142. this.log(`Module (${this.name}) starting`);
  143. this.setStatus(ModuleStatus.STARTING);
  144. }
  145. /**
  146. * started - called with the module has started
  147. */
  148. protected async started() {
  149. await this._loadJobs();
  150. this.log(`Module (${this.name}) started`);
  151. this.setStatus(ModuleStatus.STARTED);
  152. }
  153. /**
  154. * shutdown - Shutdown module
  155. */
  156. public async shutdown() {
  157. this.log(`Module (${this.name}) stopping`);
  158. this.setStatus(ModuleStatus.STOPPING);
  159. }
  160. /**
  161. * stopped - called when the module has stopped
  162. */
  163. protected async stopped() {
  164. this.log(`Module (${this.name}) stopped`);
  165. this.setStatus(ModuleStatus.STOPPED);
  166. }
  167. /**
  168. * log - Add log to logbook
  169. *
  170. * @param log - Log message or object
  171. */
  172. protected log(log: string | Omit<Log, "timestamp" | "category">) {
  173. const {
  174. message,
  175. type = undefined,
  176. data = {}
  177. } = {
  178. ...(typeof log === "string" ? { message: log } : log)
  179. };
  180. this.logBook.log({
  181. message,
  182. type,
  183. category: `modules.${this.getName()}`,
  184. data: {
  185. moduleName: this.name,
  186. ...data
  187. }
  188. });
  189. }
  190. }