admin/plugins: install dependencies automatically
Massimo Melina committed
Oct 7, 2023 at 20:31 UTC
3a0d0f0b39d50f98d67ee57b467cda61ae7dcb1a
4 files changed
+63
-44
admin/src/OnlinePlugins.ts
+28
-11
@@ -3,13 +3,13 @@
3
import { apiCall, useApiList } from './api'
4
import { Fragment, createElement as h, useState } from 'react'
5
import { DataTable } from './DataTable'
6
-import { IconBtn } from './misc'
6
+import { HTTP_FAILED_DEPENDENCY, IconBtn } from './misc'
7
import { Download, Search } from '@mui/icons-material'
8
import { StringField } from '@hfs/mui-grid-form'
9
import { useDebounce } from 'usehooks-ts'
10
import { renderName, startPlugin } from './InstalledPlugins'
11
import { state, useSnapState } from './state'
12
-import { alertDialog, confirmDialog } from './dialog'
12
+import { alertDialog, confirmDialog, toast } from './dialog'
13
14
export default function OnlinePlugins() {
15
const [search, setSearch] = useState('')
@@ -73,23 +73,40 @@ export default function OnlinePlugins() {
73
disabled: row.installed && "Already installed",
74
tooltipProps: { placement:'bottom-end' }, // workaround problem with horizontal scrolling by moving the tooltip leftward
75
confirm: "WARNING - Proceed only if you trust this author and this plugin",
76
- async onClick() {
76
+ onClick() {
77
const branch = row.branch || row.default_branch
78
- try {
79
- const res = await apiCall('download_plugin', { id, branch }, { timeout: false })
80
- if (await confirmDialog(`Plugin ${id} downloaded`, { confirmText: "Start" }))
81
- await startPlugin(res.id)
82
- }
83
- catch(e: any) {
84
- if (e.code !== 424) throw e
78
+ installPlugin(id, branch).catch((e: any) => {
79
+ if (e.code !== HTTP_FAILED_DEPENDENCY)
80
+ return alertDialog(e)
81
const msg = h(Fragment, {}, "This plugin has some dependencies unmet:",
82
e.data.map((x: any) => h('li', { key: x.repo }, x.repo + ': ' + x.error)) )
83
return alertDialog(msg, 'error')
88
- }
84
+ })
85
}
86
})
87
]
88
})
89
)
90
+
91
+ async function installPlugin(id: string, branch?: string): Promise<any> {
92
+ try {
93
+ const res = await apiCall('download_plugin', { id, branch }, { timeout: false })
94
+ if (await confirmDialog(`Plugin ${id} downloaded`, { confirmText: "Start" }))
95
+ await startPlugin(res.id)
96
+ }
97
+ catch(e:any) {
98
+ let done = false
99
+ if (e.code === HTTP_FAILED_DEPENDENCY) // try to install automatically
100
+ for (const x of e.cause)
101
+ if (x.error === 'missing') {
102
+ toast("Installing dependency: " + x.repo)
103
+ await installPlugin(x.repo)
104
+ done = true
105
+ }
106
+ if (done) // try again
107
+ return installPlugin(id, branch)
108
+ throw e
109
+ }
110
+ }
111
}
112
src/const.ts
+1
-33
@@ -5,6 +5,7 @@ import * as fs from 'fs'
5
import { homedir } from 'os'
6
import { mkdirSync } from 'fs'
7
import { basename, dirname, join } from 'path'
8
+export * from './cross-const'
9
10
export const argv = minimist(process.argv.slice(2))
11
export const DEV = process.env.DEV || argv.dev ? 'DEV' : ''
@@ -15,40 +16,7 @@ export const BUILD_TIMESTAMP = fs.statSync(PKG_PATH).mtime.toISOString()
16
const pkg = JSON.parse(fs.readFileSync(PKG_PATH,'utf8'))
17
export const VERSION = pkg.version
18
export const RUNNING_BETA = VERSION.includes('-')
18
-
19
-export const API_VERSION = 8.4
20
-export const COMPATIBLE_API_VERSION = 1 // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
21
-
22
-export const HFS_REPO = 'rejetto/hfs'
19
export const HFS_REPO_BRANCH = RUNNING_BETA ? 'next' : 'main'
24
-
25
-export const SPECIAL_URI = '/~/'
26
-export const FRONTEND_URI = SPECIAL_URI + 'frontend/'
27
-export const ADMIN_URI = SPECIAL_URI + 'admin/'
28
-export const API_URI = SPECIAL_URI + 'api/'
29
-export const PLUGINS_PUB_URI = SPECIAL_URI + 'plugins/'
30
-
31
-export const HTTP_OK = 200
32
-export const HTTP_NO_CONTENT = 204
33
-export const HTTP_PARTIAL_CONTENT = 206
34
-export const HTTP_MOVED_PERMANENTLY = 301
35
-export const HTTP_TEMPORARY_REDIRECT = 302
36
-export const HTTP_NOT_MODIFIED = 304
37
-export const HTTP_BAD_REQUEST = 400
38
-export const HTTP_UNAUTHORIZED = 401
39
-export const HTTP_FORBIDDEN = 403
40
-export const HTTP_NOT_FOUND = 404
41
-export const HTTP_METHOD_NOT_ALLOWED = 405
42
-export const HTTP_NOT_ACCEPTABLE = 406
43
-export const HTTP_CONFLICT = 409
44
-export const HTTP_PRECONDITION_FAILED = 412
45
-export const HTTP_PAYLOAD_TOO_LARGE = 413
46
-export const HTTP_RANGE_NOT_SATISFIABLE = 416
47
-export const HTTP_FOOL = 418
48
-export const HTTP_FAILED_DEPENDENCY = 424
49
-export const HTTP_SERVER_ERROR = 500
50
-export const HTTP_SERVICE_UNAVAILABLE = 503
51
-
20
export const IS_WINDOWS = process.platform === 'win32'
21
export const IS_MAC = process.platform === 'darwin'
22
export const IS_BINARY = !basename(process.execPath).includes('node') // this won't be node if pkg was used
src/cross-const.ts
new
+33
@@ -0,0 +1,33 @@
1
+
2
+export const API_VERSION = 8.4
3
+export const COMPATIBLE_API_VERSION = 1 // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
4
+
5
+export const HFS_REPO = 'rejetto/hfs'
6
+
7
+export const SPECIAL_URI = '/~/'
8
+export const FRONTEND_URI = SPECIAL_URI + 'frontend/'
9
+export const ADMIN_URI = SPECIAL_URI + 'admin/'
10
+export const API_URI = SPECIAL_URI + 'api/'
11
+export const PLUGINS_PUB_URI = SPECIAL_URI + 'plugins/'
12
+
13
+export const HTTP_OK = 200
14
+export const HTTP_NO_CONTENT = 204
15
+export const HTTP_PARTIAL_CONTENT = 206
16
+export const HTTP_MOVED_PERMANENTLY = 301
17
+export const HTTP_TEMPORARY_REDIRECT = 302
18
+export const HTTP_NOT_MODIFIED = 304
19
+export const HTTP_BAD_REQUEST = 400
20
+export const HTTP_UNAUTHORIZED = 401
21
+export const HTTP_FORBIDDEN = 403
22
+export const HTTP_NOT_FOUND = 404
23
+export const HTTP_METHOD_NOT_ALLOWED = 405
24
+export const HTTP_NOT_ACCEPTABLE = 406
25
+export const HTTP_CONFLICT = 409
26
+export const HTTP_PRECONDITION_FAILED = 412
27
+export const HTTP_PAYLOAD_TOO_LARGE = 413
28
+export const HTTP_RANGE_NOT_SATISFIABLE = 416
29
+export const HTTP_FOOL = 418
30
+export const HTTP_FAILED_DEPENDENCY = 424
31
+export const HTTP_SERVER_ERROR = 500
32
+export const HTTP_SERVICE_UNAVAILABLE = 503
33
+
src/cross.ts
+1
@@ -2,6 +2,7 @@
2
// all content here is shared between client and server
3
import _ from 'lodash'
4
import { VfsNode } from './vfs'
5
+export * from './cross-const'
6
7
export const REPO_URL = 'https://github.com/rejetto/hfs/'
8
export const WIKI_URL = REPO_URL + 'wiki/'