hfs 0.57.26 → 0.57.27-beta2

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.
Files changed (71) hide show
  1. package/admin/assets/{index-CHMhmNYS.js → index-DSkhXlWX.js} +103 -103
  2. package/admin/assets/{sha512-Dsv49IAs.js → sha512-CoddZKeD.js} +1 -1
  3. package/admin/index.html +1 -1
  4. package/frontend/assets/{index-legacy-BI8icwFp.js → index-legacy-C4Gclsbz.js} +4 -4
  5. package/frontend/assets/{sha512-legacy-D82alHBq.js → sha512-legacy-C-FMk44u.js} +1 -1
  6. package/frontend/index.html +1 -1
  7. package/package.json +4 -3
  8. package/src/QuickZipStream.js +11 -8
  9. package/src/SendList.js +10 -8
  10. package/src/ThrottledStream.js +8 -6
  11. package/src/acme.js +5 -7
  12. package/src/adminApis.js +6 -7
  13. package/src/api.accounts.js +4 -6
  14. package/src/api.auth.js +7 -11
  15. package/src/api.get_file_list.js +11 -16
  16. package/src/api.lang.js +1 -1
  17. package/src/api.monitor.js +2 -3
  18. package/src/api.net.js +1 -2
  19. package/src/api.plugins.js +8 -10
  20. package/src/api.vfs.js +14 -19
  21. package/src/apiMiddleware.js +4 -4
  22. package/src/auth.js +6 -9
  23. package/src/basicWeb.js +1 -2
  24. package/src/block.js +1 -1
  25. package/src/commands.js +9 -9
  26. package/src/comments.js +4 -5
  27. package/src/config.js +8 -9
  28. package/src/connections.js +11 -6
  29. package/src/consoleLog.js +6 -6
  30. package/src/const.js +4 -4
  31. package/src/cross.js +19 -21
  32. package/src/customHtml.js +3 -4
  33. package/src/ddns.js +4 -4
  34. package/src/debounceAsync.js +3 -3
  35. package/src/errorPages.js +2 -3
  36. package/src/events.js +7 -10
  37. package/src/fileAttr.js +6 -6
  38. package/src/first.js +13 -8
  39. package/src/frontEndApis.js +5 -5
  40. package/src/geo.js +3 -5
  41. package/src/github.js +14 -17
  42. package/src/i18n.js +3 -4
  43. package/src/icons.js +1 -1
  44. package/src/index.js +2 -3
  45. package/src/lang.js +5 -6
  46. package/src/listen.js +25 -28
  47. package/src/log.js +18 -17
  48. package/src/middlewares.js +12 -15
  49. package/src/misc.js +9 -11
  50. package/src/nat.js +14 -16
  51. package/src/outboundProxy.js +7 -7
  52. package/src/perm.js +7 -12
  53. package/src/persistence.js +1 -1
  54. package/src/plugins.js +62 -73
  55. package/src/roots.js +1 -2
  56. package/src/selfCheck.js +5 -4
  57. package/src/serveFile.js +6 -7
  58. package/src/serveGuiAndSharedFiles.js +7 -9
  59. package/src/serveGuiFiles.js +11 -20
  60. package/src/stat.js +40 -0
  61. package/src/statWorker.js +11 -0
  62. package/src/throttler.js +5 -7
  63. package/src/update.js +7 -7
  64. package/src/upload.js +11 -13
  65. package/src/util-files.js +42 -32
  66. package/src/util-http.js +80 -29
  67. package/src/util-os.js +2 -4
  68. package/src/vfs.js +77 -68
  69. package/src/walkDir.js +9 -9
  70. package/src/watchLoad.js +4 -4
  71. package/src/zip.js +5 -6
package/src/listen.js CHANGED
@@ -70,17 +70,19 @@ const consoleLog_1 = require("./consoleLog");
70
70
  let httpSrv;
71
71
  let httpsSrv;
72
72
  const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', true);
73
- exports.baseUrl = (0, config_1.defineConfig)(misc_1.CFG.base_url, '', x => { var _a; return (_a = /(?<=\/\/)[^\/]+/.exec(x)) === null || _a === void 0 ? void 0 : _a[0]; }); // compiled is host only
73
+ exports.baseUrl = (0, config_1.defineConfig)(misc_1.CFG.base_url, '', x => /(?<=\/\/)[^\/]+/.exec(x)?.[0]); // compiled is host only
74
74
  async function getBaseUrlOrDefault() {
75
75
  return exports.baseUrl.get() || await nat_1.defaultBaseUrl.get();
76
76
  }
77
77
  function getHttpsWorkingPort() {
78
- var _a;
79
- return (httpsSrv === null || httpsSrv === void 0 ? void 0 : httpsSrv.listening) && ((_a = httpsSrv.address()) === null || _a === void 0 ? void 0 : _a.port);
78
+ return httpsSrv?.listening && httpsSrv.address()?.port;
80
79
  }
81
- const commonServerOptions = { requestTimeout: 0 };
80
+ const commonServerOptions = {
81
+ requestTimeout: 0, // disable timeout on long uploads
82
+ maxHeaderSize: 32768, // allow a larger header for a larger list in ?get=zip
83
+ };
82
84
  // these are properties that can be assigned to the server object
83
- const commonServerAssign = { headersTimeout: 30000, timeout: misc_1.MINUTE }; // 'headersTimeout' is not recognized by type lib, and 'timeout' is not effective when passed in parameters
85
+ const commonServerAssign = { headersTimeout: 30_000, timeout: misc_1.MINUTE }; // 'headersTimeout' is not recognized by type lib, and 'timeout' is not effective when passed in parameters
84
86
  const readyToListen = Promise.all([persistence_1.storedMap.isOpening(), events_1.default.once('app')]);
