hfs 0.57.0-beta2 → 0.57.0-beta4

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.
package/src/perm.js CHANGED
@@ -4,7 +4,7 @@ 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.changeSrpHelper = exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.createAdmin = exports.saveSrpInfo = exports.getAccount = exports.getUsernames = exports.ctxBelongsTo = exports.expandUsername = void 0;
7
+ exports.changeSrpHelper = exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accounts = exports.updateAccount = exports.createAdmin = exports.saveSrpInfo = exports.getAccount = exports.getUsernames = exports.ctxBelongsTo = exports.expandUsername = void 0;
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
9
  const misc_1 = require("./misc");
10
10
  const config_1 = require("./config");
@@ -12,7 +12,6 @@ const tssrp6a_1 = require("tssrp6a");
12
12
  const events_1 = __importDefault(require("./events"));
13
13
  const apiMiddleware_1 = require("./apiMiddleware");
14
14
  const auth_1 = require("./auth");
15
- let accounts = {};
16
15
  // provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
17
16
  function expandUsername(who) {
18
17
  const ret = [];
@@ -36,13 +35,13 @@ function ctxBelongsTo(ctx, usernames) {
36
35
  }
37
36
  exports.ctxBelongsTo = ctxBelongsTo;
38
37
  function getUsernames() {
39
- return Object.keys(accounts);
38
+ return Object.keys(exports.accounts.get());
40
39
  }
41
40
  exports.getUsernames = getUsernames;
42
41
  function getAccount(username, normalize = true) {
43
42
  if (normalize)
44
43
  username = normalizeUsername(username);
45
- return username ? accounts[username] : undefined;
44
+ return username ? exports.accounts.get()[username] : undefined;
46
45
  }
47
46
  exports.getAccount = getAccount;
48
47
  function saveSrpInfo(account, salt, verifier) {
@@ -83,7 +82,7 @@ async function updateAccount(account, change) {
83
82
  if (account.belongs) {
84
83
  account.belongs = (0, misc_1.wantArray)(account.belongs);
85
84
  lodash_1.default.remove(account.belongs, b => {
86
- if (accounts.hasOwnProperty(b))
85
+ if (exports.accounts.get().hasOwnProperty(b))
87
86
  return;
88
87
  console.error(`account ${username} belongs to non-existing ${b}`);
89
88
  return true;
@@ -99,10 +98,10 @@ async function updateAccount(account, change) {
99
98
  }
100
99
  exports.updateAccount = updateAccount;
101
100
  const saveAccountsAsap = config_1.saveConfigAsap;
102
- exports.accountsConfig = (0, config_1.defineConfig)('accounts', {});
103
- exports.accountsConfig.sub(obj => {
104
- // consider some validation here
105
- lodash_1.default.each(accounts = obj, (rec, k) => {
101
+ exports.accounts = (0, config_1.defineConfig)('accounts', {});
102
+ exports.accounts.sub(lodash_1.default.debounce(obj => {
103
+ // consider some validation here, in case of manual edit of the config
104
+ lodash_1.default.each(obj, (rec, k) => {
106
105
  const norm = normalizeUsername(k);
107
106
  if ((rec === null || rec === void 0 ? void 0 : rec.username) !== norm) {
108
107
  if (!rec) // an empty object in yaml is parsed as null
@@ -113,32 +112,32 @@ exports.accountsConfig.sub(obj => {
113
112
  }
114
113
  void updateAccount(rec, {}); // work fields
115
114
  });
116
- });
115
+ })); // don't trigger in the middle of a series of deletion, as we may have an inconsistent state
117
116
  function normalizeUsername(username) {
118
117
  return username.toLocaleLowerCase();
119
118
  }
120
119
  exports.normalizeUsername = normalizeUsername;
121
120
  function renameAccount(from, to) {
121
+ var _a;
122
122
  from = normalizeUsername(from);
123
+ const as = exports.accounts.get();
123
124
  to = normalizeUsername(to);
124
- if (!to || !accounts[from] || accounts[to])
125
+ if (!to || !as[from] || as[to])
125
126
  return false;
126
127
  if (to === from)
127
128
  return true;
128
- (0, misc_1.objRenameKey)(accounts, from, to);
129
- updateReferences();
129
+ (0, misc_1.objRenameKey)(as, from, to);
130
+ (0, misc_1.setHidden)(as[to], { username: to });
131
+ // update references
132
+ for (const a of Object.values(as)) {
133
+ const idx = (_a = a.belongs) === null || _a === void 0 ? void 0 : _a.indexOf(from);
134
+ if (idx !== undefined && idx >= 0)
135
+ a.belongs[idx] = to;
136
+ }
137
+ exports.accounts.set(as);
138
+ events_1.default.emit('accountRenamed', from, to); // everybody, take care of your stuff
130
139
  saveAccountsAsap();
131
140
  return true;
132
- function updateReferences() {
133
- var _a;
134
- (0, misc_1.setHidden)(accounts[to], { username: to });
135
- for (const a of Object.values(accounts)) {
136
- const idx = (_a = a.belongs) === null || _a === void 0 ? void 0 : _a.indexOf(from);
137
- if (idx !== undefined && idx >= 0)
138
- a.belongs[idx] = to;
139
- }
140
- events_1.default.emit('accountRenamed', from, to); // everybody, take care of your stuff
141
- }
142
141
  }
143
142
  exports.renameAccount = renameAccount;
144
143
  function addAccount(username, props, updateExisting = false) {
@@ -150,14 +149,14 @@ function addAccount(username, props, updateExisting = false) {
150
149
  return;
151
150
  account = (0, misc_1.setHidden)(account || {}, { username }); // hidden so that stringification won't include it
152
151
  Object.assign(account, lodash_1.default.pickBy(props, Boolean));
153
- exports.accountsConfig.set(accounts => Object.assign(accounts, { [username]: account }));
152
+ exports.accounts.set(accounts => Object.assign(accounts, { [username]: account }));
154
153
  return updateAccount(account, account).then(() => account);
155
154
  }
156
155
  exports.addAccount = addAccount;
157
156
  function delAccount(username) {
158
157
  if (!getAccount(username))
159
158
  return false;
160
- exports.accountsConfig.set(accounts => lodash_1.default.omit(accounts, normalizeUsername(username)));
159
+ exports.accounts.set(x => lodash_1.default.omit(x, normalizeUsername(username)));
161
160
  saveAccountsAsap();
162
161
  return true;
163
162
  }
package/src/plugins.js CHANGED
@@ -209,7 +209,7 @@ const pluginsMiddleware = async (ctx, next) => {
209
209
  warnOnce(`plugin ${id} is using deprecated API (return true on middleware) and may not work with future versions (check for an update to "${id}")`);
210
210
  }
211
211
  // don't just check ctx.isStopped, as the async plugin that called ctx.stop will reach here after sync ones
212
- if (res === true && !ctx.pluginBlockedRequest)
212
+ if (ctx.isStopped && !ctx.pluginBlockedRequest)
213
213
  console.debug("plugin blocked request", ctx.pluginBlockedRequest = id);
214
214
  if (typeof res === 'function')
215
215
  after[id] = res;
@@ -219,7 +219,7 @@ const pluginsMiddleware = async (ctx, next) => {
219
219
  }
220
220
  }));
221
221
  // expose public plugins' files`
222
- if (!ctx.pluginBlockedRequest) {
222
+ if (!ctx.isStopped) {
223
223
  const { path } = ctx;
224
224
  if (path.startsWith(const_1.PLUGINS_PUB_URI)) {
225
225
  const a = path.substring(const_1.PLUGINS_PUB_URI.length).split('/');
package/src/roots.js CHANGED
@@ -12,7 +12,7 @@ const lodash_1 = __importDefault(require("lodash"));
12
12
  exports.roots = (0, config_1.defineConfig)(misc_1.CFG.roots, {}, map => {
13
13
  const list = Object.keys(map);
14
14
  const matchers = list.map(hostMask => (0, misc_1.makeMatcher)(hostMask));
15
- const values = Object.values(map);
15
+ const values = Object.values(map).map(x => (0, misc_1.enforceFinal)('/', (0, misc_1.enforceStarting)('/', x)));
16
16
  return (host) => values[matchers.findIndex(m => m(host))];
17
17
  });
18
18
  const forceAddress = (0, config_1.defineConfig)(misc_1.CFG.force_address, false);
@@ -35,7 +35,7 @@ const rootsMiddleware = (ctx, next) => (() => {
35
35
  }
36
36
  if (lodash_1.default.isEmpty(exports.roots.get()))
37
37
  return;
38
- const root = (_a = exports.roots.compiled()) === null || _a === void 0 ? void 0 : _a(ctx.host);
38
+ const root = ctx.state.root = (_a = exports.roots.compiled()) === null || _a === void 0 ? void 0 : _a(ctx.host);
39
39
  if (!ctx.state.skipFilters && forceAddress.get())
40
40
  if (root === undefined && !(0, misc_1.isLocalHost)(ctx) && ctx.host !== listen_1.baseUrl.compiled()) {
41
41
  (0, connections_1.disconnect)(ctx, forceAddress.key());
package/src/serveFile.js CHANGED
@@ -121,9 +121,10 @@ exports.monitorAsDownload = monitorAsDownload;
121
121
  function applyRange(ctx, totalSize = ctx.response.length) {
122
122
  ctx.set('Accept-Ranges', 'bytes');
123
123
  const { range } = ctx.request.header;
124
- if (!range) {
124
+ if (!range || isNaN(totalSize)) {
125
125
  ctx.state.includesLastByte = true;
126
- ctx.response.length = totalSize;
126
+ if (!isNaN(totalSize))
127
+ ctx.response.length = totalSize;
127
128
  return;
128
129
  }
129
130
  const [unit, ranges] = range.split('=');
@@ -60,7 +60,7 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
60
60
  if (ctx.method === 'PUT') { // curl -T file url/
61
61
  const decPath = decodeURIComponent(path);
62
62
  let rest = (0, path_1.basename)(decPath);
63
- const folderUri = (0, path_1.dirname)(path);
63
+ const folderUri = (0, misc_1.pathEncode)((0, path_1.dirname)(decPath)); // re-encode to get readable urls
64
64
  const folder = await (0, vfs_1.urlToNode)(folderUri, ctx, vfs_1.vfs, v => rest = v + '/' + rest);
65
65
  if (!folder)
66
66
  return (0, errorPages_1.sendErrorPage)(ctx, cross_const_1.HTTP_NOT_FOUND);
@@ -77,7 +77,7 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
77
77
  }
78
78
  return;
79
79
  }
80
- if (/^\/favicon.ico(\??.*)/.test(ctx.originalUrl) && adminApis_1.favicon.get()) // originalUrl to not be subject to changes (vhosting plugin)
80
+ if (/^\/favicon.ico(\??.*)/.test(ctx.originalUrl) && adminApis_1.favicon.get() && ctx.method === 'GET') // originalUrl to not be subject to changes (vhosting plugin)
81
81
  return (0, serveFile_1.serveFile)(ctx, adminApis_1.favicon.get());
82
82
  let node = await (0, vfs_1.urlToNode)(path, ctx);
83
83
  if (!node)
@@ -74,9 +74,9 @@ async function treatIndex(ctx, filesUri, body) {
74
74
  const plugins = Object.fromEntries((0, misc_1.onlyTruthy)((0, plugins_1.mapPlugins)((pl, name) => {
75
75
  var _a, _b;
76
76
  let configs = (0, misc_1.newObj)((0, plugins_1.getPluginConfigFields)(name), (v, k, skip) => {
77
- var _a, _b, _c, _d, _e;
77
+ var _a, _b, _c;
78
78
  return !v.frontend ? skip() :
79
- ((_c = (_b = (_a = plugins_1.pluginsConfig.get()) === null || _a === void 0 ? void 0 : _a[name]) === null || _b === void 0 ? void 0 : _b[k]) !== null && _c !== void 0 ? _c : (_e = (_d = pl.getData().config) === null || _d === void 0 ? void 0 : _d[k]) === null || _e === void 0 ? void 0 : _e.defaultValue);
79
+ adjustValueByConfig((_b = (_a = plugins_1.pluginsConfig.get()) === null || _a === void 0 ? void 0 : _a[name]) === null || _b === void 0 ? void 0 : _b[k], (_c = pl.getData().config) === null || _c === void 0 ? void 0 : _c[k]);
80
80
  });
81
81
  configs = ((_b = (_a = (0, plugins_1.getPluginInfo)(name)).onFrontendConfig) === null || _b === void 0 ? void 0 : _b.call(_a, configs)) || configs;
82
82
  return !lodash_1.default.isEmpty(configs) && [name, configs];
@@ -142,6 +142,24 @@ async function treatIndex(ctx, filesUri, body) {
142
142
  return (0, customHtml_1.getSection)('bottom') + all;
143
143
  return all; // unchanged
144
144
  });
145
+ function adjustValueByConfig(v, cfg) {
146
+ v !== null && v !== void 0 ? v : (v = cfg.defaultValue);
147
+ const { type } = cfg;
148
+ if (v && type === 'vfs_path') {
149
+ v = (0, misc_1.enforceStarting)('/', v);
150
+ const { root } = ctx.state;
151
+ if (root)
152
+ if (v.startsWith(root))
153
+ v = v.slice(root.length - 1);
154
+ else
155
+ return;
156
+ if (ctx.state.revProxyPath)
157
+ v = ctx.state.revProxyPath + v;
158
+ }
159
+ else if (type === 'array' && Array.isArray(v))
160
+ v = v.map(x => (0, misc_1.objSameKeys)(x, (xv, xk) => adjustValueByConfig(xv, cfg.fields[xk])));
161
+ return v;
162
+ }
145
163
  }
146
164
  function serializeCss(v) {
147
165
  var _a;
package/src/util-http.js CHANGED
@@ -45,7 +45,7 @@ function httpStream(url, { body, jar, noRedirect, httpThrow, proxy, ...options }
45
45
  if (jar)
46
46
  options.headers.cookie = lodash_1.default.map(jar, (v, k) => `${k}=${v}; `).join('')
47
47
  + (options.headers.cookie || ''); // preserve parameter
48
- Object.assign(options, lodash_1.default.pick((0, node_url_1.parse)(proxy || url), ['hostname', 'port', 'path', 'protocol']));
48
+ Object.assign(options, lodash_1.default.pick((0, node_url_1.parse)(proxy || url), ['hostname', 'port', 'path', 'protocol', 'auth']));
49
49
  if (proxy) {
50
50
  options.path = url;
51
51
  (_d = (_g = options.headers).host) !== null && _d !== void 0 ? _d : (_g.host = (0, node_url_1.parse)(url).host || undefined);
package/src/vfs.js CHANGED
@@ -250,6 +250,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
250
250
  const stream = new node_stream_1.Readable({
251
251
  objectMode: true,
252
252
  async read() {
253
+ var _a;
253
254
  if (started)
254
255
  return; // for simplicity, we care about starting, and never suspend
255
256
  started = true;
@@ -266,11 +267,11 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
266
267
  const item = { ...child, name };
267
268
  if (await cantSee(item))
268
269
  continue;
269
- if (item.source) // real items must be accessible
270
+ if (item.source && !((_a = item.children) === null || _a === void 0 ? void 0 : _a.length)) // real items must be accessible, unless there's more to it
270
271
  try {
271
272
  await promises_1.default.access(item.source);
272
273
  }
273
- catch (_a) {
274
+ catch (_b) {
274
275
  continue;
275
276
  }
276
277
  const isFolder = await nodeIsDirectory(child);
package/src/zip.js CHANGED
@@ -82,7 +82,6 @@ async function zipStreamFromFolder(node, ctx) {
82
82
  const zip = new QuickZipStream_1.QuickZipStream(mappedWalker);
83
83
  const time = 1000 * zipSeconds.get();
84
84
  const size = await zip.calculateSize(time);
85
- ctx.response.length = size;
86
85
  const range = (0, serveFile_1.applyRange)(ctx, size); // keep var size as ctx.response.length won't preserve a NaN
87
86
  if (ctx.status >= 400)
88
87
  return;