hfs 0.26.9 → 0.29.0

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 (56) hide show
  1. package/README.md +17 -3
  2. package/admin/assets/index-cbb42a0e.js +415 -0
  3. package/admin/assets/index-f8049da8.css +1 -0
  4. package/{frontend/assets/sha512.6af42937.js → admin/assets/sha512-3273321f.js} +2 -2
  5. package/admin/index.html +2 -2
  6. package/frontend/assets/index-72e96bb2.js +85 -0
  7. package/frontend/assets/index-cbcc6ac5.css +1 -0
  8. package/{admin/assets/sha512.9dfe82e1.js → frontend/assets/sha512-2c2fa926.js} +2 -2
  9. package/frontend/index.html +3 -3
  10. package/package.json +8 -10
  11. package/plugins/vhosting/plugin.js +23 -20
  12. package/src/QuickZipStream.js +2 -25
  13. package/src/ThrottledStream.js +1 -1
  14. package/src/adminApis.js +6 -8
  15. package/src/api.accounts.js +10 -10
  16. package/src/api.auth.js +21 -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 +17 -19
  22. package/src/apiMiddleware.js +16 -9
  23. package/src/block.js +1 -0
  24. package/src/commands.js +1 -0
  25. package/src/config.js +3 -2
  26. package/src/connections.js +1 -1
  27. package/src/const.js +19 -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 +23 -2
  32. package/src/github.js +5 -1
  33. package/src/index.js +5 -3
  34. package/src/listen.js +6 -3
  35. package/src/log.js +6 -6
  36. package/src/middlewares.js +32 -26
  37. package/src/misc.js +27 -2
  38. package/src/perm.js +31 -29
  39. package/src/plugins.js +6 -8
  40. package/src/serveFile.js +15 -13
  41. package/src/serveGuiFiles.js +5 -28
  42. package/src/sse.js +3 -2
  43. package/src/throttler.js +1 -1
  44. package/src/update.js +3 -2
  45. package/src/upload.js +92 -0
  46. package/src/util-files.js +19 -13
  47. package/src/util-generators.js +1 -0
  48. package/src/util-http.js +3 -1
  49. package/src/util-os.js +41 -0
  50. package/src/vfs.js +44 -37
  51. package/src/watchLoad.js +1 -1
  52. package/src/zip.js +9 -6
  53. package/admin/assets/index.bb5198ec.js +0 -281
  54. package/admin/assets/index.dcc78777.css +0 -1
  55. package/frontend/assets/index.27a78796.js +0 -85
  56. package/frontend/assets/index.93366732.css +0 -1
package/src/vfs.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
  };
@@ -21,6 +21,7 @@ const WHO_ANY_ACCOUNT = '*';
21
21
  exports.defaultPerms = {
22
22
  can_see: WHO_ANYONE,
23
23
  can_read: WHO_ANYONE,
24
+ can_upload: WHO_NO_ONE,
24
25
  };
25
26
  exports.MIME_AUTO = 'auto';
