hfs 0.26.9 → 0.27.2

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 (54) hide show
  1. package/README.md +15 -2
  2. package/admin/assets/index-509bb1d6.js +415 -0
  3. package/admin/assets/index-60a380a7.css +1 -0
  4. package/{frontend/assets/sha512.6af42937.js → admin/assets/sha512-738f0943.js} +2 -2
  5. package/admin/index.html +2 -2
  6. package/frontend/assets/index-6e178dfd.css +1 -0
  7. package/frontend/assets/index-aea7654e.js +85 -0
  8. package/{admin/assets/sha512.9dfe82e1.js → frontend/assets/sha512-bf915587.js} +2 -2
  9. package/frontend/index.html +3 -3
  10. package/package.json +2 -6
  11. package/plugins/vhosting/plugin.js +23 -20
  12. package/src/QuickZipStream.js +1 -1
  13. package/src/ThrottledStream.js +1 -1
  14. package/src/adminApis.js +5 -5
  15. package/src/api.accounts.js +10 -10
  16. package/src/api.auth.js +17 -17
  17. package/src/api.file_list.js +13 -6
  18. package/src/api.helpers.js +6 -6
  19. package/src/api.monitor.js +2 -0
  20. package/src/api.plugins.js +1 -0
  21. package/src/api.vfs.js +15 -12
  22. package/src/apiMiddleware.js +7 -4
  23. package/src/block.js +1 -0
  24. package/src/commands.js +1 -0
  25. package/src/config.js +1 -1
  26. package/src/connections.js +1 -1
  27. package/src/const.js +18 -7
  28. package/src/crypt.js +1 -1
  29. package/src/debounceAsync.js +1 -0
  30. package/src/events.js +1 -1
  31. package/src/frontEndApis.js +1 -1
  32. package/src/github.js +3 -1
  33. package/src/index.js +3 -2
  34. package/src/listen.js +1 -1
  35. package/src/log.js +4 -4
  36. package/src/middlewares.js +30 -10
  37. package/src/misc.js +1 -1
  38. package/src/perm.js +31 -29
  39. package/src/plugins.js +1 -1
  40. package/src/serveFile.js +11 -11
  41. package/src/serveGuiFiles.js +4 -4
  42. package/src/sse.js +3 -2
  43. package/src/throttler.js +1 -1
  44. package/src/update.js +1 -0
  45. package/src/util-files.js +17 -2
  46. package/src/util-generators.js +1 -0
  47. package/src/util-http.js +3 -1
  48. package/src/vfs.js +29 -27
  49. package/src/watchLoad.js +1 -1
  50. package/src/zip.js +3 -2
  51. package/admin/assets/index.bb5198ec.js +0 -281
  52. package/admin/assets/index.dcc78777.css +0 -1
  53. package/frontend/assets/index.27a78796.js +0 -85
  54. package/frontend/assets/index.93366732.css +0 -1
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
@@ -24,11 +24,11 @@ function apiMiddleware(apis) {
24
24
  console.debug('API', ctx.method, ctx.path, { ...params });
25
25
  if (!apis.hasOwnProperty(ctx.path)) {
26
26
  ctx.body = 'invalid api';
27
- return ctx.status = 404;
27
+ return ctx.status = const_1.HTTP_NOT_FOUND;
28
28
  }
29
29
  const csrf = ctx.cookies.get('csrf');
30
30
  // we don't rely on SameSite cookie option because it's https-only
31
- let res = csrf && csrf !== params.csrf ? new ApiError(const_1.UNAUTHORIZED, 'csrf')
31
+ let res = csrf && csrf !== params.csrf ? new ApiError(const_1.HTTP_UNAUTHORIZED, 'csrf')
32
32
  : await apis[ctx.path](params || {}, ctx);
33
33
  if (isAsyncGenerator(res))
34
34
  res = (0, misc_1.asyncGeneratorToReadable)(res);
@@ -45,7 +45,7 @@ function apiMiddleware(apis) {
45
45
  }
46
46
  if (res instanceof Error) { // generic exception
47
47
  ctx.body = String(res);
48
- return ctx.status = 400;
48
+ return ctx.status = const_1.HTTP_BAD_REQUEST;
49
49
  }
50
50
  ctx.body = res;
51
51
  };
