hfs 0.38.2 → 0.38.3

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
- import{c as SF}from"./index-4b763f31.js";function OF(iF,hF){for(var eF=0;eF<hF.length;eF++){const tF=hF[eF];if(typeof tF!="string"&&!Array.isArray(tF)){for(const w in tF)if(w!=="default"&&!(w in iF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(iF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(iF,Symbol.toStringTag,{value:"Module"}))}var EF={},UF={get exports(){return EF},set exports(iF){EF=iF}};/*
1
+ import{c as SF}from"./index-869b7be6.js";function OF(iF,hF){for(var eF=0;eF<hF.length;eF++){const tF=hF[eF];if(typeof tF!="string"&&!Array.isArray(tF)){for(const w in tF)if(w!=="default"&&!(w in iF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(iF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(iF,Symbol.toStringTag,{value:"Module"}))}var EF={},UF={get exports(){return EF},set exports(iF){EF=iF}};/*
2
2
  * [js-sha512]{@link https://github.com/emn178/js-sha512}
3
3
  *
4
4
  * @version 0.8.0
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0" />
6
6
  <link href="/fontello.css" rel="stylesheet" />
7
7
 
8
- <script type="module" crossorigin src="/assets/index-4b763f31.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-869b7be6.js"></script>
9
9
  <link rel="stylesheet" href="/assets/index-6d4e81f7.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hfs",
3
- "version": "0.38.2",
3
+ "version": "0.38.3",
4
4
  "description": "HTTP File Server",
5
5
  "keywords": [
6
6
  "file server",
package/src/api.lang.js CHANGED
@@ -8,8 +8,8 @@ const apiMiddleware_1 = require("./apiMiddleware");
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
9
  const fast_glob_1 = __importDefault(require("fast-glob"));
10
10
  const promises_1 = require("fs/promises");
11
- const util_files_1 = require("./util-files");
12
11
  const const_1 = require("./const");
12
+ const misc_1 = require("./misc");
13
13
  const PREFIX = 'hfs-lang-';
14
14
  const SUFFIX = '.json';
15
15
  const apis = {
@@ -44,7 +44,11 @@ const apis = {
44
44
  if (code.endsWith(SUFFIX)) // filename, actually
45
45
  code = code.slice(PREFIX.length, -SUFFIX.length);
46
46
  validateCode(code);
47
- await (0, promises_1.writeFile)(code2file(code), String(content), 'utf8');
47
+ const fn = code2file(code);
48
+ const s = content = String(content);
49
+ if (!(0, misc_1.tryJson)(s))
50
+ return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, "bad content for file " + fn);
51
+ await (0, promises_1.writeFile)(fn, s, 'utf8');
48
52
  }
49
53
  return {};
50
54
  }
@@ -54,6 +58,6 @@ function code2file(code) {
54
58
  return PREFIX + code.toLowerCase() + SUFFIX;
55
59
  }
56
60
  function validateCode(code) {
57
- if (!(0, util_files_1.isValidFileName)(code) || (0, util_files_1.dirTraversal)(code))
58
- throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad code');
61
+ if (!/^(\w\w)(-\w\w)*$/.test(code))
62
+ throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad code/filename');
59
63
  }
@@ -12,12 +12,17 @@ const throttler_1 = require("./throttler");
12
12
  const perm_1 = require("./perm");
13
13
  const apis = {
14
14
  async disconnect({ ip, port, wait }) {
15
+ var _a, _b;
15
16
  const match = lodash_1.default.matches({ ip, port });
16
17
  const c = (0, connections_1.getConnections)().find(c => match(getConnAddress(c)));
17
- const waiter = (0, misc_1.pendingPromise)();
18
- c === null || c === void 0 ? void 0 : c.socket.end(waiter.resolve);
19
- if (wait)
20
- await waiter;
18
+ if (c) {
19
+ const waiter = (0, misc_1.pendingPromise)();
20
+ c.socket.end(waiter.resolve);
21
+ (_a = c.ctx) === null || _a === void 0 ? void 0 : _a.res.end();
22
+ (_b = c.ctx) === null || _b === void 0 ? void 0 : _b.req.socket.end('');
23
+ if (wait)
24
+ await waiter;
25
+ }
21
26
  return { result: Boolean(c) };
22
27
  },
23
28
  get_connections({}, ctx) {
package/src/const.js CHANGED
@@ -38,7 +38,7 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
38
38
  exports.ORIGINAL_CWD = process.cwd();
39
39
  exports.HFS_STARTED = new Date();
40
40
  const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
41
- exports.BUILD_TIMESTAMP = "2023-03-07T12:16:23.728Z";
41
+ exports.BUILD_TIMESTAMP = "2023-03-10T14:41:56.924Z";
42
42
  const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
43
43
  exports.VERSION = pkg.version;
44
44
  exports.DAY = 86400000;
package/src/index.js CHANGED
@@ -43,6 +43,7 @@ function errorHandler(err) {
43
43
  if (const_1.DEV && code === 'ENOENT' && err.path.endsWith('sockjs-node'))
44
44
  return; // spam out dev stuff
45
45
  if (code === 'ECANCELED' || code === 'ECONNRESET' || code === 'ECONNABORTED' || code === 'EPIPE'
46
+ || code === 'ERR_STREAM_WRITE_AFTER_END' // happens disconnecting uploads, don't care
46
47
  || code === 'HPE_INVALID_EOF_STATE')
47
48
  return; // someone interrupted, don't care
48
49
  console.error('server error', err);
package/src/log.js CHANGED
@@ -28,6 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
30
  exports.log = exports.loggers = void 0;
31
+ const stream_1 = require("stream");
31
32
  const config_1 = require("./config");
32
33
  const fs_1 = require("fs");
33
34
  const util = __importStar(require("util"));
@@ -81,44 +82,47 @@ const logRotation = (0, config_1.defineConfig)('log_rotation', 'weekly');
81
82
  function log() {
82
83
  const debounce = lodash_1.default.debounce(cb => cb(), 1000);
83
84
  return async (ctx, next) => {
84
- var _a, _b;
85
- await next();
86
- const isError = ctx.status >= 400;
87
- const logger = isError && accessErrorLog || accessLogger;
88
- const rotate = (_a = logRotation.get()) === null || _a === void 0 ? void 0 : _a[0];
89
- let { stream, last, path } = logger;
90
- if (!stream)
91
- return;
92
85
  const now = new Date();
93
- const a = now.toString().split(' ');
94
- logger.last = now;
95
- if (rotate && last) { // rotation enabled and a file exists?
96
- const passed = Number(now) - Number(last)
97
- - 3600000; // be pessimistic and count a possible DST change
98
- if (rotate === 'm' && (passed >= 31 * const_1.DAY || now.getMonth() !== last.getMonth())
99
- || rotate === 'd' && (passed >= const_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
100
- || rotate === 'w' && (passed >= 7 * const_1.DAY || now.getDay() < last.getDay())) {
101
- stream.end();
102
- const postfix = last.getFullYear() + '-' + doubleDigit(last.getMonth() + 1) + '-' + doubleDigit(last.getDate());
103
- try { // other logging requests shouldn't happen while we are renaming. Since this is very infrequent we can tolerate solving this by making it sync.
104
- (0, fs_1.renameSync)(path, path + '-' + postfix);
105
- }
106
- catch (e) { // ok, rename failed, but this doesn't mean we ain't gonna log
107
- console.error(e);
86
+ await next();
87
+ console.debug(ctx.status, ctx.method, ctx.path);
88
+ Promise.race([(0, stream_1.once)(ctx.res, 'finish'), (0, stream_1.once)(ctx.res, 'close')]).then(() => {
89
+ var _a, _b, _c, _d;
90
+ const isError = ctx.status >= 400;
91
+ const logger = isError && accessErrorLog || accessLogger;
92
+ const rotate = (_a = logRotation.get()) === null || _a === void 0 ? void 0 : _a[0];
93
+ let { stream, last, path } = logger;
94
+ if (!stream)
95
+ return;
96
+ logger.last = now;
97
+ if (rotate && last) { // rotation enabled and a fµile exists?
98
+ const passed = Number(now) - Number(last)
99
+ - 3600000; // be pessimistic and count a possible DST change
100
+ if (rotate === 'm' && (passed >= 31 * const_1.DAY || now.getMonth() !== last.getMonth())
101
+ || rotate === 'd' && (passed >= const_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
102
+ || rotate === 'w' && (passed >= 7 * const_1.DAY || now.getDay() < last.getDay())) {
103
+ stream.end();
104
+ const postfix = last.getFullYear() + '-' + doubleDigit(last.getMonth() + 1) + '-' + doubleDigit(last.getDate());
105
+ try { // other logging requests shouldn't happen while we are renaming. Since this is very infrequent we can tolerate solving this by making it sync.
106
+ (0, fs_1.renameSync)(path, path + '-' + postfix);
107
+ }
108
+ catch (e) { // ok, rename failed, but this doesn't mean we ain't gonna log
109
+ console.error(e);
110
+ }
111
+ stream = logger.reopen(); // keep variable updated
112
+ if (!stream)
113
+ return;
108
114
  }
109
- stream = logger.reopen(); // keep variable updated
110
- if (!stream)
111
- return;
112
115
  }
113
- }
114
- const format = '%s - %s [%s] "%s %s HTTP/%s" %d %s\n'; // Apache's Common Log Format
115
- const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + ((_b = a[5]) === null || _b === void 0 ? void 0 : _b.slice(3));
116
- const user = (0, perm_1.getCurrentUsername)(ctx);
117
- events_1.default.emit(logger.name, Object.assign(lodash_1.default.pick(ctx, ['ip', 'method', 'status', 'length']), { user, ts: now, uri: ctx.path }));
118
- console.debug(ctx.status, ctx.method, ctx.path);
119
- debounce(() => // once in a while we check if the file is still good (not deleted, etc), or we'll reopen it
120
- (0, promises_1.stat)(logger.path).catch(() => logger.reopen())); // async = smoother but we may lose some entries
121
- stream.write(util.format(format, ctx.ip, user || '-', date, ctx.method, ctx.path, ctx.req.httpVersion, ctx.status, ctx.length ? ctx.length.toString() : '-'));
116
+ const format = '%s - %s [%s] "%s %s HTTP/%s" %d %s\n'; // Apache's Common Log Format
117
+ const a = now.toString().split(' ');
118
+ const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + ((_b = a[5]) === null || _b === void 0 ? void 0 : _b.slice(3));
119
+ const user = (0, perm_1.getCurrentUsername)(ctx);
120
+ const length = (_c = ctx.state.length) !== null && _c !== void 0 ? _c : ctx.length;
121
+ events_1.default.emit(logger.name, Object.assign(lodash_1.default.pick(ctx, ['ip', 'method', 'status']), { length, user, ts: now, uri: ctx.path }));
122
+ debounce(() => // once in a while we check if the file is still good (not deleted, etc), or we'll reopen it
123
+ (0, promises_1.stat)(logger.path).catch(() => logger.reopen())); // async = smoother but we may lose some entries
124
+ stream.write(util.format(format, ctx.ip, user || '-', date, ctx.method, ctx.path, ctx.req.httpVersion, ctx.status, (_d = length === null || length === void 0 ? void 0 : length.toString()) !== null && _d !== void 0 ? _d : '-'));
125
+ });
122
126
  };
123
127
  }
124
128
  exports.log = log;
package/src/misc.js CHANGED
@@ -104,7 +104,7 @@ onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP'], signal
104
104
  function onFirstEvent(emitter, events, cb) {
105
105
  let already = false;
106
106
  for (const e of events)
107
- emitter.on(e, (...args) => {
107
+ emitter.once(e, (...args) => {
108
108
  if (already)
109
109
  return;
110
110
  already = true;
package/src/throttler.js CHANGED
@@ -65,6 +65,8 @@ const throttler = async (ctx, next) => {
65
65
  ctx.body = ctx.body.pipe(ts);
66
66
  if (bak)
67
67
  ctx.response.length = bak;
68
+ ts.once('end', () => // in case of compressed response, we offer calculation of real size
69
+ ctx.state.length = ts.getBytesSent());
68
70
  };
69
71
  exports.throttler = throttler;
70
72
  function roundSpeed(n) {