@samitouri / QOSami-HFS / commits / 66152188

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 +}