@@ -95,6 +95,9 @@ class SendListReadable extends stream_1.Readable {
95
95
  custom(data) {
96
96
  this._push(data);
97
97
  }
98
+ props(props) {
99
+ this._push({ props });
100
+ }
98
101
  error(msg, close = false) {
99
102
  this._push({ error: msg });
100
103
  this.lastError = msg;
package/src/block.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ // 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
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
5
  };
package/src/commands.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ // 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
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
5
  };
package/src/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
package/src/const.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -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.APP_PATH = exports.IS_WINDOWS = exports.UNAUTHORIZED = exports.FORBIDDEN = exports.NO_CONTENT = exports.METHOD_NOT_ALLOWED = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.SESSION_DURATION = exports.DAY = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
30
+ exports.APP_PATH = exports.IS_WINDOWS = exports.HTTP_SERVER_ERROR = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.SESSION_DURATION = exports.DAY = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
31
31
  const minimist_1 = __importDefault(require("minimist"));
32
32
  const fs = __importStar(require("fs"));
33
33
  const os_1 = require("os");
@@ -50,10 +50,21 @@ exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
50
50
  exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
51
51
  exports.API_URI = exports.SPECIAL_URI + 'api/';
52
52
  exports.PLUGINS_PUB_URI = exports.SPECIAL_URI + 'plugins/';
53
- exports.METHOD_NOT_ALLOWED = 405;
54
- exports.NO_CONTENT = 204;
55
- exports.FORBIDDEN = 403;
56
- exports.UNAUTHORIZED = 401;
53
+ exports.HTTP_OK = 200;
54
+ exports.HTTP_NO_CONTENT = 204;
55
+ exports.HTTP_PARTIAL_CONTENT = 206;
56
+ exports.HTTP_TEMPORARY_REDIRECT = 302;
57
+ exports.HTTP_NOT_MODIFIED = 304;
58
+ exports.HTTP_BAD_REQUEST = 400;
59
+ exports.HTTP_UNAUTHORIZED = 401;
60
+ exports.HTTP_FORBIDDEN = 403;
61
+ exports.HTTP_NOT_FOUND = 404;
62
+ exports.HTTP_METHOD_NOT_ALLOWED = 405;
63
+ exports.HTTP_NOT_ACCEPTABLE = 406;
64
+ exports.HTTP_CONFLICT = 409;
65
+ exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
66
+ exports.HTTP_FOOL = 418;
67
+ exports.HTTP_SERVER_ERROR = 500;
57
68
  exports.IS_WINDOWS = process.platform === 'win32';
58
69
  const IS_BINARY = !(0, path_1.basename)(process.argv0).includes('node'); // this won't be node if pkg was used
59
70
  exports.APP_PATH = (0, path_1.dirname)(IS_BINARY ? process.argv0 : __dirname);
@@ -62,7 +73,7 @@ if (exports.DEV)
62
73
  console.clear();
63
74
  else
64
75
  console.debug = () => { };
65
- console.log(`HFS ~ HTTP File Server - Copyright 2021-2022, Massimo Melina <a@rejetto.com>`);
76
+ console.log(`HFS ~ HTTP File Server - Copyright 2021-2023, Massimo Melina <a@rejetto.com>`);
66
77
  console.log(`License https://www.gnu.org/licenses/gpl-3.0.txt`);
67
78
  console.log('started', exports.HFS_STARTED.toLocaleString(), exports.DEV);
68
79
  console.log('version', exports.VERSION || '-');
package/src/crypt.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ // 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
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  // like lodash.debounce, but also avoids async invocations to overlap
4
5
  function debounceAsync(callback, wait = 100, { leading = false, maxWait = Infinity } = {}) {
package/src/events.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
package/src/github.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ // 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
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
5
  };
@@ -9,6 +10,7 @@ const misc_1 = require("./misc");
9
10
  const plugins_1 = require("./plugins");
10
11
  const apiMiddleware_1 = require("./apiMiddleware");
11
12
  const lodash_1 = __importDefault(require("lodash"));
