fix: admin/plugins/updates: "bad request" sometimes, resulting in plugin being lost
Massimo Melina committed
Apr 9, 2025 at 22:54 UTC
7a521e216b160c8d1749439f3e44f0c10d17ecce
2 files changed
+13
-6
src/github.ts
+5
-5
@@ -6,7 +6,7 @@ import {
6
} from './misc'
7
import {
8
DISABLING_SUFFIX, enablePlugin, findPluginByRepo, getAvailablePlugins, getPluginInfo, isPluginRunning, mapPlugins,
9
- parsePluginSource, PATH as PLUGINS_PATH, Repo, startPlugin, stopPlugin, STORAGE_FOLDER
9
+ parsePluginSource, PATH as PLUGINS_PATH, Repo, startPlugin, stopPlugin, STORAGE_FOLDER, DELETE_ME_SUFFIX
10
} from './plugins'
11
import { ApiError } from './apiMiddleware'
12
import _ from 'lodash'
@@ -105,10 +105,10 @@ export async function downloadPlugin(repo: Repo, { branch='', overwrite=false }=
105
if (await res.then(() => true, e => e.code === 'ENOENT')) break
106
await wait(1000)
107
}
108
- // delete old
109
- await rm(installPath, { recursive: true }).catch(e => {
110
- if (e.code !== 'ENOENT') throw e
111
- })
108
+ // delete old folder, but it may fail in the presence of .node files, so we rename it first as a precaution (clearing require.cache doesn't help)
109
+ const deleteMe = installPath + DELETE_ME_SUFFIX
110
+ await rename(installPath, deleteMe)
111
+ await rm(deleteMe, { recursive: true, force: true }).catch(e => console.warn(String(e)))
112
// final replace
113
await rename(tempInstallPath, installPath)
114
.catch(e => { throw e.code !== 'ENOENT' ? e : new ApiError(HTTP_NOT_ACCEPTABLE, "missing main file") })
src/plugins.ts
+8
-1
@@ -18,7 +18,7 @@ import { DirEntry } from './api.get_file_list'
18
import { VfsNode } from './vfs'
19
import { serveFile } from './serveFile'
20
import events from './events'
21
-import { mkdir, readFile, rename } from 'fs/promises'
21
+import { mkdir, readdir, readFile, rename, rm } from 'fs/promises'
22
import { existsSync, mkdirSync } from 'fs'
23
import { getConnections } from './connections'
24
import { dirname, join, resolve } from 'path'
@@ -36,8 +36,15 @@ import { CustomizedIcons, watchIconsFolder } from './icons'
36
37
export const PATH = 'plugins'
38
export const DISABLING_SUFFIX = '-disabled'
39
+export const DELETE_ME_SUFFIX = '-delete_me' + DISABLING_SUFFIX
40
export const STORAGE_FOLDER = 'storage'
41
42
+setTimeout(async () => { // delete leftovers, if any
43
+ for (const x of await readdir(PATH))
44
+ if (x.endsWith(DELETE_ME_SUFFIX))
45
+ await rm(join(PATH, x), { recursive: true, force: true }).catch(() => {})
46
+}, 1000)
47
+
48
const plugins = new Map<string, Plugin>() // now that we care about the order, a simple object wouldn't do, because numbers are always at the beginning
49
50
export function isPluginRunning(id: string) {