85
87
  const considerHttp = (0, misc_1.debounceAsync)(async () => {
86
88
  await readyToListen;
@@ -105,7 +107,7 @@ const listenInterface = (0, config_1.defineConfig)('listen_interface', '');
105
107
  (0, config_1.subMultipleConfigs)(considerHttp, [exports.portCfg, listenInterface]);
106
108
  function openAdmin() {
107
109
  for (const srv of [httpSrv, httpsSrv]) {
108
- const a = srv === null || srv === void 0 ? void 0 : srv.address();
110
+ const a = srv?.address();
109
111
  if (!a || typeof a === 'string')
110
112
  continue;
111
113
  const i = listenInterface.get();
@@ -123,21 +125,19 @@ function openAdmin() {
123
125
  console.log("openAdmin failed");
124
126
  }
125
127
  function getCertObject() {
126
- var _a;
127
128
  const c = exports.cert.compiled();
128
129
  if (!c)
129
130
  return;
130
131
  const all = new crypto_1.X509Certificate(c);
131
132
  const some = lodash_1.default.pick(all, ['subject', 'issuer', 'validFrom', 'validTo']);
132
- const ret = (0, misc_1.objSameKeys)(some, v => (v === null || v === void 0 ? void 0 : v.includes('=')) ? Object.fromEntries(v.split('\n').map(x => x.split('='))) : v);
133
- return Object.assign(ret, { altNames: (_a = all.subjectAltName) === null || _a === void 0 ? void 0 : _a.replace(/DNS:/g, '').split(/, */) });
133
+ const ret = (0, misc_1.objSameKeys)(some, v => v?.includes('=') ? Object.fromEntries(v.split('\n').map(x => x.split('='))) : v);
134
+ return Object.assign(ret, { altNames: all.subjectAltName?.replace(/DNS:/g, '').split(/, */) });
134
135
  }
135
136
  const considerHttps = (0, misc_1.debounceAsync)(async () => {
136
- var _a, _b, _c, _d;
137
137
  await readyToListen;
138
138
  void stopServer(httpsSrv);
139
139
  nat_1.defaultBaseUrl.proto = 'http';
140
- nat_1.defaultBaseUrl.port = (_a = getCurrentPort(httpSrv)) !== null && _a !== void 0 ? _a : 0;
140
+ nat_1.defaultBaseUrl.port = getCurrentPort(httpSrv) ?? 0;
141
141
  let port = exports.httpsPortCfg.get();
142
142
  try {
143
143
  const moreOptions = Object.assign({}, ...await events_1.default.emitAsync('httpsServerOptions') || []); // emitAsync returns an array of objects
@@ -150,9 +150,9 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
150
150
  if (port >= 0) {
151
151
  const certObj = getCertObject();
152
152
  if (certObj) {
153
- const cn = (_b = certObj.subject) === null || _b === void 0 ? void 0 : _b.CN;
153
+ const cn = certObj.subject?.CN;
154
154
  if (cn)
155
- console.log("certificate loaded for", ((_c = certObj.altNames) === null || _c === void 0 ? void 0 : _c.join(' + ')) || cn);
155
+ console.log("certificate loaded for", certObj.altNames?.join(' + ') || cn);
156
156
  const now = new Date();
157
157
  const from = new Date(certObj.validFrom);
158
158
  const to = new Date(certObj.validTo);
@@ -178,7 +178,7 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
178
178
  }
179
179
  }
180
180
  catch (e) {
181
- httpsSrv || (httpsSrv = Object.assign(https.createServer({}), { name: 'https' })); // a dummy container, in case creation failed because of certificate errors
181
+ httpsSrv ||= Object.assign(https.createServer({}), { name: 'https' }); // a dummy container, in case creation failed because of certificate errors
182
182
  httpsSrv.error = "bad private key or certificate";
183
183
  console.error("failed to create https server: check your private key and certificate", e.message);
184
184
  return;
@@ -196,14 +196,13 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
196
196
  printUrls(httpsSrv.name);
197
197
  events_1.default.emit('httpsReady');
198
198
  nat_1.defaultBaseUrl.proto = 'https';
199
- nat_1.defaultBaseUrl.port = (_d = getCurrentPort(httpsSrv)) !== null && _d !== void 0 ? _d : 0;
199
+ nat_1.defaultBaseUrl.port = getCurrentPort(httpsSrv) ?? 0;
200
200
  }, { wait: 200 }); // give time to have key and cert ready
201
201
  exports.cert = (0, config_1.defineConfig)('cert', '', load);
202
202
  exports.privateKey = (0, config_1.defineConfig)('private_key', '', load);
203
203
  const httpsNeeds = [exports.cert, exports.privateKey];
204
204
  function load(v, { object }) {
205
- var _a;
206
- (_a = object.watcher) === null || _a === void 0 ? void 0 : _a.unwatch();
205
+ object.watcher?.unwatch();
207
206
  if (!v || v.includes('\n'))
208
207
  return v;
209
208
  // v is a path, we'll watch the file for changes
@@ -244,12 +243,12 @@ function startServer(srv, { port, host }) {
244
243
  });
245
244
  async function testIpV4() {
246
245
  const res = await listen('0.0.0.0', true);
247
- await new Promise(res => srv === null || srv === void 0 ? void 0 : srv.close(res)); // close, if any, and wait
246
+ await new Promise(res => srv?.close(res)); // close, if any, and wait
248
247
  return res > 0;
249
248
  }
250
249
  function listen(host, silence = false) {
251
250
  return new Promise(async (resolve, reject) => {
252
- srv === null || srv === void 0 ? void 0 : srv.on('error', onError).listen({ port, host }, () => {
251
+ srv?.on('error', onError).listen({ port, host }, () => {
253
252
  const ad = srv.address();
254
253
  if (!ad)
255
254
  return reject('no address');
@@ -268,7 +267,7 @@ function startServer(srv, { port, host }) {
268
267
  srv.busy = undefined;
269
268
  const { code } = e;
270
269
  if (code)
271
- srv.busy = (0, find_process_1.default)('port', port).then(res => res === null || res === void 0 ? void 0 : res.map(x => { var _a; return (0, misc_1.prefix)("Service", x.name === 'svchost.exe' && ((_a = x.cmd.split(x.name)[1]) === null || _a === void 0 ? void 0 : _a.trim())) || x.name; }).join(' + '), () => '');
270
+ srv.busy = (0, find_process_1.default)('port', port).then(res => res?.map(x => (0, misc_1.prefix)("Service", x.name === 'svchost.exe' && x.cmd.split(x.name)[1]?.trim()) || x.name).join(' + '), () => '');
272
271
  if (code === 'EACCES' && port < 1024 && !srv.busy) // on Windows, when port is used by a service, we get EACCES
273
272
  srv.error = `lacking permission on port ${port}, try with permission (${const_1.IS_WINDOWS ? 'administrator' : 'sudo'}) or port > 1024`;
274
273
  if (code === 'EADDRINUSE' || srv.busy)
@@ -282,7 +281,7 @@ function startServer(srv, { port, host }) {
282
281
  }
283
282
  function stopServer(srv) {
284
283
  return new Promise(resolve => {
285
- if (!(srv === null || srv === void 0 ? void 0 : srv.listening))
284
+ if (!srv?.listening)
286
285
  return resolve(null);
287
286
  const ad = srv.address();
288
287
  if (ad && typeof ad !== 'string')
@@ -295,8 +294,7 @@ function stopServer(srv) {
295
294
  });
296
295
  }
297
296
  function getCurrentPort(srv) {
298
- var _a;
299
- return (_a = srv === null || srv === void 0 ? void 0 : srv.address()) === null || _a === void 0 ? void 0 : _a.port;
297
+ return srv?.address()?.port;
300
298
  }
301
299
  async function getServerStatus(includeSrv = true) {
302
300
  return {
@@ -304,7 +302,7 @@ async function getServerStatus(includeSrv = true) {
304
302
  https: await serverStatus(httpsSrv, exports.httpsPortCfg.get()),
305
303
  };
306
304
  async function serverStatus(srv, configuredPort) {
307
- const busy = await (srv === null || srv === void 0 ? void 0 : srv.busy);
305
+ const busy = await srv?.busy;
308
306
  await (0, misc_1.wait)(0); // simple trick to wait for also .error to be updated. If this trickery becomes necessary elsewhere, then we should make also error a Promise.
309
307
  return {
310
308
  ...lodash_1.default.pick(srv, ['listening', 'error']),
@@ -329,17 +327,16 @@ async function getIps(external = true) {
329
327
  x => x !== nat_1.defaultBaseUrl.localIp, // use the "nat" info to put best ip first
330
328
  net_1.isIPv6 // false=IPV4 comes first
331
329
  ]);
332
- nat_1.defaultBaseUrl.localIp || (nat_1.defaultBaseUrl.localIp = ret[0] || '');
330
+ nat_1.defaultBaseUrl.localIp ||= ret[0] || '';
333
331
  return ret;
334
332
  }
335
333
  async function getUrls() {
336
334
  const on = listenInterface.get();
337
335
  const ips = on === renderHost(on) ? [on] : await getIps();
338
336
  return Object.fromEntries((0, misc_1.onlyTruthy)([httpSrv, httpsSrv].map(srv => {
339
- var _a;
340
- if (!(srv === null || srv === void 0 ? void 0 : srv.listening))
337
+ if (!srv?.listening)
341
338
  return false;
342
- const port = (_a = srv === null || srv === void 0 ? void 0 : srv.address()) === null || _a === void 0 ? void 0 : _a.port;
339
+ const port = srv?.address()?.port;
343
340
  const appendPort = port === (srv.name === 'https' ? 443 : 80) ? '' : ':' + port;
344
341
  const urls = ips.map(ip => `${srv.name}://${(0, misc_1.ipForUrl)(ip)}${appendPort}`);
345
342
  return urls.length && [srv.name, urls];
package/src/log.js CHANGED
@@ -54,14 +54,16 @@ const index_1 = require("./index");
54
54
  const serveGuiFiles_1 = require("./serveGuiFiles");
55
55
  const fast_glob_1 = __importDefault(require("fast-glob"));
56
56
  class Logger {
57
+ name;
58
+ stream;
59
+ last;
60
+ path = '';
57
61
  constructor(name) {
58
62
  this.name = name;
59
- this.path = '';
60
63
  }
61
64
  async setPath(path) {
62
- var _a;
63
65
  this.path = path;
64
- (_a = this.stream) === null || _a === void 0 ? void 0 : _a.end();
66
+ this.stream?.end();
65
67
  this.last = undefined;
66
68
  if (!path)
67
69
  return this.stream = undefined;
@@ -69,15 +71,15 @@ class Logger {
69
71
  const stats = await (0, util_files_1.statWithTimeout)(path);
70
72
  this.last = stats.mtime;
71
73
  }
72
- catch (_b) {
73
- if (await (0, util_files_1.prepareFolder)(path) === false)
74
+ catch {
75
+ if (await (0, util_files_1.ensureParentFolder)(path) === false)
74
76
  console.log("cannot create folder for", path);
75
77
  }
76
78
  this.reopen();
77
79
  }
78
80
  reopen() {
79
- var _a;
80
- return this.stream = (_a = (0, util_files_1.createFileWithPath)(this.path, { flags: 'a' })) === null || _a === void 0 ? void 0 : _a.on('error', () => this.stream = undefined);
81
+ return this.stream = (0, util_files_1.createFileWithPath)(this.path, { flags: 'a' })
82
+ ?.on('error', () => this.stream = undefined);
81
83
  }
82
84
  }
83
85
  // we'll have names same as config keys. These are used also by the get_log api.
@@ -115,21 +117,20 @@ const logMw = async (ctx, next) => {
115
117
  const conn = (0, connections_1.getConnection)(ctx); // collect reference before close
116
118
  // don't await, as we don't want to hold the middlewares chain
117
119
  ctx.state.completed.then(() => {
118
- var _a, _b, _c, _d;
119
120
  if (ctx.state.dontLog || ctx.state.considerAsGui && !serveGuiFiles_1.logGui.get())
120
121
  return;
121
122
  if (dontLogNet.compiled()(ctx.ip))
122
123
  return;
123
124
  const isError = ctx.status >= 400;
124
125
  const logger = isError && accessErrorLog || accessLogger;
125
- const rotate = (_a = logRotation.get()) === null || _a === void 0 ? void 0 : _a[0];
126
+ const rotate = logRotation.get()?.[0];
126
127
  let { stream, last, path } = logger;
127
128
  if (!stream)
128
129
  return;
129
130
  logger.last = now;
130
131
  if (rotate && last) { // rotation enabled and a file exists?
131
132
  const passed = Number(now) - Number(last)
132
- - 3600000; // be pessimistic and count a possible DST change
133
+ - 3600_000; // be pessimistic and count a possible DST change
133
134
  if (rotate === 'm' && (passed >= 31 * misc_1.DAY || now.getMonth() !== last.getMonth())
134
135
  || rotate === 'd' && (passed >= misc_1.DAY || now.getDate() !== last.getDate()) // checking passed will solve the case when the day of the month is the same but a month has passed
135
136
  || rotate === 'w' && (passed >= 7 * misc_1.DAY || now.getDay() < last.getDay())) {
@@ -149,9 +150,9 @@ const logMw = async (ctx, next) => {
149
150
  }
150
151
  const format = '%s - %s [%s] "%s %s HTTP/%s" %d %s %s\n'; // Apache's Common Log Format
151
152
  const a = now.toString().split(' '); // like nginx, our default log contains the time of log writing
152
- const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + ((_b = a[5]) === null || _b === void 0 ? void 0 : _b.slice(3));
153
+ const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + a[5]?.slice(3);
153
154
  const user = (0, auth_1.getCurrentUsername)(ctx);
154
- const length = (_c = ctx.state.length) !== null && _c !== void 0 ? _c : ctx.length;
155
+ const length = ctx.state.length ?? ctx.length;
155
156
  const uri = ctx.originalUrl;
156
157
  const duration = (Date.now() - Number(now)) / 1000;
157
158
  ctx.logExtra(ctx.vfsNode && {
@@ -162,7 +163,7 @@ const logMw = async (ctx, next) => {
162
163
  size: ctx.state.uploadSize,
163
164
  speed: Math.round(ctx.state.uploadSize / duration),
164
165
  });
165
- if (conn === null || conn === void 0 ? void 0 : conn.country)
166
+ if (conn?.country)
166
167
  ctx.logExtra({ country: conn.country });
167
168
  if (logUA.get())
168
169
  ctx.logExtra({ ua: ctx.get('user-agent') || undefined });
@@ -171,7 +172,7 @@ const logMw = async (ctx, next) => {
171
172
  events_2.default.emit(logger.name, { ctx, length, user, ts: now, uri, extra });
172
173
  debounce(() => // once in a while we check if the file is still good (not deleted, etc), or we'll reopen it
173
174
  (0, util_files_1.statWithTimeout)(logger.path).catch(() => logger.reopen())); // async = smoother but we may lose some entries
174
- stream.write(util.format(format, ctx.ip, user || '-', date, ctx.method, uri, ctx.req.httpVersion, ctx.status, (_d = length === null || length === void 0 ? void 0 : length.toString()) !== null && _d !== void 0 ? _d : '-', lodash_1.default.isEmpty(extra) ? '' : JSON.stringify(JSON.stringify(extra))));
175
+ stream.write(util.format(format, ctx.ip, user || '-', date, ctx.method, uri, ctx.req.httpVersion, ctx.status, length?.toString() ?? '-', lodash_1.default.isEmpty(extra) ? '' : JSON.stringify(JSON.stringify(extra))));
175
176
  });
176
177
  };
177
178
  exports.logMw = logMw;
@@ -186,7 +187,7 @@ function doubleDigit(n) {
186
187
  async function getRotatedFiles() {
187
188
  return Object.fromEntries(await Promise.all(exports.loggers.map(async (x) => {
188
189
  const mask = (0, misc_1.strinsert)(x.path, x.path.length - (0, path_1.extname)(x.path).length, '-2*'); // including 2, initial digit of the year, will only take rotated files and not "-error"
189
- return [x.name, (await (0, fast_glob_1.default)(mask, { stats: true })).map(x => { var _a; return ({ path: x.path, size: (_a = x.stats) === null || _a === void 0 ? void 0 : _a.size }); })];
190
+ return [x.name, (await (0, fast_glob_1.default)(mask, { stats: true })).map(x => ({ path: x.path, size: x.stats?.size }))];
190
191
  })));
191
192
  }
192
193
  // dump console.error to file
@@ -195,11 +196,11 @@ debugLogFile.once('open', () => {
195
196
  const was = console.error;
196
197
  console.error = function (...args) {
197
198
  was.apply(this, args);
198
- args = args.map(x => { var _a; return typeof x === 'string' ? x : ((_a = (0, misc_1.tryJson)(x)) !== null && _a !== void 0 ? _a : String(x)); });
199
+ args = args.map(x => typeof x === 'string' ? x : ((0, misc_1.tryJson)(x) ?? String(x)));
199
200
  debugLogFile.write((0, misc_1.formatTimestamp)(new Date) + ' - ' + args.join(' ') + '\n');
200
201
  };
201
202
  // limit log size
202
- const LIMIT = 1000000;
203
+ const LIMIT = 1_000_000;
203
204
  const { path } = debugLogFile;
204
205
  (0, misc_1.repeat)(misc_1.DAY, () => {
205
206
  if ((0, fs_1.statSync)(path).size < LIMIT)
@@ -53,7 +53,7 @@ let proxyDetected;
53
53
  const someSecurity = (ctx, next) => {
54
54
  ctx.request.ip = (0, connections_1.normalizeIp)(ctx.ip);
55
55
  const ss = ctx.session;
56
- if ((ss === null || ss === void 0 ? void 0 : ss.username) && !(ss === null || ss === void 0 ? void 0 : ss[misc_1.ALLOW_SESSION_IP_CHANGE]))
56
+ if (ss?.username && !ss?.[misc_1.ALLOW_SESSION_IP_CHANGE])
57
57
  if (!ss.ip)
58
58
  ss.ip = ctx.ip;
59
59
  else if (ss.ip !== ctx.ip) {
@@ -61,7 +61,7 @@ const someSecurity = (ctx, next) => {
61
61
  ss.ip = ctx.ip;
62
62
  }
63
63
  try {
64
- if ((0, misc_1.dirTraversal)(decodeURI(ctx.path)))
64
+ if ((0, misc_1.hasDirTraversal)(decodeURI(ctx.path)))
65
65
  return ctx.status = const_1.HTTP_FOOL;
66
66
  if (!ctx.state.skipFilters && (0, block_1.applyBlock)(ctx.socket, ctx.ip))
67
67
  return;
@@ -74,7 +74,7 @@ const someSecurity = (ctx, next) => {
74
74
  if (ctx.get('cf-ray'))
75
75
  exports.cloudflareDetected = new Date();
76
76
  }
77
- catch (_a) {
77
+ catch {
78
78
  return ctx.status = const_1.HTTP_FOOL;
79
79
  }
80
80
  if (!ctx.secure && forceHttps.get() && (0, listen_1.getHttpsWorkingPort)() && !(0, misc_1.isLocalHost)(ctx)) {
@@ -89,20 +89,19 @@ const someSecurity = (ctx, next) => {
89
89
  exports.someSecurity = someSecurity;
90
90
  // limited to http proxies
91
91
  function getProxyDetected() {
92
- if ((proxyDetected === null || proxyDetected === void 0 ? void 0 : proxyDetected.state.whenProxyDetected) < Date.now() - misc_1.DAY) // detection is reset after a day
92
+ if (proxyDetected?.state.whenProxyDetected < Date.now() - misc_1.DAY) // detection is reset after a day
93
93
  proxyDetected = undefined;
94
94
  return proxyDetected && { from: proxyDetected.socket.remoteAddress, for: proxyDetected.get('X-Forwarded-For') };
95
95
  }
96
96
  const prepareState = async (ctx, next) => {
97
- var _a, _b;
98
- if ((_a = ctx.session) === null || _a === void 0 ? void 0 : _a.username) {
97
+ if (ctx.session?.username) {
99
98
  if (ctx.session.ts < auth_1.invalidateSessionBefore.get(ctx.session.username))
100
99
  delete ctx.session.username;
101
100
  ctx.session.maxAge = exports.sessionDuration.compiled();
102
101
  }
103
102
  // calculate these once and for all
104
103
  ctx.state.connection = (0, connections_1.socket2connection)(ctx.socket);
105
- const a = ctx.state.account = await urlLogin() || await getHttpAccount() || (0, perm_1.getAccount)((_b = ctx.session) === null || _b === void 0 ? void 0 : _b.username, false);
104
+ const a = ctx.state.account = await urlLogin() || await getHttpAccount() || (0, perm_1.getAccount)(ctx.session?.username, false);
106
105
  if (a && (!(0, perm_1.accountCanLogin)(a) || failAllowNet(ctx, a))) // enforce allow_net also after login
107
106
  ctx.state.account = undefined;
108
107
  ctx.state.revProxyPath = ctx.get('x-forwarded-prefix');
@@ -117,25 +116,23 @@ const prepareState = async (ctx, next) => {
117
116
  return u && (0, auth_1.clearTextLogin)(ctx, u, p, 'url');
118
117
  }
119
118
  function getHttpAccount() {
120
- var _a, _b;
121
- const b64 = allowAuthorizationHeader.get() && ((_a = ctx.get('authorization')) === null || _a === void 0 ? void 0 : _a.split(' ')[1]);
119
+ const b64 = allowAuthorizationHeader.get() && ctx.get('authorization')?.split(' ')[1];
122
120
  if (!b64)
123
121
  return;
124
122
  try {
125
123
  const [u, p] = atob(b64).split(':');
126
- if (!u || u === ((_b = ctx.session) === null || _b === void 0 ? void 0 : _b.username))
124
+ if (!u || u === ctx.session?.username)
127
125
  return; // providing credentials, but not needed
128
126
  return (0, auth_1.clearTextLogin)(ctx, u, p || '', 'header');
129
127
  }
130
- catch (_c) { }
128
+ catch { }
131
129
  }
132
130
  };
133
131
  exports.prepareState = prepareState;
134
132
  function failAllowNet(ctx, a) {
135
- var _a, _b;
136
- const cached = (_a = ctx.session) === null || _a === void 0 ? void 0 : _a.allowNet; // won't reflect changes until session is terminated
137
- const mask = cached !== null && cached !== void 0 ? cached : (0, perm_1.getFromAccount)(a || '', a => a.allow_net);
138
- if (!cached && mask && ((_b = ctx.session) === null || _b === void 0 ? void 0 : _b.username))
133
+ const cached = ctx.session?.allowNet; // won't reflect changes until session is terminated
134
+ const mask = cached ?? (0, perm_1.getFromAccount)(a || '', a => a.allow_net);
135
+ if (!cached && mask && ctx.session?.username)
139
136
  ctx.session.allowNet = mask; // must be deleted on logout by setLoggedIn
140
137
  const ret = mask && !(0, misc_1.netMatches)(ctx.ip, mask, true);
141
138
  if (ret)
package/src/misc.js CHANGED
@@ -51,12 +51,10 @@ function isLocalHost(c) {
51
51
  }
52
52
  // this will memory-leak over mask, so be careful with what you use this. Object is 3x faster than _.memoize
53
53
  function netMatches(ip, mask, emptyMaskReturns = false) {
54
- var _a, _b;
55
- const cache = (_a = netMatches).cache || (_a.cache = {});
56
- return (cache[_b = mask + (emptyMaskReturns ? '1' : '0')] || (cache[_b] = makeNetMatcher(mask, emptyMaskReturns)))(ip); // cache the matcher
54
+ const cache = netMatches.cache ||= {};
55
+ return (cache[mask + (emptyMaskReturns ? '1' : '0')] ||= makeNetMatcher(mask, emptyMaskReturns))(ip); // cache the matcher
57
56
  }
58
57
  function makeNetMatcher(mask, emptyMaskReturns = false) {
59
- var _a;
60
58
  if (!mask)
61
59
  return () => emptyMaskReturns;
62
60
  mask = mask.replaceAll(' ', '');
@@ -68,7 +66,7 @@ function makeNetMatcher(mask, emptyMaskReturns = false) {
68
66
  return (0, cross_1.makeMatcher)(mask);
69
67
  }
70
68
  const all = mask.split('|');
71
- const neg = ((_a = all[0]) === null || _a === void 0 ? void 0 : _a[0]) === '!';
69
+ const neg = all[0]?.[0] === '!';
72
70
  if (neg)
73
71
  all[0] = all[0].slice(1);
74
72
  const bl = new node_net_1.BlockList();
@@ -83,14 +81,14 @@ function makeNetMatcher(mask, emptyMaskReturns = false) {
83
81
  try {
84
82
  bl.addSubnet(address, Number(m[2]));
85
83
  }
86
- catch (_b) {
84
+ catch {
87
85
  throw x;
88
86
  }
89
87
  else if (m[3])
90
88
  try {
91
89
  bl.addRange(address, parseAddress(m[3]));
92
90
  }
93
- catch (_c) {
91
+ catch {
94
92
  throw m[3];
95
93
  }
96
94
  else
@@ -100,7 +98,7 @@ function makeNetMatcher(mask, emptyMaskReturns = false) {
100
98
  try {
101
99
  return neg !== bl.check(parseAddress(ip));
102
100
  }
103
- catch (_a) {
101
+ catch {
104
102
  console.error("invalid address ", ip);
105
103
  return false;
106
104
  }
@@ -122,8 +120,7 @@ function asyncGeneratorToReadable(generator) {
122
120
  return new stream_1.Readable({
123
121
  objectMode: true,
124
122
  destroy() {
125
- var _a;
126
- void ((_a = iterator.return) === null || _a === void 0 ? void 0 : _a.call(iterator));
123
+ void iterator.return?.();
127
124
  },
128
125
  read() {
129
126
  iterator.next().then(it => {
@@ -136,10 +133,11 @@ function asyncGeneratorToReadable(generator) {
136
133
  }
137
134
  // produces as promises resolve, not sequentially
138
135
  class AsapStream extends stream_1.Readable {
136
+ promises;
137
+ finished = false;
139
138
  constructor(promises) {
140
139
  super({ objectMode: true });
141
140
  this.promises = promises;
142
- this.finished = false;
143
141
  }
144
142
  _read() {
145
143
  if (this.finished)
package/src/nat.js CHANGED
@@ -30,10 +30,10 @@ exports.defaultBaseUrl = (0, valtio_1.proxy)({
30
30
  return `${this.proto}://${(0, cross_1.ipForUrl)(ip || 'localhost')}${!port || port === defPort ? '' : ':' + port}`;
31
31
  }
32
32
  });
33
- exports.upnpClient = new nat_upnp_rejetto_1.Client({ timeout: 4000 });
33
+ exports.upnpClient = new nat_upnp_rejetto_1.Client({ timeout: 4_000 });
34
34
  const originalMethod = exports.upnpClient.getGateway;
35
35
  // other client methods call getGateway too, so this will ensure they reuse this same result
36
- exports.upnpClient.getGateway = (0, debounceAsync_1.debounceAsync)(() => originalMethod.apply(exports.upnpClient), { retain: cross_1.HOUR, retainFailure: 30000 });
36
+ exports.upnpClient.getGateway = (0, debounceAsync_1.debounceAsync)(() => originalMethod.apply(exports.upnpClient), { retain: cross_1.HOUR, retainFailure: 30_000 });
37
37
  exports.upnpClient.getGateway().then(res => {
38
38
  console.log("upnp found", res.gateway.description);
39
39
  }, e => console.debug('upnp failed:', e.message || String(e)));
@@ -46,16 +46,16 @@ exports.upnpClient.getGateway().then(res => {
46
46
  }, () => { }));
47
47
  exports.getPublicIps = (0, debounceAsync_1.debounceAsync)(async () => {
48
48
  const res = await (0, github_1.getProjectInfo)();
49
- const groupedByVersion = Object.values(lodash_1.default.groupBy(res.publicIpServices, x => { var _a; return (_a = x.v) !== null && _a !== void 0 ? _a : 4; }));
49
+ const groupedByVersion = Object.values(lodash_1.default.groupBy(res.publicIpServices, x => x.v ?? 4));
50
50
  const ips = await (0, cross_1.promiseBestEffort)(groupedByVersion.map(singleVersion => Promise.any(singleVersion.map(async (svc) => {
51
51
  if (typeof svc === 'string')
52
52
  svc = { type: 'http', url: svc };
53
53
  console.debug("trying ip service", svc.url || svc.name);
54
54
  if (svc.type === 'http')
55
- return (0, util_http_1.httpString)(svc.url, { timeout: 5000 });
55
+ return (0, util_http_1.httpString)(svc.url, { timeout: 5_000 });
56
56
  if (svc.type !== 'dns')
57
57
  throw "unsupported";
58
- const resolver = new promises_1.Resolver({ timeout: 2000 });
58
+ const resolver = new promises_1.Resolver({ timeout: 2_000 });
59
59
  resolver.setServers(svc.ips);
60
60
  return resolver.resolve(svc.name, svc.dnsRecord);
61
61
  }).map(async (ret) => {
@@ -67,18 +67,17 @@ exports.getPublicIps = (0, debounceAsync_1.debounceAsync)(async () => {
67
67
  return exports.defaultBaseUrl.publicIps = lodash_1.default.uniq(ips.flat());
68
68
  }, { retain: 10 * cross_1.MINUTE });
69
69
  exports.getNatInfo = (0, debounceAsync_1.debounceAsync)(async () => {
70
- var _a, _b, _c, _d;
71
70
  const gatewayIpPromise = findGateway().catch(() => undefined);
72
- const res = await (0, cross_1.haveTimeout)(10000, exports.upnpClient.getGateway()).catch(() => null);
71
+ const res = await (0, cross_1.haveTimeout)(10_000, exports.upnpClient.getGateway()).catch(() => null);
73
72
  const status = await (0, listen_1.getServerStatus)();
74
- const mappings = res && await (0, cross_1.haveTimeout)(5000, exports.upnpClient.getMappings()).catch(() => null);
75
- console.debug("mappings found:", (mappings === null || mappings === void 0 ? void 0 : mappings.map(x => x.description).join(', ')) || "none");
73
+ const mappings = res && await (0, cross_1.haveTimeout)(5_000, exports.upnpClient.getMappings()).catch(() => null);
74
+ console.debug("mappings found:", mappings?.map(x => x.description).join(', ') || "none");
76
75
  const localIps = await (0, listen_1.getIps)(false);
77
76
  const gatewayIp = await gatewayIpPromise;
78
- const localIp = (res === null || res === void 0 ? void 0 : res.address) || (gatewayIp ? lodash_1.default.maxBy(localIps, x => (0, cross_1.inCommon)(x, gatewayIp)) : localIps[0]);
79
- const internalPort = ((_a = status === null || status === void 0 ? void 0 : status.https) === null || _a === void 0 ? void 0 : _a.listening) && status.https.port || ((_b = status === null || status === void 0 ? void 0 : status.http) === null || _b === void 0 ? void 0 : _b.listening) && status.http.port || undefined;
77
+ const localIp = res?.address || (gatewayIp ? lodash_1.default.maxBy(localIps, x => (0, cross_1.inCommon)(x, gatewayIp)) : localIps[0]);
78
+ const internalPort = status?.https?.listening && status.https.port || status?.http?.listening && status.http.port || undefined;
80
79
  const mapped = lodash_1.default.find(mappings, x => x.private.host === localIp && x.private.port === internalPort);
81
- const externalPort = mapped === null || mapped === void 0 ? void 0 : mapped.public.port;
80
+ const externalPort = mapped?.public.port;
82
81
  if (localIp)
83
82
  exports.defaultBaseUrl.localIp = localIp;
84
83
  exports.defaultBaseUrl.port = externalPort || internalPort || 0;
@@ -91,18 +90,17 @@ exports.getNatInfo = (0, debounceAsync_1.debounceAsync)(async () => {
91
90
  mapped80: lodash_1.default.find(mappings, x => x.private.host === localIp && x.private.port === 80 && x.public.port === 80),
92
91
  internalPort,
93
92
  externalPort,
94
- proto: ((_c = status === null || status === void 0 ? void 0 : status.https) === null || _c === void 0 ? void 0 : _c.listening) ? 'https' : ((_d = status === null || status === void 0 ? void 0 : status.http) === null || _d === void 0 ? void 0 : _d.listening) ? 'http' : '',
93
+ proto: status?.https?.listening ? 'https' : status?.http?.listening ? 'http' : '',
95
94
  };
96
95
  }, { reuseRunning: true });
97
96
  (0, exports.getNatInfo)();
98
97
  function findGateway() {
99
98
  return new Promise((resolve, reject) => (0, child_process_1.exec)(const_1.IS_WINDOWS || const_1.IS_MAC ? 'netstat -rn' : 'route -n', (err, out) => {
100
- var _a, _b;
101
99
  if (err)
102
100
  return reject(err);
103
101
  if (!const_1.IS_WINDOWS)
104
- return resolve((_a = out.match(const_1.IS_MAC ? /default +([\d.]+)/ : /^0\.0\.0\.0 +([\d.]+)/)) === null || _a === void 0 ? void 0 : _a[1]);
102
+ return resolve(out.match(const_1.IS_MAC ? /default +([\d.]+)/ : /^0\.0\.0\.0 +([\d.]+)/)?.[1]);
105
103
  const sortedByMetric = lodash_1.default.sortBy([...out.matchAll(/(?:0\.0\.0\.0 +){2}([\d.]+)\s+[\d.]+\s+(\d+)/g)], x => Number(x[2]));
106
- resolve((_b = sortedByMetric[0]) === null || _b === void 0 ? void 0 : _b[1]); // take ip with lowest metric
104
+ resolve(sortedByMetric[0]?.[1]); // take ip with lowest metric
107
105
  }));
108
106
  }
@@ -13,27 +13,27 @@ const outboundProxy = (0, config_1.defineConfig)(cross_1.CFG.outbound_proxy, '',
13
13
  if (!v || process.env.HFS_SKIP_PROXY_TEST)
14
14
  return;
15
15
  const test = 'https://google.com';
16
- (0, util_http_1.httpString)(test).catch(e => { var _a; return console.error(`proxy failed for ${test} : ${((_a = e === null || e === void 0 ? void 0 : e.errors) === null || _a === void 0 ? void 0 : _a[0]) || e}`); }); // `.errors` in case of AggregateError
16
+ console.debug("testing proxy using", test);
17
+ (0, util_http_1.httpString)(test, { noRedirect: true }).catch(e => console.error(`proxy test failed on ${test} : ${e?.errors?.[0] || e}`)); // `.errors` in case of AggregateError
17
18
  }
18
- catch (_a) {
19
+ catch {
19
20
  console.warn("invalid URL", v);
20
21
  return '';
21
22
  }
22
23
  });
23
24
  config_1.configReady.then(async ([startedWithoutConfig]) => {
24
- var _a, _b, _c, _d;
25
25
  if (!const_1.IS_WINDOWS || !startedWithoutConfig)
26
26
  return;
27
27
  // try to read Windows system setting for proxy
28
28
  const out = await (0, util_os_1.reg)('query', 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings');
29
- if (!Number((_a = /ProxyEnable\s+\w+\s+(.+)/.exec(out)) === null || _a === void 0 ? void 0 : _a[1]))
29
+ if (!Number(/ProxyEnable\s+\w+\s+(.+)/.exec(out)?.[1]))
30
30
  return;
31
- const read = (_b = /ProxyServer\s+\w+\s+(.+)/.exec(out)) === null || _b === void 0 ? void 0 : _b[1];
31
+ const read = /ProxyServer\s+\w+\s+(.+)/.exec(out)?.[1];
32
32
  if (!read)
33
33
  return;
34
34
  // it can be like "IP:PORT" or "http=IP:PORT;https=IP:PORT;ftp=IP:PORT"
35
- const url = (0, cross_1.prefix)('https://', (_c = /https=([\d:.]+)/.exec(out)) === null || _c === void 0 ? void 0 : _c[1]) // prefer https
36
- || (0, cross_1.prefix)('http://', (_d = /http=([\d:.]+)/.exec(out)) === null || _d === void 0 ? void 0 : _d[1])
35
+ const url = (0, cross_1.prefix)('https://', /https=([\d:.]+)/.exec(out)?.[1]) // prefer https
36
+ || (0, cross_1.prefix)('http://', /http=([\d:.]+)/.exec(out)?.[1])
37
37
  || !read.includes('=') && 'http://' + read; // simpler form
38
38
  if (!url)
39
39
  return;
package/src/perm.js CHANGED
@@ -46,8 +46,7 @@ function expandUsername(who) {
46
46
  }
47
47
  // check if current username or any ancestor match the provided usernames
48
48
  function ctxBelongsTo(ctx, usernames) {
49
- var _a;
50
- return ((_a = ctx.state).usernames || (_a.usernames = expandUsername((0, auth_1.getCurrentUsername)(ctx)))) // cache ancestors' usernames inside context state
49
+ return (ctx.state.usernames ||= expandUsername((0, auth_1.getCurrentUsername)(ctx))) // cache ancestors' usernames inside context state
51
50
  .some((u) => usernames.includes(u));
52
51
  }
53
52
  function getUsernames() {
@@ -78,7 +77,7 @@ async function updateAccount(account, change) {
78
77
  const jsonWas = JSON.stringify(account);
79
78
  const { username: usernameWas } = account;
80
79
  if (typeof change === 'function')
81
- await (change === null || change === void 0 ? void 0 : change(account));
80
+ await change?.(account);
82
81
  else {
83
82
  const u = normalizeUsername(change.username || '');
84
83
  if (u && u !== usernameWas && getAccount(u))
@@ -106,7 +105,7 @@ async function updateAccount(account, change) {
106
105
  if (!account.belongs.length)
107
106
  delete account.belongs;
108
107
  }
109
- account.expire && (account.expire = new Date(account.expire));
108
+ account.expire &&= new Date(account.expire);
110
109
  if (username !== usernameWas)
111
110
  renameAccount(usernameWas, username);
112
111
  if (jsonWas !== JSON.stringify(account)) // this test will miss the 'username' field, because hidden, but renameAccount is already calling saveAccountsASAP
@@ -118,7 +117,7 @@ exports.accounts.sub(lodash_1.default.debounce(obj => {
118
117
  // consider some validation here, in case of manual edit of the config
119
118
  lodash_1.default.each(obj, (rec, k) => {
120
119
  const norm = normalizeUsername(k);
121
- if ((rec === null || rec === void 0 ? void 0 : rec.username) !== norm) {
120
+ if (rec?.username !== norm) {
122
121
  if (!rec) // an empty object in yaml is parsed as null
123
122
  rec = obj[norm] = { username: norm };
124
123
  else if ((0, misc_1.objRenameKey)(obj, k, norm))
@@ -132,7 +131,6 @@ function normalizeUsername(username) {
132
131
  return username.toLocaleLowerCase();
133
132
  }
134
133
  function renameAccount(from, to) {
135
- var _a;
136
134
  from = normalizeUsername(from);
137
135
  const as = exports.accounts.get();
138
136
  to = normalizeUsername(to);
@@ -144,7 +142,7 @@ function renameAccount(from, to) {
144
142
  (0, misc_1.setHidden)(as[to], { username: to });
145
143
  // update references
146
144
  for (const a of Object.values(as)) {
147
- const idx = (_a = a.belongs) === null || _a === void 0 ? void 0 : _a.indexOf(from);
145
+ const idx = a.belongs?.indexOf(from);
148
146
  if (idx !== undefined && idx >= 0)
149
147
  a.belongs[idx] = to;
150
148
  }
@@ -190,15 +188,12 @@ function accountHasPassword(account) {
190
188
  return Boolean(account.password || account.srp);
191
189
  }
192
190
  function accountCanLogin(account) {
193
- var _a;
194
- return (accountHasPassword(account) || ((_a = account.plugin) === null || _a === void 0 ? void 0 : _a.auth)) && !accountIsDisabled(account);
191
+ return (accountHasPassword(account) || account.plugin?.auth) && !accountIsDisabled(account);
195
192
  }
196
193
  function accountIsDisabled(account) {
197
- var _a;
198
194
  return Boolean(account.disabled
199
195
  || account.expire < Date.now()
200
- || ((_a = account.belongs) === null || _a === void 0 ? void 0 : _a.length // don't every() on empty array, as it returns true
201
- )
196
+ || account.belongs?.length // don't every() on empty array, as it returns true
202
197
  && account.belongs.map(u => getAccount(u, false)).every(a => a && accountIsDisabled(a)));
203
198
  }
204
199
  function accountCanLoginAdmin(account) {
@@ -9,7 +9,7 @@ exports.storedMap = new kvstorage_1.KvStorage({
9
9
  maxPutDelay: misc_1.MINUTE,
10
10
  maxPutDelayCreate: 1000,
11
11
  rewriteLater: true,
12
- bucketThreshold: 10000,
12
+ bucketThreshold: 10_000,
13
13
  });
14
14
  exports.storedMap.open('data.kv');
15
15
  (0, first_1.onProcessExit)(() => exports.storedMap.flush());