better code: split function
Massimo Melina committed
Jul 2, 2022 at 19:31 UTC
30bff37eeced05105f7f19a69a4e6c4c6016731f
2 files changed
+29
-21
server/src/github.ts
+6
-20
@@ -1,9 +1,8 @@
1
import events from './events'
2
-import { httpsStream, httpsString } from './misc'
2
+import { httpsString, httpsStream, unzip } from './misc'
3
import { getAvailablePlugins, mapPlugins, parsePluginSource, PATH as PLUGINS_PATH, rescan } from './plugins'
4
// @ts-ignore
5
import unzipper from 'unzip-stream'
6
-import { createWriteStream, mkdirSync } from 'fs'
6
import { ApiError } from './apiMiddleware'
7
import _ from 'lodash'
8
@@ -27,8 +26,6 @@ export async function downloadPlugin(repo: string, branch='', overwrite?: boolea
26
const rec = await getRepoInfo(repo)
27
if (!branch)
28
branch = rec.default_branch
30
- const url = `https://github.com/${repo}/archive/refs/heads/${branch}.zip`
31
- const res = await httpsStream(url)
29
const short = repo.split('/')[1] // second part, repo without the owner
30
const folder2repo = getFolder2repo()
31
const folder = overwrite ? _.findKey(folder2repo, x => x===repo) // use existing folder
@@ -37,22 +34,11 @@ export async function downloadPlugin(repo: string, branch='', overwrite?: boolea
34
const installPath = PLUGINS_PATH + '/' + folder
35
const GITHUB_ZIP_ROOT = short + '-' + branch // GitHub puts everything within this folder
36
const rootWithinZip = GITHUB_ZIP_ROOT + '/' + DIST_ROOT
40
- return new Promise(resolve =>
41
- res.pipe(unzipper.Parse())
42
- .on('entry', (entry: any) => {
43
- const { path, type } = entry
44
- if (!path.startsWith(rootWithinZip))
45
- return entry.autodrain()
46
- const dest = installPath + '/' + path.slice(rootWithinZip.length)
47
- if (type === 'File')
48
- return entry.pipe(createWriteStream(dest))
49
- mkdirSync(dest, { recursive: true }) // easy way be sure to have the folder ready before proceeding
50
- })
51
- .on('close', () => {
52
- rescan() // workaround: for some reason, operations above are not triggering the rescan of the watched folder. Let's invoke it.
53
- resolve(undefined)
54
- downloadProgress(repo, undefined)
55
- }))
37
+ const stream = await httpsStream(`https://github.com/${repo}/archive/refs/heads/${branch}.zip`)
38
+ await unzip(stream, path =>
39
+ path.startsWith(rootWithinZip) && installPath + '/' + path.slice(rootWithinZip.length) )
40
+ downloadProgress(repo, undefined)
41
+ await rescan() // workaround: for some reason, operations are not triggering the rescan of the watched folder. Let's invoke it.
42
}
43
44
export function getRepoInfo(id: string) {
server/src/util-files.ts
+23
-1
@@ -1,10 +1,13 @@
1
import fs from 'fs/promises'
2
import { wait } from './misc'
3
-import { watch } from 'fs'
3
+import { createWriteStream, mkdirSync, watch } from 'fs'
4
import { basename, dirname } from 'path'
5
import glob from 'fast-glob'
6
import { IS_WINDOWS } from './const'
7
import { execFile } from 'child_process'
8
+import { once, Readable } from 'stream'
9
+// @ts-ignore
10
+import unzipper from 'unzip-stream'
11
12
export async function isDirectory(path: string) {
13
try { return (await fs.stat(path)).isDirectory() }
@@ -93,3 +96,22 @@ export function run(cmd: string, args: string[] = []): Promise<string> {
96
}))
97
}
98
99
+export async function unzip(stream: Readable, cb: (path: string) => false | string) {
100
+ let pending: Promise<any> = Promise.resolve()
101
+ return new Promise(resolve =>
102
+ stream.pipe(unzipper.Parse())
103
+ .on('end', () => pending.then(resolve))
104
+ .on('entry', async (entry: any) => {
105
+ const { path, type } = entry
106
+ const dest = cb(path)
107
+ if (!dest || type !== 'File')
108
+ return entry.autodrain()
109
+ await pending // don't overlap writings
110
+ console.debug('unzip', dest)
111
+ mkdirSync(dirname(dest), { recursive: true }) // easy way be sure to have the folder ready before proceeding
112
+ const thisFile = entry.pipe(createWriteStream(dest))
113
+ pending = once(thisFile, 'finish')
114
+ })
115
+ )
116
+}
117
+