admin/plugins: much faster search online
Massimo Melina committed
Sep 10, 2023 at 21:02 UTC
66152188a2afe1adbf166a2c804af9fb2c13288f
3 files changed
+26
-11
src/api.plugins.ts
+1
-1
@@ -111,7 +111,7 @@ const apis: ApiHandlers = {
111
ctx.req.once('close', () => undo.forEach(x => x()))
112
113
const folder2repo = getFolder2repo()
114
- for await (const pl of searchPlugins(text)) {
114
+ for await (const pl of await searchPlugins(text)) {
115
const repo = pl.repo || pl.id // .repo property can be more trustworthy in case github user renamed and left the previous link in 'repo'
116
if (_.includes(folder2repo, repo)) continue // don't include installed plugins
117
list.add(pl)
src/github.ts
+10
-10
@@ -1,7 +1,7 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
import events from './events'
4
-import { httpString, httpStream, unzip } from './misc'
4
+import { httpString, httpStream, unzip, AsapStream } from './misc'
5
import {
6
DISABLING_POSTFIX, findPluginByRepo,
7
getAvailablePlugins,
@@ -152,16 +152,16 @@ async function apiGithub(uri: string) {
152
}
153
}
154
155
-export async function* searchPlugins(text='') {
155
+export async function searchPlugins(text='') {
156
const projectInfo = await getProjectInfo()
157
- const res = await apiGithub('search/repositories?q=topic:hfs-plugin+' + encodeURI(text))
158
- for (const it of res.items) {
157
+ const list = await apiGithub('search/repositories?q=topic:hfs-plugin+' + encodeURI(text))
158
+ return new AsapStream(list.items.map(async (it: any) => {
159
const repo = it.full_name as string
160
- if (projectInfo?.plugins_blacklist?.includes(repo)) continue
160
+ if (projectInfo?.plugins_blacklist?.includes(repo)) return
161
let pl = await readOnlinePlugin(repo, it.default_branch)
162
- if (!pl?.apiRequired) continue // mandatory field
162
+ if (!pl?.apiRequired) return // mandatory field
163
if (pl.badApi) { // we try other branches (starting with 'api')
164
- const res = await apiGithub('repos/' + it.full_name + '/branches')
164
+ const res = await apiGithub('repos/' + repo + '/branches')
165
const branches: string[] = res.map((x: any) => x?.name)
166
.filter((x: any) => typeof x === 'string' && x.startsWith('api'))
167
.sort().reverse()
@@ -175,13 +175,13 @@ export async function* searchPlugins(text='') {
175
}
176
}
177
if (!pl || pl.badApi)
178
- continue
178
+ return
179
Object.assign(pl, { // inject some extra useful fields
180
downloading: downloading[repo],
181
license: it.license?.spdx_id,
182
}, _.pick(it, ['pushed_at', 'stargazers_count', 'default_branch']))
183
- yield pl
184
- }
183
+ return pl
184
+ }))
185
}
186
187
// centralized hosted information, to be used as little as possible
src/misc.ts
+15
@@ -134,3 +134,18 @@ export function asyncGeneratorToReadable<T>(generator: AsyncIterable<T>) {
134
}
135
})
136
}
137
+
138
+// produces as soon as a promise resolves, not sequentially
139
+export class AsapStream<T> extends Readable {
140
+ finished = false
141
+ constructor(private promises: Promise<T>[]) {
142
+ super({ objectMode: true })
143
+ }
144
+ _read() {
145
+ if (this.finished) return
146
+ this.finished = true
147
+ for (const p of this.promises)
148
+ p.then(data => this.push(data), e => this.emit('error', e))
149
+ Promise.allSettled(this.promises).then(() => this.push(null))
150
+ }
151
+}