26
27
  function inheritFromParent(parent, child) {
@@ -36,7 +37,7 @@ function inheritFromParent(parent, child) {
36
37
  child.mime || (child.mime = parent.mime);
37
38
  return child;
38
39
  }
39
- async function urlToNode(url, ctx, parent = exports.vfs) {
40
+ async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
40
41
  var _a;
41
42
  let initialSlashes = 0;
42
43
  while (url[initialSlashes] === '/')
@@ -48,25 +49,23 @@ async function urlToNode(url, ctx, parent = exports.vfs) {
48
49
  const rest = nextSlash < 0 ? '' : url.slice(nextSlash + 1, url.endsWith('/') ? -1 : undefined);
49
50
  if ((0, misc_1.dirTraversal)(name) || /[\/]/.test(name)) {
50
51
  if (ctx)
51
- ctx.status = 418;
52
+ ctx.status = const_1.HTTP_FOOL;
52
53
  return;
53
54
  }
54
- const parents = parent.parents || []; // don't waste time cloning the array, as we won't keep intermediate nodes
55
+ // does the tree node have a child that goes by this name?
56
+ const sameName = !const_1.IS_WINDOWS ? (x) => x === name // easy
57
+ : (0, misc_2.with_)(name.toLowerCase(), lc => (x) => x.toLowerCase() === lc);
58
+ const child = (_a = parent.children) === null || _a === void 0 ? void 0 : _a.find(x => sameName(getNodeName(x)));
55
59
  const ret = {
60
+ ...child,
61
+ original: child,
56
62
  isTemp: true,
57
- url: (0, misc_1.enforceFinal)('/', parent.url || '') + name,
58
- parents,
59
63
  };
60
- parents.push(parent);
61
64
  inheritFromParent(parent, ret);
62
65
  inheritMasks(ret, parent, name);
63
66
  applyMasks(ret, parent, name);
64
- // does the tree node have a child that goes by this name?
65
- const sameName = !const_1.IS_WINDOWS ? (x) => x === name // easy
66
- : (0, misc_2.with_)(name.toLowerCase(), lc => (x) => x.toLowerCase() === lc);
67
- const child = (_a = parent.children) === null || _a === void 0 ? void 0 : _a.find(x => sameName(getNodeName(x)));
68
67
  if (child) // yes
69
- return urlToNode(rest, ctx, Object.assign(ret, child, { original: child }));
68
+ return urlToNode(rest, ctx, ret, getRest);
70
69
  // not in the tree, we can see consider continuing on the disk
71
70
  if (!parent.source)
72
71
  return; // but then we need the current node to be linked to the disk, otherwise, we give up
@@ -83,13 +82,16 @@ async function urlToNode(url, ctx, parent = exports.vfs) {
83
82
  if (parent.default)
84
83
  inheritFromParent({ mime: { '*': exports.MIME_AUTO } }, ret);
85
84
  if (rest)
86
- return urlToNode(rest, ctx, ret);
85
+ return urlToNode(rest, ctx, ret, getRest);
87
86
  if (ret.source)
88
87
  try {
89
88
  await promises_1.default.stat(ret.source);
90
89
  } // check existence
91
90
  catch (_b) {
92
- return;
91
+ if (!getRest)
92
+ return;
93
+ getRest(onDisk);
94
+ return parent;
93
95
  }
94
96
  return ret;
95
97
  }
@@ -101,11 +103,17 @@ function saveVfs() {
101
103
  }
102
104
  exports.saveVfs = saveVfs;
103
105
  function getNodeName(node) {
104
- return node.name
105
- || node.source && (/^[a-zA-Z]:\\?$/.test(node.source) && node.source.slice(0, 2)
106
- || (0, path_1.basename)(node.source)
107
- || node.source)
108
- || ''; // should happen only for root
106
+ const { name, source: s } = node;
107
+ if (name)
108
+ return name;
109
+ if (!s)
110
+ return ''; // should happen only for root
111
+ if (/^[a-zA-Z]:\\?$/.test(s))
112
+ return s.slice(0, 2); // exclude trailing slash
113
+ const base = (0, path_1.basename)(s);
114
+ if (/^[./\\]*$/.test(base)) // if empty or special-chars-only
115
+ return (0, path_1.basename)((0, path_1.resolve)(s)); // resolve to try to get more
116
+ return base;
109
117
  }
110
118
  exports.getNodeName = getNodeName;
111
119
  async function nodeIsDirectory(node) {
@@ -115,18 +123,20 @@ exports.nodeIsDirectory = nodeIsDirectory;
115
123
  function hasPermission(node, perm, ctx) {
116
124
  var _a;
117
125
  return matchWho((_a = node[perm]) !== null && _a !== void 0 ? _a : exports.defaultPerms[perm], ctx)
118
- && (perm !== 'can_see' || hasPermission(node, 'can_read', ctx)); // for can_see you must also can_read
126
+ && (perm !== 'can_see' || hasPermission(node, 'can_read', ctx)); // can_see is used to hide something you nonetheless can_read, so you MUST also can_read
119
127
  }
120
128
  exports.hasPermission = hasPermission;
121
129
  async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
122
130
  var _a;
123
131
  const { children, source } = parent;
132
+ const took = prefixPath ? undefined : new Set();
124
133
  if (children)
125
- for (let idx = 0; idx < children.length; idx++) {
126
- const child = children[idx];
134
+ for (const child of children) {
135
+ const name = prefixPath + getNodeName(child);
136
+ took === null || took === void 0 ? void 0 : took.add(name);
127
137
  yield* workItem({
128
138
  ...child,
129
- name: prefixPath ? (prefixPath + getNodeName(child)) : child.name
139
+ name,
130
140
  }, depth > 0 && await nodeIsDirectory(child).catch(() => false));
131
141
  }
132
142
  if (!source)
@@ -135,9 +145,11 @@ async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
135
145
  for await (const path of (0, misc_1.dirStream)(source, depth)) {
136
146
  if (ctx === null || ctx === void 0 ? void 0 : ctx.req.aborted)
137
147
  return;
138
- const renamed = (_a = parent.rename) === null || _a === void 0 ? void 0 : _a[path];
148
+ const name = prefixPath + (((_a = parent.rename) === null || _a === void 0 ? void 0 : _a[path]) || path);
149
+ if (took === null || took === void 0 ? void 0 : took.has(name))
150
+ continue;
139
151
  yield* workItem({
140
- name: prefixPath + (renamed || path),
152
+ name,
141
153
  source: (0, path_1.join)(source, path),
142
154
  rename: renameUnderPath(parent.rename, path),
143
155
  });
@@ -151,12 +163,7 @@ async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
151
163
  const name = getNodeName(item);
152
164
  // we basename for depth>0 where we already have the rest of the path in the parent's url, and would be duplicated
153
165
  const virtualBasename = (0, path_1.basename)(name);
154
- const url = (0, misc_1.enforceFinal)('/', parent.url || '') + virtualBasename;
155
- Object.assign(item, {
156
- isTemp: true,
157
- url,
158
- parents: [...parent.parents || [], parent],
159
- });
166
+ item.isTemp = true;
160
167
  inheritFromParent(parent, item);
161
168
  applyMasks(item, parent, virtualBasename);
162
169
  if (ctx && !hasPermission(item, 'can_see', ctx))
@@ -183,11 +190,11 @@ function inheritMasks(item, parent, virtualBasename) {
183
190
  if (!masks)
184
191
  return;
185
192
  const o = {};
186
- for (const k in masks)
193
+ for (const [k, v] of Object.entries(masks))
187
194
  if (k.startsWith('**/'))
188
- o[k.slice(3)] = masks[k];
195
+ o[k.slice(3)] = v;
189
196
  else if (k.startsWith(virtualBasename + '/'))
190
- o[k.slice(virtualBasename.length + 1)] = masks[k];
197
+ o[k.slice(virtualBasename.length + 1)] = v;
191
198
  if (Object.keys(o).length)
192
199
  item.masks = o;
193
200
  }
@@ -206,7 +213,7 @@ function matchWho(who, ctx) {
206
213
  (0, misc_1.getOrSet)(ctx.state, 'usernames', () => (0, perm_1.getCurrentUsernameExpanded)(ctx)).some((u) => who.includes(u)))();
207
214
  }
208
215
  function cantReadStatusCode(node) {
209
- return node.can_read === false ? const_1.FORBIDDEN : const_1.UNAUTHORIZED;
216
+ return node.can_read === false ? const_1.HTTP_FORBIDDEN : const_1.HTTP_UNAUTHORIZED;
210
217
  }
211
218
  exports.cantReadStatusCode = cantReadStatusCode;
212
219
  events_1.default.on('accountRenamed', (from, to) => {
@@ -214,8 +221,8 @@ events_1.default.on('accountRenamed', (from, to) => {
214
221
  saveVfs();
215
222
  function recur(n) {
216
223
  var _a;
217
- replace(n.can_see);
218
- replace(n.can_read);
224
+ for (const k of (0, misc_1.typedKeys)(exports.defaultPerms))
225
+ replace(n[k]);
219
226
  if (n.masks)
220
227
  Object.values(n.masks).forEach(recur);
221
228
  (_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach(recur);
package/src/watchLoad.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/zip.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
  };
@@ -13,16 +13,19 @@ const promises_1 = __importDefault(require("fs/promises"));
13
13
  const config_1 = require("./config");
14
14
  const path_1 = require("path");
15
15
  const serveFile_1 = require("./serveFile");
16
+ const const_1 = require("./const");
16
17
  async function zipStreamFromFolder(node, ctx) {
17
- ctx.status = 200;
18
+ var _a;
19
+ ctx.status = const_1.HTTP_OK;
18
20
  ctx.mime = 'zip';
19
- const name = (0, vfs_1.getNodeName)(node);
20
- ctx.attachment((name || 'archive') + '.zip');
21
+ // ctx.query.list is undefined | string | string[]
22
+ const list = (_a = (0, misc_1.wantArray)(ctx.query.list)[0]) === null || _a === void 0 ? void 0 : _a.split('*'); // we are using * as separator because it cannot be used in a file name and doesn't need url encoding
23
+ const name = (list === null || list === void 0 ? void 0 : list.length) === 1 ? (0, path_1.basename)(list[0]) : (0, vfs_1.getNodeName)(node);
24
+ ctx.attachment(((0, misc_1.isWindowsDrive)(name) ? name[0] : (name || 'archive')) + '.zip');
21
25
  const filter = (0, misc_1.pattern2filter)(String(ctx.query.search || ''));
22
- const { list } = ctx.query;
23
26
  const walker = !list ? (0, vfs_1.walkNode)(node, ctx, Infinity)
24
27
  : (async function* () {
25
- for await (const el of String(list).split('*')) { // we are using * as separator because it cannot be used in a file name and doesn't need url encoding
28
+ for await (const el of list) {
26
29
  const subNode = await (0, vfs_1.urlToNode)(el, ctx, node);
27
30
  if (!subNode || !(0, vfs_1.hasPermission)(subNode, 'can_read', ctx))
28
31
  continue;