13
+ const const_1 = require("./const");
12
14
  const DIST_ROOT = 'dist/';
13
15
  const downloading = {};
14
16
  function downloadProgress(id, status) {
@@ -20,7 +22,7 @@ function downloadProgress(id, status) {
20
22
  }
21
23
  async function downloadPlugin(repo, branch = '', overwrite) {
22
24
  if (downloading[repo])
23
- return new apiMiddleware_1.ApiError(409, "already downloading");
25
+ return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT, "already downloading");
24
26
  downloadProgress(repo, true);
25
27
  const rec = await getRepoInfo(repo);
26
28
  if (!branch)
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
4
4
  var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  return (mod && mod.__esModule) ? mod : { "default": mod };
6
6
  };
@@ -42,7 +42,8 @@ function errorHandler(err) {
42
42
  const { code } = err;
43
43
  if (const_1.DEV && code === 'ENOENT' && err.path.endsWith('sockjs-node'))
44
44
  return; // spam out dev stuff
45
- if (code === 'ECANCELED' || code === 'ECONNRESET' || code === 'ECONNABORTED' || code === 'EPIPE')
45
+ if (code === 'ECANCELED' || code === 'ECONNRESET' || code === 'ECONNABORTED' || code === 'EPIPE'
46
+ || code === 'HPE_INVALID_EOF_STATE')
46
47
  return; // someone interrupted, don't care
47
48
  console.error('server error', err);
48
49
  }
package/src/listen.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
package/src/log.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -35,7 +35,7 @@ const promises_1 = require("fs/promises");
35
35
  const const_1 = require("./const");
36
36
  const events_1 = __importDefault(require("./events"));
37
37
  const lodash_1 = __importDefault(require("lodash"));
38
- const path_1 = require("path");
38
+ const util_files_1 = require("./util-files");
39
39
  const perm_1 = require("./perm");
