You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PluginLoader.ts 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import { RPCExporter } from "rpclibrary/js/src/Interfaces";
  2. import { Git } from "upgiter"
  3. import { FolderStatus } from "upgiter/js/src/Types";
  4. import { Plugin } from "./Plugin";
  5. import { FrontworkAdmin } from "./Admin";
  6. class PluginLoader {
  7. private runningPlugins: Plugin[]
  8. private pluginUpdaters:{[name in string]:Git.Updater} = {}
  9. constructor(private admin:FrontworkAdmin){}
  10. async selfUpdate(force:boolean = false){
  11. const updater = new Git.Updater({
  12. schema: 'https',
  13. localPath: './dist',
  14. remoteHost: 'www.versioncontrol.me',
  15. remotePath: 'frontwork-distribution',
  16. repoName: 'admin'
  17. })
  18. let status = await updater.getStatus()
  19. if(force || !status.remote || !status.remote.includes("fb-dist/admin") || !status.exists || status.empty || !status.currentTag){
  20. //logger.warn("Cloning fb-dist/admin into ./dist ..."+(force?" USING FORCE!":""))
  21. status = await updater.cloneRepo(force)
  22. this.destroy()
  23. const installer = eval("require")("./Installer").installAdmin
  24. installer(this.getPlugins())
  25. }
  26. return status
  27. }
  28. async installPlugin(name: string, force:boolean = false):Promise<FolderStatus>{
  29. //logger.warn("Cloning fb-dist/"+name+".git into ./plugins/"+name+" ..."+(force?" USING FORCE!":""))
  30. this.pluginUpdaters[name] = new Git.Updater({
  31. schema: 'https',
  32. localPath: './dist',
  33. remoteHost: 'www.versioncontrol.me',
  34. remotePath: 'frontwork-distribution',
  35. repoName: name
  36. })
  37. const status = await this.pluginUpdaters[name].cloneRepo(force)
  38. return status
  39. }
  40. async startPlugin(name:string):Promise<boolean>{
  41. if(!this.pluginUpdaters[name]) return false
  42. const status = await this.pluginUpdaters[name].getStatus()
  43. if(!status.exists || status.empty || !status.tags || status.tags.length === 0){
  44. if(status.currentTag && !status.latestTag){
  45. //git glitches sometimes if you check immediately after clone
  46. //logger.warn("re-fetching tag for "+name+"...")
  47. return await this.startPlugin(name)
  48. }
  49. //logger.error("Bad repo status", name, status)
  50. return false
  51. }
  52. /* if(this.loadedPlugins[name]){
  53. logger.error("Plugin", name, "is already started")
  54. return false
  55. }
  56. */
  57. let evalstr = "../plugins/"+name+"/Plugin"
  58. const pluginClass = await eval('require')(evalstr)
  59. const pluginObj = new pluginClass.default(this)
  60. try{
  61. if(pluginObj.start)
  62. await pluginObj.start()
  63. //this.pushNotification({message: "Started plugin "+pluginObj.name, severity: "Important", topic:"admin"})
  64. this.addPlugin(pluginObj)
  65. return true
  66. }catch(e){
  67. //logger.error(e)
  68. //this.pushNotification({message: "Start of plugin "+pluginObj.name+" failed because of "+String(e), severity: "Error", topic:"admin"})
  69. return false
  70. }
  71. }
  72. async updatePlugin(name:string):Promise<boolean>{
  73. if(!this.pluginUpdaters[name]) return false
  74. const status = await this.pluginUpdaters[name].getStatus()
  75. if(!status.exists || status.empty || !status.tags || status.tags.length === 0){
  76. //logger.error("Bad repo status", name, status)
  77. return false
  78. }
  79. if(status.currentTag == status.latestTag){
  80. //logger.warn(name, "already at latest tag")
  81. return false
  82. }
  83. /*
  84. if(this.loadedPlugins[name]){
  85. logger.error("Plugin", name, "is running. Stop it first")
  86. return false
  87. }
  88. */
  89. this.pluginUpdaters[name].checkoutTag(status.latestTag!)
  90. return true
  91. }
  92. async setPluginVersion(pluginName:string, tag:string):Promise<FolderStatus>{
  93. const status = await this.pluginUpdaters[pluginName].getStatus()
  94. if(!status.exists || !status.tags || status.tags.length === 0 || !status.tags.includes(tag)){
  95. //logger.error("Bad repo status", pluginName, status)
  96. return status
  97. }
  98. return await this.pluginUpdaters[pluginName].checkoutTag(tag)
  99. }
  100. async addPlugin(plugin: Plugin){
  101. this.runningPlugins.push(plugin)
  102. if(plugin.getTableDefinitions().length != 0)
  103. await this.admin.makeKnex()
  104. }
  105. removePlugin(plugin: Plugin){
  106. this.runningPlugins = this.runningPlugins.filter(p => p.name !== plugin.name)
  107. }
  108. getPlugins():Plugin[]{
  109. return [...this.runningPlugins]
  110. }
  111. async destroy(){
  112. return
  113. }
  114. }
  115. export type PluginLoaderIfc = {
  116. PluginLoader: {
  117. installPlugin: (name:string, force:boolean) => Promise<FolderStatus>
  118. startPlugin: (name:string) => Promise<boolean>
  119. updatePlugin: (name:string) => Promise<boolean>
  120. setPluginVersion: (name:string, tag:string) => Promise<FolderStatus>
  121. getLoadedPluginNames: () => Promise<String[]>
  122. selfUpdate: (force:boolean) => Promise<FolderStatus>
  123. destroy: () => Promise<void>
  124. }
  125. }
  126. class RPCPluginLoader
  127. extends PluginLoader
  128. implements RPCExporter<PluginLoaderIfc, "PluginLoader">{
  129. name = "PluginLoader" as "PluginLoader";
  130. exportRPCs(){
  131. return [{
  132. name: "installPlugin" as "installPlugin",
  133. call: async (name:string, force = false) => {return await this.installPlugin(name, force)},
  134. },{
  135. name: "startPlugin" as "startPlugin",
  136. call: async (name:string) => {return await this.startPlugin(name)},
  137. },{
  138. name: "updatePlugin" as "updatePlugin",
  139. call: async (name:string) => {return await this.updatePlugin(name)},
  140. },{
  141. name: "setPluginVersion" as "setPluginVersion",
  142. call: async (name:string, tag:string) => {return await this.setPluginVersion(name, tag)},
  143. },{
  144. name: "getLoadedPluginNames" as "getLoadedPluginNames",
  145. call: async () => {return this.getPlugins().map(p => p.name)},
  146. },{
  147. name: "selfUpdate" as "selfUpdate",
  148. call: async (force: boolean) => {return await this.selfUpdate(force)},
  149. },{
  150. name: "destroy" as "destroy",
  151. call: async () => {return this.destroy()}
  152. }]
  153. }
  154. }