Browse Source

add PluginLoader

master
peter 6 years ago
parent
commit
fd324300ea
2 changed files with 165 additions and 11 deletions
  1. 1
    1
      src/backend/Admin.ts
  2. 164
    10
      src/backend/PluginLoader.ts

+ 1
- 1
src/backend/Admin.ts View File

@@ -134,7 +134,7 @@ implements TableDefinitionExporter {
134 134
         logger.info("Webserver stopped")
135 135
     }
136 136
 
137
-    protected async makeKnex():Promise<Knex>{
137
+    public async makeKnex():Promise<Knex>{
138 138
         const conf:Knex.Config = this.config.getConfigKey("dbConf")
139 139
 
140 140
         logger.debug("Making new knex:", conf)        

+ 164
- 10
src/backend/PluginLoader.ts View File

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

Loading…
Cancel
Save