40
40
  class Logger {
41
41
  constructor(name) {
@@ -54,8 +54,8 @@ class Logger {
54
54
  this.last = stats.mtime || stats.ctime;
55
55
  }
56
56
  catch (_b) {
57
- await (0, promises_1.mkdir)((0, path_1.dirname)(path), { recursive: true })
58
- .catch(() => console.log("cannot create folder for", path));
57
+ if (await (0, util_files_1.prepareFolder)(path) === false)
58
+ console.log("cannot create folder for", path);
59
59
  }
60
60
  this.reopen();
61
61
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
@@ -22,6 +22,9 @@ const connections_1 = require("./connections");
22
22
  const basic_auth_1 = __importDefault(require("basic-auth"));
23
23
  const tssrp6a_1 = require("tssrp6a");
24
24
  const api_auth_1 = require("./api.auth");
25
+ const path_1 = require("path");
26
+ const fs_1 = require("fs");
27
+ const promises_1 = require("stream/promises");
25
28
  exports.gzipper = (0, koa_compress_1.default)({
26
29
  threshold: 2048,
27
30
  gzip: { flush: require('zlib').constants.Z_SYNC_FLUSH },
@@ -66,9 +69,17 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
66
69
  return ctx.redirect(const_1.ADMIN_URI);
67
70
  if (path.startsWith(const_1.ADMIN_URI))
68
71
  return serveAdminPrefixed(ctx, next);
72
+ if (ctx.method === 'PUT') { // curl -T file url/
73
+ const decPath = decodeURI(path);
74
+ let rest = (0, path_1.basename)(decPath);
75
+ const folder = await (0, vfs_1.urlToNode)((0, path_1.dirname)(decPath), ctx, vfs_1.vfs, v => rest = v + '/' + rest);
76
+ if (!folder)
77
+ return ctx.status = const_1.HTTP_NOT_FOUND;
78
+ return await receiveUpload(folder, rest, ctx.req, ctx);
79
+ }
69
80
  const node = await (0, vfs_1.urlToNode)(path, ctx);
70
81
  if (!node)
71
- return ctx.status = 404;
82
+ return ctx.status = const_1.HTTP_NOT_FOUND;
72
83
  const canRead = (0, vfs_1.hasPermission)(node, 'can_read', ctx);
73
84
  const isFolder = await (0, vfs_1.nodeIsDirectory)(node);
74
85
  if (isFolder && !path.endsWith('/'))
@@ -78,7 +89,7 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
78
89
  : next();
79
90
  if (!canRead) {
80
91
  ctx.status = (0, vfs_1.cantReadStatusCode)(node);
81
- if (ctx.status === const_1.FORBIDDEN)
92
+ if (ctx.status === const_1.HTTP_FORBIDDEN)
82
93
  return;
83
94
  const browserDetected = ctx.get('Upgrade-Insecure-Requests') || ctx.get('Sec-Fetch-Mode'); // ugh, heuristics
84
95
  if (!browserDetected) // we don't want to trigger basic authentication on browsers, it's meant for download managers only
@@ -99,6 +110,15 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
99
110
  return serveFrontendFiles(ctx, next);
100
111
  };
101
112
  exports.serveGuiAndSharedFiles = serveGuiAndSharedFiles;
113
+ async function receiveUpload(base, path, stream, ctx) {
114
+ if (!base.source || !(0, vfs_1.hasPermission)(base, 'can_upload', ctx))
115
+ return ctx.status = base.can_upload === false ? const_1.HTTP_FORBIDDEN : const_1.HTTP_UNAUTHORIZED;
116
+ path = (0, path_1.join)(base.source, path);
117
+ await (0, misc_1.prepareFolder)(path);
118
+ const dest = (0, fs_1.createWriteStream)(path);
119
+ await (0, promises_1.pipeline)(stream, dest);
120
+ ctx.body = '{}';
121
+ }
102
122
  let proxyDetected = false;
103
123
  const someSecurity = async (ctx, next) => {
104
124
  ctx.request.ip = (0, connections_1.normalizeIp)(ctx.ip);
@@ -108,14 +128,14 @@ const someSecurity = async (ctx, next) => {
108
128
  if (const_1.DEV && proxy && [process.env.FRONTEND_PROXY, process.env.ADMIN_PROXY].includes(ctx.get('X-Forwarded-port')))
109
129
  proxy = '';
110
130
  if ((0, misc_1.dirTraversal)(decodeURI(ctx.path)))
111
- return ctx.status = 418;
131
+ return ctx.status = const_1.HTTP_FOOL;
112
132
  if ((0, block_1.applyBlock)(ctx.socket, ctx.ip))
113
133
  return;
114
134
  proxyDetected || (proxyDetected = proxy > '');
115
135
  ctx.state.proxiedFor = proxy;
116
136
  }
117
137
  catch (_a) {
118
- return ctx.status = 418;
138
+ return ctx.status = const_1.HTTP_FOOL;
119
139
  }
120
140
  return next();
121
141
  };
@@ -153,17 +173,17 @@ async function srpCheck(username, password) {
153
173
  }
154
174
  // unify get/post parameters, with JSON decoding to not be limited to strings
155
175
  const paramsDecoder = async (ctx, next) => {
156
- ctx.params = ctx.method === 'POST' ? (0, misc_1.tryJson)(await getReqData(ctx.req))
176
+ ctx.params = ctx.method === 'POST' ? (0, misc_1.tryJson)(await stream2string(ctx.req))
157
177
  : (0, misc_1.objSameKeys)(ctx.query, x => Array.isArray(x) ? x : (0, misc_1.tryJson)(x));
158
178
  await next();
159
179
  };
160
180
  exports.paramsDecoder = paramsDecoder;
161
- async function getReqData(req) {
181
+ async function stream2string(stream) {
162
182
  return new Promise((resolve, reject) => {
163
183
  let data = '';
164
- req.on('data', chunk => data += chunk);
165
- req.on('error', reject);
166
- req.on('end', () => {
184
+ stream.on('data', chunk => data += chunk);
185
+ stream.on('error', reject);
186
+ stream.on('end', () => {
167
187
  try {
168
188
  resolve(data);
169
189
  }
package/src/misc.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
package/src/perm.js CHANGED
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.anyAccountCanLoginAdmin = exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.updateAccount = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.getCurrentUsernameExpanded = exports.getCurrentUsername = exports.getAccounts = void 0;
7
+ exports.anyAccountCanLoginAdmin = exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.getCurrentUsernameExpanded = exports.getCurrentUsername = void 0;
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
9
  const crypt_1 = require("./crypt");
10
10
  const misc_1 = require("./misc");
@@ -12,10 +12,6 @@ const config_1 = require("./config");
12
12
  const tssrp6a_1 = require("tssrp6a");
13
13
  const events_1 = __importDefault(require("./events"));
14
14
  let accounts = {};
15
- function getAccounts() {
16
- return accounts;
17
- }
18
- exports.getAccounts = getAccounts;
19
15
  function getCurrentUsername(ctx) {
20
16
  var _a;
21
17
  return ((_a = ctx.state.account) === null || _a === void 0 ? void 0 : _a.username) || '';
@@ -63,27 +59,34 @@ async function updateAccount(account, changer) {
63
59
  console.log('please reset password for account', username);
64
60
  process.exit(1);
65
61
  }
66
- if (account.belongs)
67
- account.belongs = (0, misc_1.wantArray)(account.belongs).filter(b => b in accounts // at this stage the group record may still be null if specified later in the file
68
- || console.error(`account ${username} belongs to non-existing ${b}`));
62
+ if (account.belongs) {
63
+ account.belongs = (0, misc_1.wantArray)(account.belongs);
64
+ lodash_1.default.remove(account.belongs, b => {
65
+ if (b in accounts)
66
+ return;
67
+ console.error(`account ${username} belongs to non-existing ${b}`);
68
+ return true;
69
+ });
70
+ }
69
71
  if (was !== JSON.stringify(account))
70
72
  saveAccountsAsap();
71
73
  }
72
74
  exports.updateAccount = updateAccount;
73
- const saveAccountsAsap = config_1.saveConfigAsap;
74
- const accountsConfig = (0, config_1.defineConfig)('accounts', {});
75
- accountsConfig.sub(async (v) => {
76
- // we should validate content here
77
- accounts = v; // keep local reference
78
- await Promise.all(lodash_1.default.map(accounts, async (rec, k) => {
75
+ const saveAccountsAsap = () => { (0, config_1.saveConfigAsap)().then(); };
76
+ exports.accountsConfig = (0, config_1.defineConfig)('accounts', {});
77
+ exports.accountsConfig.sub(obj => {
78
+ // consider some validation here
79
+ lodash_1.default.each(accounts = obj, (rec, k) => {
79
80
  const norm = normalizeUsername(k);
80
- if (!rec) // an empty object in yaml is stored as null
81
- rec = accounts[norm] = { username: norm };
82
- else if ((0, misc_1.objRenameKey)(accounts, k, norm))
83
- saveAccountsAsap();
84
- (0, misc_1.setHidden)(rec, { username: norm });
85
- await updateAccount(rec); // work password fields
86
- }));
81
+ if ((rec === null || rec === void 0 ? void 0 : rec.username) !== norm) {
82
+ if (!rec) // an empty object in yaml is parsed as null
83
+ rec = obj[norm] = { username: norm };
84
+ else if ((0, misc_1.objRenameKey)(obj, k, norm))
85
+ saveAccountsAsap();
86
+ (0, misc_1.setHidden)(rec, { username: norm });
87
+ }
88
+ updateAccount(rec).then(); // work password fields
89
+ });
87
90
  });
88
91
  function normalizeUsername(username) {
89
92
  return username.toLocaleLowerCase();
@@ -120,8 +123,7 @@ function addAccount(username, props) {
120
123
  return;
121
124
  const filteredProps = lodash_1.default.pickBy(lodash_1.default.pick(props, assignableProps), Boolean);
122
125
  const copy = (0, misc_1.setHidden)(filteredProps, { username }); // have the field in the object but hidden so that stringification won't include it
123
- accountsConfig.set(accounts => Object.assign(accounts, { [username]: copy }));
124
- saveAccountsAsap().then();
126
+ exports.accountsConfig.set(accounts => Object.assign(accounts, { [username]: copy }));
125
127
  return copy;
126
128
  }
127
129
  exports.addAccount = addAccount;
@@ -136,15 +138,15 @@ function setAccount(username, changes) {
136
138
  Object.assign(acc, rest);
137
139
  if (changes.username)
138
140
  renameAccount(username, changes.username);
139
- saveAccountsAsap().then();
141
+ saveAccountsAsap();
140
142
  return acc;
141
143
  }
142
144
  exports.setAccount = setAccount;
143
145
  function delAccount(username) {
144
146
  if (!getAccount(username))
145
147
  return false;
146
- accountsConfig.set(accounts => Object.assign(accounts, { [normalizeUsername(username)]: undefined }));
147
- saveAccountsAsap().then();
148
+ exports.accountsConfig.set(accounts => Object.assign(accounts, { [normalizeUsername(username)]: undefined }));
149
+ saveAccountsAsap();
148
150
  return true;
149
151
  }
150
152
  exports.delAccount = delAccount;
@@ -172,10 +174,10 @@ function accountCanLogin(account) {
172
174
  }
173
175
  exports.accountCanLogin = accountCanLogin;
174
176
  function accountCanLoginAdmin(account) {
175
- return accountCanLogin(account) && getFromAccount(account, a => a.admin);
177
+ return accountCanLogin(account) && Boolean(getFromAccount(account, a => a.admin));
176
178
  }
177
179
  exports.accountCanLoginAdmin = accountCanLoginAdmin;
178
180
  function anyAccountCanLoginAdmin() {
179
- return Object.values(accounts).find(accountCanLoginAdmin);
181
+ return Boolean(lodash_1.default.find(exports.accountsConfig.get(), accountCanLoginAdmin));
180
182
  }
181
183
  exports.anyAccountCanLoginAdmin = anyAccountCanLoginAdmin;
package/src/plugins.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
package/src/serveFile.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
@@ -27,7 +27,7 @@ function serveFileNode(node) {
27
27
  const ref = (_a = /\/\/([^:/]+)/.exec(ctx.get('referer'))) === null || _a === void 0 ? void 0 : _a[1]; // extract host from url
28
28
  if (ref && ref !== host() // automatic accept if referer is basically the hosting domain
29
29
  && !(0, micromatch_1.isMatch)(ref, allowed))
30
- return ctx.status = const_1.FORBIDDEN;
30
+ return ctx.status = const_1.HTTP_FORBIDDEN;
31
31
  function host() {
32
32
  const s = ctx.get('host');
33
33
  return s[0] === '[' ? s.slice(1, s.indexOf(']')) : s === null || s === void 0 ? void 0 : s.split(':')[0];
@@ -50,26 +50,26 @@ function serveFile(source, mime, content) {
50
50
  if (mime)
51
51
  ctx.type = mime;
52
52
  if (ctx.method === 'OPTIONS') {
53
- ctx.status = const_1.NO_CONTENT;
53
+ ctx.status = const_1.HTTP_NO_CONTENT;
54
54
  ctx.set({ Allow: 'OPTIONS, GET, HEAD' });
55
55
  return;
56
56
  }
57
57
  if (ctx.method !== 'GET')
58
- return ctx.status = const_1.METHOD_NOT_ALLOWED;
58
+ return ctx.status = const_1.HTTP_METHOD_NOT_ALLOWED;
59
59
  try {
60
60
  const stats = await (0, util_1.promisify)(fs_1.stat)(source); // using fs's function instead of fs/promises, because only the former is supported by pkg
61
61
  ctx.set('Last-Modified', stats.mtime.toUTCString());
62
62
  ctx.fileSource = source;
63
- ctx.status = 200;
63
+ ctx.status = const_1.HTTP_OK;
64
64
  if (ctx.fresh)
65
- return ctx.status = 304;
65
+ return ctx.status = const_1.HTTP_NOT_MODIFIED;
66
66
  if (content !== undefined)
67
67
  return ctx.body = content;
68
68
  const range = getRange(ctx, stats.size);
69
69
  ctx.body = (0, fs_1.createReadStream)(source, range);
70
70
  }
71
71
  catch (_a) {
72
- return ctx.status = 404;
72
+ return ctx.status = const_1.HTTP_NOT_FOUND;
73
73
  }
74
74
  };
75
75
  }
@@ -83,21 +83,21 @@ function getRange(ctx, totalSize) {
83
83
  }
84
84
  const ranges = range.split('=')[1];
85
85
  if (ranges.includes(','))
86
- return ctx.throw(400, 'multi-range not supported');
86
+ return ctx.throw(const_1.HTTP_BAD_REQUEST, 'multi-range not supported');
87
87
  let bytes = ranges === null || ranges === void 0 ? void 0 : ranges.split('-');
88
88
  if (!(bytes === null || bytes === void 0 ? void 0 : bytes.length))
89
- return ctx.throw(400, 'bad range');
89
+ return ctx.throw(const_1.HTTP_BAD_REQUEST, 'bad range');
90
90
  const max = totalSize - 1;
91
91
  const start = bytes[0] ? Number(bytes[0]) : Math.max(0, totalSize - Number(bytes[1])); // a negative start is relative to the end
92
92
  const end = bytes[0] ? Number(bytes[1] || max) : max;
93
93
  // we don't support last-bytes without knowing max
94
94
  if (isNaN(end) && isNaN(max) || end > max || start > max) {
95
- ctx.status = 416;
95
+ ctx.status = const_1.HTTP_RANGE_NOT_SATISFIABLE;
96
96
  ctx.set('Content-Range', `bytes ${totalSize}`);
97
97
  ctx.body = 'Requested Range Not Satisfiable';
98
98
  return;
99
99
  }
100
- ctx.status = 206;
100
+ ctx.status = const_1.HTTP_PARTIAL_CONTENT;
101
101
  ctx.set('Content-Range', `bytes ${start}-${isNaN(end) ? '' : end}/${isNaN(totalSize) ? '*' : totalSize}`);
102
102
  ctx.response.length = end - start + 1;
103
103
  return { start, end };
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -43,12 +43,12 @@ function serveStatic(uri) {
43
43
  const cache = {};
44
44
  return async (ctx, next) => {
45
45
  if (ctx.method === 'OPTIONS') {
46
- ctx.status = const_1.NO_CONTENT;
46
+ ctx.status = const_1.HTTP_NO_CONTENT;
47
47
  ctx.set({ Allow: 'OPTIONS, GET' });
48
48
  return;
49
49
  }
50
50
  if (ctx.method !== 'GET')
51
- return ctx.status = const_1.METHOD_NOT_ALLOWED;
51
+ return ctx.status = const_1.HTTP_METHOD_NOT_ALLOWED;
52
52
  const serveApp = shouldServeApp(ctx);
53
53
  const fullPath = (0, path_1.join)(__dirname, '..', DEV_STATIC, folder, serveApp ? '/index.html' : ctx.path);
54
54
  const content = await (0, misc_1.getOrSet)(cache, ctx.path, async () => {
@@ -57,7 +57,7 @@ function serveStatic(uri) {
57
57
  : adjustBundlerLinks(ctx.path, uri, data);
58
58
  });
59
59
  if (content === null)
60
- return ctx.status = 404;
60
+ return ctx.status = const_1.HTTP_NOT_FOUND;
61
61
  if (!serveApp)
62
62
  return (0, serveFile_1.serveFile)(fullPath, 'auto', content)(ctx, next);
63
63
  // we don't cache the index as it's small and may prevent plugins change to apply
package/src/sse.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const stream_1 = require("stream");
5
+ const const_1 = require("./const");
5
6
  function createSSE(ctx) {
6
7
  const { socket } = ctx.req;
7
8
  socket.setTimeout(0);
@@ -13,7 +14,7 @@ function createSSE(ctx) {
13
14
  'Connection': 'keep-alive',
14
15
  'X-Accel-Buffering': 'no', // avoid buffering when reverse-proxied through nginx
15
16
  });
16
- ctx.status = 200;
17
+ ctx.status = const_1.HTTP_OK;
17
18
  return ctx.body = new stream_1.Transform({
18
19
  objectMode: true,
19
20
  transform(chunk, encoding, cb) {
package/src/throttler.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
package/src/update.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ // 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
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.update = exports.getUpdate = void 0;
4
5
  const github_1 = require("./github");