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 +