hfs 0.53.0-alpha1 → 0.53.0-alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- System.register(["./index-legacy-DBpnLaOV.js"],(function(h,t){"use strict";var i,s;return{setters:[function(h){i=h.g,s=h.c}],execute:function(){function t(h,t){for(var i=function(){var i=t[s];if("string"!=typeof i&&!Array.isArray(i)){var r=function(t){if("default"!==t&&!(t in h)){var s=Object.getOwnPropertyDescriptor(i,t);s&&Object.defineProperty(h,t,s.get?s:{enumerable:!0,get:function(){return i[t]}})}};for(var e in i)r(e)}},s=0;s<t.length;s++)i();return Object.freeze(Object.defineProperty(h,Symbol.toStringTag,{value:"Module"}))}var r={exports:{}};
1
+ System.register(["./index-legacy-C21Ot_ue.js"],(function(h,t){"use strict";var i,s;return{setters:[function(h){i=h.g,s=h.c}],execute:function(){function t(h,t){for(var i=function(){var i=t[s];if("string"!=typeof i&&!Array.isArray(i)){var r=function(t){if("default"!==t&&!(t in h)){var s=Object.getOwnPropertyDescriptor(i,t);s&&Object.defineProperty(h,t,s.get?s:{enumerable:!0,get:function(){return i[t]}})}};for(var e in i)r(e)}},s=0;s<t.length;s++)i();return Object.freeze(Object.defineProperty(h,Symbol.toStringTag,{value:"Module"}))}var r={exports:{}};
2
2
  /*
3
3
  * [js-sha512]{@link https://github.com/emn178/js-sha512}
4
4
  *
@@ -11,6 +11,6 @@
11
11
  <div id="root"></div>
12
12
  <script nomodule>document.getElementById('root').innerText = "Please use a newer browser"</script>
13
13
  <script crossorigin id="vite-legacy-polyfill" src="/assets/polyfills-legacy-DMrMt_pQ.js"></script>
14
- <script crossorigin id="vite-legacy-entry" data-src="/assets/index-legacy-DBpnLaOV.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
14
+ <script crossorigin id="vite-legacy-entry" data-src="/assets/index-legacy-C21Ot_ue.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
15
15
  </body>
16
16
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hfs",
3
- "version": "0.53.0-alpha1",
3
+ "version": "0.53.0-alpha2",
4
4
  "description": "HTTP File Server",
5
5
  "keywords": [
6
6
  "file server",
@@ -1,6 +1,6 @@
1
1
  exports.version = 3
2
2
  exports.description = "Introduce increasing delays between login attempts."
3
- exports.apiRequired = 8.81 // attemptingLogin
3
+ exports.apiRequired = 8.8 // attemptingLogin
4
4
 
5
5
  exports.config = {
6
6
  increment: { type: 'number', min: 1, defaultValue: 5, unit: "seconds", helperText: "How longer user must wait for each login attempt" },
@@ -30,7 +30,6 @@ exports.init = api => {
30
30
  if (rec.attempts > api.getConfig('blockAfter') && !isLocalHost(ctx)) {
31
31
  const hours = api.getConfig('blockForHours')
32
32
  const newRule = { ip, comment: "From antibrute plugin", expire: hours ? new Date(now.getTime() + hours * HOUR) : undefined }
33
- console.log({ hours, newRule })
34
33
  block.set(x => [...x, newRule])
35
34
  }
36
35
  clearTimeout(rec.timer)
@@ -39,7 +38,7 @@ exports.init = api => {
39
38
  ctx.set('x-anti-brute-force', wait)
40
39
  await new Promise(resolve => setTimeout(resolve, wait))
41
40
  }
42
- rec.timer = setTimeout(() => delete byIp[ip], max * 10) // no memory leak
41
+ rec.timer = setTimeout(() => delete byIp[ip], 24 * HOUR) // no memory leak
43
42
  },
44
43
  login: ctx => {
45
44
  if (ctx.state.account)
@@ -10,7 +10,6 @@ const lodash_1 = __importDefault(require("lodash"));
10
10
  const assert_1 = __importDefault(require("assert"));
11
11
  const misc_1 = require("./misc");
12
12
  const apiMiddleware_1 = require("./apiMiddleware");
13
- const events_1 = __importDefault(require("./events"));
14
13
  const promises_1 = require("fs/promises");
15
14
  const github_1 = require("./github");
16
15
  const const_1 = require("./const");
@@ -91,29 +90,33 @@ const apis = {
91
90
  get_online_plugins({ text }, ctx) {
92
91
  return new SendList_1.SendListReadable({
93
92
  async doAtStart(list) {
93
+ const repos = [];
94
+ list.events(ctx, {
95
+ pluginInstalled: p => {
96
+ if (repos.includes(p.repo))
97
+ list.update({ id: p.repo }, { installed: true });
98
+ },
99
+ pluginUninstalled: folder => {
100
+ const repo = (0, github_1.getFolder2repo)()[folder];
101
+ if (typeof repo !== 'string')
102
+ return; // custom repo
103
+ if (repos.includes(repo))
104
+ list.update({ id: repo }, { installed: false });
105
+ },
106
+ pluginDownload({ id, status }) {
107
+ if (repos.includes(id))
108
+ list.update({ id }, { downloading: status !== null && status !== void 0 ? status : null });
109
+ }
110
+ });
94
111
  try {
95
- // avoid creating N listeners on ctx.req, and getting a warning
96
- const undo = [];
97
- ctx.req.once('close', () => undo.forEach(x => x()));
98
- const folder2repo = (0, github_1.getFolder2repo)();
99
- for await (const pl of await (0, github_1.searchPlugins)(text)) {
112
+ const already = Object.values((0, github_1.getFolder2repo)()).map(String);
113
+ for await (const pl of await (0, github_1.searchPlugins)(text, { skipRepos: already })) {
100
114
  const repo = pl.repo || pl.id; // .repo property can be more trustworthy in case github user renamed and left the previous link in 'repo'
101
- if (lodash_1.default.includes(folder2repo, repo))
102
- continue; // don't include installed plugins
103
115
  const missing = await (0, plugins_1.getMissingDependencies)(pl);
104
116
  if (missing.length)
105
117
  pl.missing = missing;
106
118
  list.add(pl);
107
- // watch for events about this plugin, until this request is closed
108
- undo.push(lodash_1.default.flow(events_1.default.on('pluginInstalled', p => {
109
- if (p.repo === repo)
110
- list.update({ id: repo }, { installed: true });
111
- }), events_1.default.on('pluginUninstalled', folder => {
112
- if (repo === (0, github_1.getFolder2repo)()[folder])
113
- list.update({ id: repo }, { installed: false });
114
- }), events_1.default.on('pluginDownload_' + repo, status => {
115
- list.update({ id: repo }, { downloading: status !== null && status !== void 0 ? status : null });
116
- })));
119
+ repos.push(repo);
117
120
  }
118
121
  }
119
122
  catch (err) {
package/src/const.js CHANGED
@@ -45,7 +45,7 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
45
45
  exports.ORIGINAL_CWD = process.cwd();
46
46
  exports.HFS_STARTED = new Date();
47
47
  const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
48
- exports.BUILD_TIMESTAMP = "2024-05-06T22:10:28.456Z";
48
+ exports.BUILD_TIMESTAMP = "2024-05-08T10:54:50.461Z";
49
49
  const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
50
50
  exports.VERSION = pkg.version;
51
51
  exports.RUNNING_BETA = exports.VERSION.includes('-');
package/src/github.js CHANGED
@@ -21,7 +21,7 @@ function downloadProgress(id, status) {
21
21
  delete downloading[id];
22
22
  else
23
23
  downloading[id] = status;
24
- events_1.default.emit('pluginDownload_' + id, status);
24
+ events_1.default.emit('pluginDownload', { id, status });
25
25
  }
26
26
  // determine default branch, possibly without consuming api quota
27
27
  async function getGithubDefaultBranch(repo) {
@@ -161,13 +161,29 @@ async function apiGithub(uri) {
161
161
  : e;
162
162
  });
163
163
  }
164
- async function searchPlugins(text = '') {
164
+ async function* apiGithubPaginated(uri) {
165
+ const PAGE_SIZE = 100;
166
+ let page = 1;
167
+ let n = 0;
168
+ while (1) {
169
+ const res = await apiGithub(uri + `&page=${page++}&per_page=${PAGE_SIZE}`);
170
+ for (const x of res.items)
171
+ yield x;
172
+ const now = res.items.length;
173
+ n += now;
174
+ if (!now || n >= res.total_count)
175
+ break;
176
+ }
177
+ }
178
+ async function searchPlugins(text = '', { skipRepos = [''] } = {}) {
165
179
  const projectInfo = await (0, exports.getProjectInfo)();
166
- const list = await apiGithub('search/repositories?q=topic:hfs-plugin+' + encodeURI(text));
167
- return new misc_1.AsapStream(list.items.map(async (it) => {
180
+ const list = await Promise.all(['', 'user:'].map(specifier => // search text with multiple specifiers
181
+ (0, misc_1.asyncGeneratorToArray)(apiGithubPaginated(`search/repositories?q=topic:hfs-plugin+${specifier}${encodeURI(text)}`))))
182
+ .then(all => all.flat()); // make it a single array
183
+ return new misc_1.AsapStream(list.map(async (it) => {
168
184
  var _a, _b;
169
185
  const repo = it.full_name;
170
- if ((_a = projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.plugins_blacklist) === null || _a === void 0 ? void 0 : _a.includes(repo))
186
+ if (((_a = projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.plugins_blacklist) === null || _a === void 0 ? void 0 : _a.includes(repo)) || skipRepos.includes(repo))
171
187
  return;
172
188
  const pl = await readOnlineCompatiblePlugin(repo, it.default_branch).catch(() => undefined);
173
189
  if (!pl)
package/src/plugins.js CHANGED
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.getMissingDependencies = exports.parsePluginSource = exports.rescan = exports.pluginsConfig = exports.enablePlugins = exports.pluginsWatcher = exports.getAvailablePlugins = exports.mapPlugins = exports.Plugin = exports.pluginsMiddleware = exports.getPluginConfigFields = exports.findPluginByRepo = exports.getPluginInfo = exports.setPluginConfig = exports.startPlugin = exports.stopPlugin = exports.enablePlugin = exports.isPluginEnabled = exports.isPluginRunning = exports.STORAGE_FOLDER = exports.DISABLING_SUFFIX = exports.PATH = void 0;
30
+ exports.getMissingDependencies = exports.parsePluginSource = exports.rescan = exports.pluginsConfig = exports.enablePlugins = exports.pluginsWatcher = exports.getAvailablePlugins = exports.mapPlugins = exports.SERVER_CODE_ID = exports.Plugin = exports.pluginsMiddleware = exports.getPluginConfigFields = exports.findPluginByRepo = exports.getPluginInfo = exports.setPluginConfig = exports.startPlugin = exports.stopPlugin = exports.enablePlugin = exports.isPluginEnabled = exports.isPluginRunning = exports.STORAGE_FOLDER = exports.DISABLING_SUFFIX = exports.PATH = void 0;
31
31
  const fast_glob_1 = __importDefault(require("fast-glob"));
32
32
  const watchLoad_1 = require("./watchLoad");
33
33
  const lodash_1 = __importDefault(require("lodash"));
@@ -232,7 +232,7 @@ class Plugin {
232
232
  const { id } = this;
233
233
  try {
234
234
  await ((_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.unload) === null || _b === void 0 ? void 0 : _b.call(_a));
235
- if (!reloading && id !== SERVER_CODE_ID) // we already printed 'reloading'
235
+ if (!reloading && id !== exports.SERVER_CODE_ID) // we already printed 'reloading'
236
236
  console.log('unloaded plugin', id);
237
237
  }
238
238
  catch (e) {
@@ -244,12 +244,12 @@ class Plugin {
244
244
  }
245
245
  }
246
246
  exports.Plugin = Plugin;
247
- const SERVER_CODE_ID = '.';
247
+ exports.SERVER_CODE_ID = '.'; // a name that will surely be not found among plugin folders
248
248
  const serverCode = (0, config_1.defineConfig)('server_code', '', async (script, { k }) => {
249
249
  const res = {};
250
250
  try {
251
251
  new Function('exports', script)(res); // parse
252
- return new Plugin(SERVER_CODE_ID, '', await initPlugin(res), lodash_1.default.noop); // '.' is a name that will surely be not found among plugin folders
252
+ return new Plugin(exports.SERVER_CODE_ID, '', await initPlugin(res), lodash_1.default.noop);
253
253
  }
254
254
  catch (e) {
255
255
  return console.error(k + ':', e.message || String(e));
@@ -260,7 +260,7 @@ serverCode.sub(() => { var _a; return (_a = serverCode.compiled()) === null || _
260
260
  function mapPlugins(cb, includeServerCode = true) {
261
261
  const entries = Object.entries(plugins);
262
262
  return entries.map(([plName, pl]) => {
263
- if (!includeServerCode && plName === SERVER_CODE_ID)
263
+ if (!includeServerCode && plName === exports.SERVER_CODE_ID)
264
264
  return;
265
265
  try {
266
266
  return cb(pl, plName);
@@ -91,7 +91,7 @@ async function treatIndex(ctx, filesUri, body) {
91
91
  ctx.set('etag', '');
92
92
  ctx.set('Cache-Control', 'no-store, no-cache, must-revalidate');
93
93
  ctx.type = 'html';
94
- const isFrontend = filesUri === const_1.FRONTEND_URI;
94
+ const isFrontend = filesUri === const_1.FRONTEND_URI ? ' ' : ''; // as a string will allow neater code later
95
95
  const pub = ctx.state.revProxyPath + const_1.PLUGINS_PUB_URI;
96
96
  // expose plugins' configs that are declared with 'frontend' attribute
97
97
  const plugins = Object.fromEntries((0, misc_1.onlyTruthy)((0, plugins_1.mapPlugins)((pl, name) => {
@@ -114,7 +114,7 @@ async function treatIndex(ctx, filesUri, body) {
114
114
  const isOpen = !isClose;
115
115
  if (isHead && isOpen)
116
116
  return all + `
117
- ${!isFrontend ? '' : `
117
+ ${isFrontend && `
118
118
  <title>${adminApis_1.title.get()}</title>
119
119
  <link rel="shortcut icon" href="/favicon.ico?${timestamp}" />
120
120
  ` + (0, customHtml_1.getSection)('htmlHead')}
@@ -125,6 +125,7 @@ async function treatIndex(ctx, filesUri, body) {
125
125
  SPECIAL_URI: const_1.SPECIAL_URI, PLUGINS_PUB_URI: const_1.PLUGINS_PUB_URI, FRONTEND_URI: const_1.FRONTEND_URI,
126
126
  session: session instanceof apiMiddleware_1.ApiError ? null : session,
127
127
  plugins,
128
+ loadScripts: Object.fromEntries((0, plugins_1.mapPlugins)((p, id) => { var _a; return [id, (_a = p.frontend_js) === null || _a === void 0 ? void 0 : _a.map(f => f.includes('//') ? f : pub + id + '/' + f)]; })),
128
129
  prefixUrl: ctx.state.revProxyPath,
129
130
  dontOverwriteUploading: upload_1.dontOverwriteUploading.get(),
130
131
  customHtml: lodash_1.default.omit(Object.fromEntries(customHtml_1.customHtmlState.sections), ['top', 'bottom']),
@@ -136,20 +137,16 @@ async function treatIndex(ctx, filesUri, body) {
136
137
  `;
137
138
  if (isBody && isOpen)
138
139
  return all + `
139
- ${!isFrontend ? '' : (0, customHtml_1.getSection)('top')}
140
+ ${isFrontend && (0, customHtml_1.getSection)('top')}
140
141
  <style>
141
142
  :root {
142
- ${lodash_1.default.map(plugins, (configs, pluginName) => lodash_1.default.map(configs, (v, k) => `--${pluginName}-${k}: ${serializeCss(v)};`).join('\n')).join('')}
143
+ ${lodash_1.default.map(plugins, (configs, pluginName) => // make plugin configs accessible via css
144
+ lodash_1.default.map(configs, (v, k) => `--${pluginName}-${k}: ${serializeCss(v)};`).join('\n')).join('')}
143
145
  }
144
146
  </style>
145
- ${!isFrontend ? '' : (0, plugins_1.mapPlugins)((plug, id) => {
147
+ ${isFrontend && (0, plugins_1.mapPlugins)((plug, id) => {
146
148
  var _a;
147
149
  return (_a = plug.frontend_css) === null || _a === void 0 ? void 0 : _a.map(f => `<link rel='stylesheet' type='text/css' href='${f.includes('//') ? f : pub + id + '/' + f}' plugin=${JSON.stringify(id)}/>`);
148
- })
149
- .flat().filter(Boolean).join('\n')}
150
- ${!isFrontend ? '' : (0, plugins_1.mapPlugins)((plug, id) => {
151
- var _a;
152
- return (_a = plug.frontend_js) === null || _a === void 0 ? void 0 : _a.map(f => `<script defer plugin=${JSON.stringify(id)} src='${f.includes('//') ? f : pub + id + '/' + f}'></script>`);
153
150
  })
154
151
  .flat().filter(Boolean).join('\n')}
155
152
  `;