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.

UpdateManger.ts 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { Plugin } from "frontblock-generic/Plugin";
  2. import { socketioRPC } from "frontblock-generic/RPC";
  3. import { GitUpdater, RepoFolderStatus } from "./GitUpdater";
  4. import { FrontblockCherryPicker } from "git-cherrypicker";
  5. import * as Logger from 'log4js'
  6. import { FrontblockAdmin } from "./FrontblockAdmin";
  7. import { NPMExtension } from "./Extension";
  8. import { TableDefiniton } from "frontblock-generic/Admin";
  9. var exec = require('child-process-promise').exec;
  10. Logger.configure({
  11. appenders:
  12. {
  13. "admin/updatemanager": { type: 'stdout' },
  14. //app: { type: 'file', filename: 'application.log' }
  15. },
  16. categories:
  17. {
  18. default: { appenders: [ 'admin/updatemanager' ], level: 'debug' }
  19. }
  20. })
  21. const logger = Logger.getLogger("admin/updatemanager")
  22. export class UpdateManager extends Plugin{
  23. private loadedPlugins:{[name in string]:Plugin} = {}
  24. private dashboardUpdater:GitUpdater
  25. private adminUpdater:GitUpdater
  26. private pluginUpdaters:{[name in string]:GitUpdater} = {}
  27. constructor(admin:FrontblockAdmin){
  28. super(admin, "UpdateManager")
  29. }
  30. getDefaultConfig(): {} {
  31. return {}
  32. }
  33. exportRPCs(): socketioRPC[] {
  34. return [{
  35. name: 'installPlugin',
  36. func: async (name:string, force = false) => { },
  37. type: 'call',
  38. visibility: 'private'
  39. },{
  40. name: 'startPlugin',
  41. func: async (name:string) => {return await this.startPlugin(name)},
  42. type: 'call',
  43. visibility: 'private'
  44. },{
  45. name: 'updatePlugin',
  46. func: async (name) => {return await this.updatePlugin(name)},
  47. type: 'call',
  48. visibility: 'private'
  49. },{
  50. name: 'setPluginVersion',
  51. func: async (name, tag) => {return await this.setPluginVersion(name, tag)},
  52. type: 'call',
  53. visibility: 'private'
  54. },{
  55. name: 'updateDashboard',
  56. func: async () => {return await this.updateDashboard()},
  57. type: 'call',
  58. visibility: 'private'
  59. },{
  60. name: 'getLoadedPluginNames',
  61. func: async () => {return await this.getLoadedPluginNames()},
  62. type: 'call',
  63. visibility: 'private'
  64. }]
  65. }
  66. getTableDefinitions(): TableDefiniton[] {
  67. return []
  68. }
  69. async updatePlatformDependencies(){
  70. logger.info("installing npm stuff")
  71. /*
  72. const res = await exec('npm install --prefix ./plugins knex sqlite3')
  73. logger.warn(res.stderr)
  74. logger.info(res.stdout)
  75. */
  76. const extensions = [
  77. new NPMExtension("knex", "./plugins", "0.19.2",),
  78. new NPMExtension("sqlite3", "./plugins", "4.1.0")
  79. ]
  80. await Promise.all(extensions.map(e => e.install()))
  81. const status = await Promise.all(extensions.map(e => e.status()))
  82. console.log(status)
  83. }
  84. async updateAdmin(force:boolean = false){
  85. this.adminUpdater = new GitUpdater("./dist")
  86. let status = await this.adminUpdater.getStatus()
  87. if(force || !status.remote || !status.remote.includes("fb-dist/admin") || !status.exists || status.empty || !status.currentTag){
  88. logger.warn("Cloning fb-dist/admin into ./dist ..."+(force?" USING FORCE!":""))
  89. status = await this.adminUpdater.cloneRepo("https://gitea.frontblock.me/fb-dist/admin.git", force)
  90. }
  91. return status
  92. }
  93. async updateFrontblockLib(){
  94. logger.warn("Picking FrontblockLib...")
  95. await (new FrontblockCherryPicker("fb-dist/admin", "master", "FrontblockLib.js")).write("./static/FrontblockLib.js")
  96. }
  97. async updateDashboard(force:boolean = false):Promise<RepoFolderStatus>{
  98. this.dashboardUpdater = new GitUpdater("./static")
  99. let status = await this.dashboardUpdater.getStatus()
  100. if(force || !status.exists || status.empty || !status.currentTag){
  101. logger.warn("Cloning fb-dist/dashboard into ./static ..."+(force?" USING FORCE!":""))
  102. status = await this.dashboardUpdater.cloneRepo("https://gitea.frontblock.me/fb-dist/dashboard.git", force)
  103. }
  104. return status
  105. }
  106. async startPlugin(name:string):Promise<boolean>{
  107. if(!this.pluginUpdaters[name]) return false
  108. const status = await this.pluginUpdaters[name].getStatus()
  109. if(!status.exists || status.empty || !status.tags || status.tags.length === 0){
  110. if(status.currentTag && !status.latestTag){
  111. //git glitches sometimes if you check immediately after clone
  112. logger.warn("re-fetching tag for "+name+"...")
  113. return await this.startPlugin(name)
  114. }
  115. logger.error("Bad repo status", name, status)
  116. return false
  117. }
  118. if(this.loadedPlugins[name]){
  119. logger.error("Plugin", name, "is already started")
  120. return false
  121. }
  122. let str = "../plugins/"+name+"/Plugin"
  123. const pluginClass = await eval('require')(str)
  124. const pluginObj = new pluginClass.default()
  125. await pluginObj.start()
  126. this.admin.addPlugin(pluginObj)
  127. this.loadedPlugins[name] = pluginObj
  128. return true
  129. }
  130. async updatePlugin(name:string):Promise<boolean>{
  131. if(!this.pluginUpdaters[name]) return false
  132. const status = await this.pluginUpdaters[name].getStatus()
  133. if(!status.exists || status.empty || !status.tags || status.tags.length === 0){
  134. logger.error("Bad repo status", name, status)
  135. return false
  136. }
  137. if(status.currentTag == status.latestTag){
  138. logger.warn(name, "already at latest tag")
  139. return false
  140. }
  141. if(this.loadedPlugins[name]){
  142. logger.error("Plugin", name, "is running. Stop it first")
  143. return false
  144. }
  145. this.pluginUpdaters[name].checkoutTag(status.latestTag!)
  146. return true
  147. }
  148. async setPluginVersion(pluginName:string, tag:string):Promise<RepoFolderStatus>{
  149. const status = await this.pluginUpdaters[pluginName].getStatus()
  150. if(!status.exists || !status.tags || status.tags.length === 0 || !status.tags.includes(tag)){
  151. logger.error("Bad repo status", pluginName, status)
  152. return status
  153. }
  154. return await this.pluginUpdaters[pluginName].checkoutTag(tag)
  155. }
  156. getLoadedPlugins():Plugin<any>[]{
  157. return Object.values(this.loadedPlugins)
  158. }
  159. getLoadedPluginNames():string[]{
  160. return Object.keys(this.loadedPlugins)
  161. }
  162. }