BaseModule.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import { readdir } from "fs/promises";
  2. import path from "path";
  3. import { forEachIn } from "@common/utils/forEachIn";
  4. import LogBook, { Log } from "@/LogBook";
  5. import ModuleManager from "@/ModuleManager";
  6. import Job from "./Job";
  7. export enum ModuleStatus {
  8. LOADED = "LOADED",
  9. STARTING = "STARTING",
  10. STARTED = "STARTED",
  11. STOPPED = "STOPPED",
  12. STOPPING = "STOPPING",
  13. ERROR = "ERROR",
  14. DISABLED = "DISABLED"
  15. }
  16. export default abstract class BaseModule {
  17. protected _name: string;
  18. protected _status: ModuleStatus;
  19. protected _dependentModules: string[];
  20. protected _jobs: Record<string, typeof Job>;
  21. /**
  22. * Base Module
  23. *
  24. * @param name - Module name
  25. */
  26. public constructor(name: string) {
  27. this._name = name;
  28. this._status = ModuleStatus.LOADED;
  29. this._dependentModules = [];
  30. this._jobs = {};
  31. this.log(`Module (${this._name}) loaded`);
  32. }
  33. /**
  34. * getName - Get module name
  35. *
  36. * @returns name
  37. */
  38. public getName() {
  39. return this._name;
  40. }
  41. /**
  42. * getStatus - Get module status
  43. *
  44. * @returns status
  45. */
  46. public getStatus() {
  47. return this._status;
  48. }
  49. /**
  50. * setStatus - Set module status
  51. *
  52. * @param status - Module status
  53. */
  54. public setStatus(status: ModuleStatus) {
  55. this._status = status;
  56. }
  57. /**
  58. * getDependentModules - Get module dependencies
  59. */
  60. public getDependentModules() {
  61. return this._dependentModules;
  62. }
  63. /**
  64. * _loadJobs - Load jobs available via api module
  65. */
  66. private async _loadJobs() {
  67. let jobs;
  68. try {
  69. jobs = await readdir(
  70. path.resolve(
  71. __dirname,
  72. `./modules/${this.constructor.name}/jobs`
  73. )
  74. );
  75. } catch (error) {
  76. if (
  77. error instanceof Error &&
  78. "code" in error &&
  79. error.code === "ENOENT"
  80. )
  81. return;
  82. throw error;
  83. }
  84. await forEachIn(jobs, async jobFile => {
  85. const { default: Job } = await import(
  86. `./modules/${this.constructor.name}/jobs/${jobFile}`
  87. );
  88. const jobName = Job.getName();
  89. this._jobs[jobName] = Job;
  90. });
  91. }
  92. /**
  93. * getJob - Get module job
  94. */
  95. public getJob(name: string) {
  96. const [, Job] =
  97. Object.entries(this._jobs).find(([jobName]) => jobName === name) ??
  98. [];
  99. if (!Job) throw new Error(`Job "${name}" not found.`);
  100. return Job;
  101. }
  102. /**
  103. * getJobs - Get module jobs
  104. */
  105. public getJobs() {
  106. return this._jobs;
  107. }
  108. /**
  109. * canRunJobs - Determine if module can run jobs
  110. */
  111. public canRunJobs() {
  112. return this.getDependentModules().reduce(
  113. (canRunJobs: boolean, moduleName: string): boolean => {
  114. if (canRunJobs === false) return false;
  115. return !!ModuleManager.getModule(moduleName)?.canRunJobs();
  116. },
  117. this.getStatus() === ModuleStatus.STARTED
  118. );
  119. }
  120. /**
  121. * startup - Startup module
  122. */
  123. public async startup() {
  124. this.log(`Module (${this._name}) starting`);
  125. this.setStatus(ModuleStatus.STARTING);
  126. }
  127. /**
  128. * started - called with the module has started
  129. */
  130. protected async _started() {
  131. await this._loadJobs();
  132. this.log(`Module (${this._name}) started`);
  133. this.setStatus(ModuleStatus.STARTED);
  134. }
  135. /**
  136. * shutdown - Shutdown module
  137. */
  138. public async shutdown() {
  139. this.log(`Module (${this._name}) stopping`);
  140. this.setStatus(ModuleStatus.STOPPING);
  141. }
  142. /**
  143. * stopped - called when the module has stopped
  144. */
  145. protected async _stopped() {
  146. this.log(`Module (${this._name}) stopped`);
  147. this.setStatus(ModuleStatus.STOPPED);
  148. }
  149. /**
  150. * log - Add log to logbook
  151. *
  152. * @param log - Log message or object
  153. */
  154. public log(log: string | Omit<Log, "timestamp" | "category">) {
  155. const {
  156. message,
  157. type = undefined,
  158. data = {}
  159. } = {
  160. ...(typeof log === "string" ? { message: log } : log)
  161. };
  162. LogBook.log({
  163. message,
  164. type,
  165. category: `modules.${this.getName()}`,
  166. data: {
  167. moduleName: this._name,
  168. ...data
  169. }
  170. });
  171. }
  172. }