hfs 0.26.1 → 0.26.4

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.5c49e66e.js";function OF(sF,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 sF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(sF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(sF,Symbol.toStringTag,{value:"Module"}))}var yF={exports:{}};/*
1
+ import{c as SF}from"./index.bbe713bb.js";function OF(sF,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 sF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(sF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(sF,Symbol.toStringTag,{value:"Module"}))}var yF={exports:{}};/*
2
2
  * [js-sha512]{@link https://github.com/emn178/js-sha512}
3
3
  *
4
4
  * @version 0.8.0
@@ -6,8 +6,8 @@
6
6
  <link href="fontello.css" rel="stylesheet" />
7
7
  <script>SESSION = _HFS_SESSION_</script>
8
8
  <title>File Server</title>
9
- <script type="module" crossorigin src="/assets/index.0f8e459c.js"></script>
10
- <link rel="stylesheet" href="/assets/index.ee805a6c.css">
9
+ <script type="module" crossorigin src="/assets/index.bbe713bb.js"></script>
10
+ <link rel="stylesheet" href="/assets/index.93366732.css">
11
11
  </head>
12
12
  <body>
13
13
  <div hidden>_HFS_PLUGINS_</div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hfs",
3
- "version": "0.26.1",
3
+ "version": "0.26.4",
4
4
  "description": "HTTP File Server",
5
5
  "keywords": [
6
6
  "file server",
@@ -27,9 +27,10 @@
27
27
  "test": "mocha -r ts-node/register 'tests/**/*.ts'",
28
28
  "pub": "cd dist && npm publish",
29
29
  "dist": "npm run build-all && npm run dist-bin",
30
- "dist-bin": "npm run dist-modules && cd dist && pkg . -C gzip && mv -f hfs-win.exe hfs.exe && zip hfs-windows.zip hfs.exe -r plugins && cp -f hfs-linux hfs && zip hfs-linux.zip hfs -r plugins && cp -f hfs-macos hfs && zip hfs-mac.zip hfs -r plugins && rm hfs",
30
+ "dist-bin": "npm run dist-modules && cd dist && pkg . -C gzip && mv -f hfs-win-x64.exe hfs.exe && zip hfs-windows.zip hfs.exe -r plugins && cp -f hfs-linux-x64 hfs && zip hfs-linux.zip hfs -r plugins && cp -f hfs-macos-x64 hfs && zip hfs-mac.zip hfs -r plugins && cp -f hfs-macos-arm64 hfs && zip hfs-mac-arm.zip hfs -r plugins && rm hfs",
31
31
  "dist-modules": "cp package*.json dist && cd dist && npm ci --omit=dev && npm i -f --no-save --omit=dev @node-rs/crc32-win32-x64-msvc && rm package-lock.json && cd .. && node prune_modules",
32
- "dist-win": "npm run dist-modules && pkg . -C gzip -t node16-win && cd dist && zip hfs-windows.zip hfs.exe -r plugins",
32
+ "dist-win": "npm run dist-modules && cd dist && pkg . -C gzip -t node16-win-x64 && zip hfs-windows.zip hfs.exe -r plugins",
33
+ "dist-mac": "cd dist && pkg . -C gzip -t node16-mac-arm64",
33
34
  "dist-node": "npm run dist-modules && cd dist && zip hfs-node.zip -r * -x *.zip *.exe hfs-* *.log logs"
34
35
  },
35
36
  "engines": {
@@ -53,9 +54,10 @@
53
54
  "frontend/**/*"
54
55
  ],
55
56
  "targets": [
56
- "node16-win",
57
- "node16-mac",
58
- "node16-linux"
57
+ "node16-win-x64",
58
+ "node16-mac-x64",
59
+ "node16-mac-arm64",
60
+ "node16-linux-x64"
59
61
  ]
60
62
  },
61
63
  "dependencies": {
@@ -9,9 +9,7 @@ exports.config = {
9
9
  fields: {
10
10
  host: { label: "Domain" },
11
11
  root: { helperText: "Root path in VFS" },
12
- },
13
- defaultValue: [],
14
- height: 300,
12
+ }
15
13
  },
16
14
  mandatory: {
17
15
  label: "Block requests that are not using any of the domains above",
@@ -37,7 +37,7 @@ const ZIP64_NUMBER_LIMIT = 0xffff;
37
37
  let crc32function;
38
38
  Promise.resolve().then(() => __importStar(require('@node-rs/crc32'))).then(lib => crc32function = lib.crc32, () => {
39
39
  console.log('using generic lib for crc32');
40
- return crc32function = buffer_crc32_1.crc32.unsigned;
40
+ crc32function = buffer_crc32_1.unsigned;
41
41
  });
42
42
  const FLAGS = 0x0808; // bit3 = no crc in local header + bit11 = utf8
43
43
  class QuickZipStream extends stream_1.Readable {
@@ -23,7 +23,9 @@ const apis = {
23
23
  pluginUninstalled: id => list.remove({ id }),
24
24
  });
25
25
  function serialize(p) {
26
- return lodash_1.default.defaults('getData' in p ? Object.assign(lodash_1.default.pick(p, ['id', 'started']), p.getData()) : p, { started: null, badApi: null }); // nulls should be used to be sure to overwrite previous values,
26
+ const o = 'getData' in p ? Object.assign(lodash_1.default.pick(p, ['id', 'started']), p.getData())
27
+ : { ...p }; // _.defaults mutates object, and we don't want that
28
+ return lodash_1.default.defaults(o, { started: null, badApi: null }); // nulls should be used to be sure to overwrite previous values,
27
29
  }
28
30
  },
29
31
  async get_plugin_updates() {
package/src/commands.js CHANGED
@@ -7,10 +7,23 @@ const perm_1 = require("./perm");
7
7
  const config_1 = require("./config");
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
9
  const update_1 = require("./update");
10
+ const listen_1 = require("./listen");
10
11
  const yaml_1 = __importDefault(require("yaml"));
11
12
  const const_1 = require("./const");
12
- console.log(`HINT: type "help" for help`);
13
- require('readline').createInterface({ input: process.stdin }).on('line', (line) => {
13
+ const readline_1 = require("readline");
14
+ try {
15
+ /*
16
+ is this try-block useful in case the stdin is unavailable?
17
+ Not sure, but someone reported a problem using nohup https://github.com/rejetto/hfs/issues/74
18
+ and I've found this example try-catching https://github.com/DefinitelyTyped/DefinitelyTyped/blob/dda83a906914489e09ca28afea12948529015d4a/types/node/readline.d.ts#L489
19
+ */
20
+ (0, readline_1.createInterface)({ input: process.stdin }).on('line', parseCommandLine);
21
+ console.log(`HINT: type "help" for help`);
22
+ }
23
+ catch (_a) {
24
+ console.log("console commands not available");
25
+ }
26
+ function parseCommandLine(line) {
14
27
  if (!line)
15
28
  return;
16
29
  const [name, ...params] = line.trim().split(/ +/);
@@ -25,7 +38,7 @@ require('readline').createInterface({ input: process.stdin }).on('line', (line)
25
38
  else
26
39
  throw err;
27
40
  });
28
- });
41
+ }
29
42
  const commands = {
30
43
  help: {
31
44
  params: '',
@@ -33,6 +46,12 @@ const commands = {
33
46
  console.log("supported commands:", ...lodash_1.default.map(commands, ({ params }, name) => '\n - ' + name + ' ' + params));
34
47
  }
35
48
  },
49
+ 'show-admin': {
50
+ params: '',
51
+ cb() {
52
+ (0, listen_1.openAdmin)();
53
+ }
54
+ },
36
55
  'create-admin': {
37
56
  params: '<password> [<username>=admin]',
38
57
  async cb(password, username = 'admin') {
package/src/config.js CHANGED
@@ -12,7 +12,6 @@ const yaml_1 = __importDefault(require("yaml"));
12
12
  const lodash_1 = __importDefault(require("lodash"));
13
13
  const misc_1 = require("./misc");
14
14
  const fs_1 = require("fs");
15
- const util_1 = require("util");
16
15
  const path_1 = require("path");
17
16
  const events_2 = __importDefault(require("./events"));
18
17
  const FILE = 'config.yaml';
@@ -163,10 +162,7 @@ exports.saveConfigAsap = (0, misc_1.debounceAsync)(async () => {
163
162
  await (0, misc_1.wait)(100);
164
163
  let txt = yaml_1.default.stringify(state, { lineWidth: 1000 });
165
164
  if (txt.trim() === '{}') // most users wouldn't understand
166
- if (await (0, util_1.promisify)(fs_1.exists)(path)) // if a file exists then empty it, else don't bother creating it
167
- txt = '';
168
- else
169
- return;
165
+ txt = '';
170
166
  save(path, txt)
171
167
  .catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
172
168
  });
package/src/const.js CHANGED
@@ -43,7 +43,7 @@ const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
43
43
  exports.VERSION = pkg.version;
44
44
  exports.DAY = 86400000;
45
45
  exports.SESSION_DURATION = exports.DAY;
46
- exports.API_VERSION = 4; // introduced type:real_path and api.subscribeConfig/setConfig/getHfsConfig
46
+ exports.API_VERSION = 4.1; // array.$width
47
47
  exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise is made equal to API_VERSION
48
48
  exports.SPECIAL_URI = '/~/';
49
49
  exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
@@ -55,7 +55,8 @@ exports.NO_CONTENT = 204;
55
55
  exports.FORBIDDEN = 403;
56
56
  exports.UNAUTHORIZED = 401;
57
57
  exports.IS_WINDOWS = process.platform === 'win32';
58
- exports.APP_PATH = (0, path_1.resolve)(__dirname + '/..');
58
+ const IS_BINARY = !(0, path_1.basename)(process.argv0).includes('node'); // this won't be node if pkg was used
59
+ exports.APP_PATH = (0, path_1.dirname)(IS_BINARY ? process.argv0 : __dirname);
59
60
  // we want this to be the first stuff to be printed, then we print it in this module, that is executed at the beginning
60
61
  if (exports.DEV)
61
62
  console.clear();
package/src/listen.js CHANGED
@@ -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.getUrls = exports.getStatus = exports.httpsPortCfg = exports.portCfg = void 0;
30
+ exports.getUrls = exports.getStatus = exports.httpsPortCfg = exports.openAdmin = exports.portCfg = void 0;
31
31
  const http = __importStar(require("http"));
32
32
  const config_1 = require("./config");
33
33
  const index_1 = require("./index");
@@ -40,6 +40,7 @@ const misc_1 = require("./misc");
40
40
  const const_1 = require("./const");
41
41
  const find_process_1 = __importDefault(require("find-process"));
42
42
  const perm_1 = require("./perm");
43
+ const lodash_1 = __importDefault(require("lodash"));
43
44
  let httpSrv;
44
45
  let httpsSrv;
45
46
  const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', !const_1.DEV);
@@ -55,13 +56,25 @@ exports.portCfg.sub(async (port) => {
55
56
  httpSrv.on('connection', connections_1.newConnection);
56
57
  printUrls(port, 'http');
57
58
  if (openBrowserAtStart.get())
58
- (0, open_1.default)('http://localhost' + (port === 80 ? '' : ':' + port) + const_1.ADMIN_URI, { wait: true }).catch(e => {
59
+ openAdmin();
60
+ });
61
+ function openAdmin() {
62
+ for (const srv of [httpSrv, httpsSrv]) {
63
+ const a = srv.address();
64
+ if (!a || typeof a === 'string')
65
+ continue;
66
+ const baseUrl = srv.name + '://localhost:' + a.port;
67
+ (0, open_1.default)(baseUrl + const_1.ADMIN_URI, { wait: true }).catch(e => {
59
68
  console.debug(String(e));
60
69
  console.warn("cannot launch browser on this machine >PLEASE< open your browser and reach one of these (you may need a different address)", ...Object.values(getUrls()).flat().map(x => '\n - ' + x + const_1.ADMIN_URI));
61
70
  if (!(0, perm_1.anyAccountCanLoginAdmin)())
62
71
  console.log(`HINT: you can enter command: create-admin YOUR_PASSWORD`);
63
72
  });
64
- });
73
+ return true;
74
+ }
75
+ console.log("openAdmin failed");
76
+ }
77
+ exports.openAdmin = openAdmin;
65
78
  const considerHttps = (0, misc_1.debounceAsync)(async () => {
66
79
  var _a, _b;
67
80
  stopServer(httpsSrv).then();
@@ -207,20 +220,16 @@ function printUrls(port, proto) {
207
220
  if (!port)
208
221
  return;
209
222
  for (const [name, nets] of Object.entries((0, os_1.networkInterfaces)())) {
210
- if (!nets)
223
+ if (!nets || ignore.test(name))
211
224
  continue;
212
- const filteredNets = nets.filter(n => !n.internal);
213
- if (!filteredNets.length || ignore.test(name))
225
+ lodash_1.default.remove(nets, 'internal');
226
+ if (!nets.length)
214
227
  continue;
215
- console.log('network', name);
216
- for (const net of nets) {
217
- if (net.internal)
218
- continue;
219
- const appendPort = port === (proto === 'https' ? 443 : 80) ? '' : ':' + port;
220
- let { address } = net;
221
- if (address.includes(':'))
222
- address = '[' + address + ']';
223
- console.log('-', proto + '://' + address + appendPort);
224
- }
228
+ const best = lodash_1.default.find(nets, { family: 'IPv4' }) || nets[0];
229
+ const appendPort = port === (proto === 'https' ? 443 : 80) ? '' : ':' + port;
230
+ let { address } = best;
231
+ if (address.includes(':'))
232
+ address = '[' + address + ']';
233
+ console.log('network', name, proto + '://' + address + appendPort);
225
234
  }
226
235
  }
package/src/plugins.js CHANGED
@@ -31,7 +31,7 @@ exports.parsePluginSource = exports.rescan = exports.pluginsConfig = exports.ena
31
31
  const fast_glob_1 = __importDefault(require("fast-glob"));
32
32
  const watchLoad_1 = require("./watchLoad");
33
33
  const lodash_1 = __importDefault(require("lodash"));
34
- const path_1 = __importStar(require("path"));
34
+ const path_1 = __importDefault(require("path"));
35
35
  const const_1 = require("./const");
36
36
  const Const = __importStar(require("./const"));
37
37
  const misc_1 = require("./misc");
@@ -66,8 +66,9 @@ function setPluginConfig(id, changes) {
66
66
  }
67
67
  exports.setPluginConfig = setPluginConfig;
68
68
  function getPluginInfo(id) {
69
- var _a, _b;
70
- return (_b = (_a = plugins[id]) === null || _a === void 0 ? void 0 : _a.getData()) !== null && _b !== void 0 ? _b : availablePlugins[id];
69
+ var _a;
70
+ const running = (_a = plugins[id]) === null || _a === void 0 ? void 0 : _a.getData();
71
+ return running && Object.assign(running, { id }) || availablePlugins[id];
71
72
  }
72
73
  exports.getPluginInfo = getPluginInfo;
73
74
  function mapPlugins(cb) {
@@ -123,25 +124,14 @@ function pluginsMiddleware() {
123
124
  exports.pluginsMiddleware = pluginsMiddleware;
124
125
  class Plugin {
125
126
  constructor(id, data, unwatch) {
126
- var _a, _b;
127
127
  this.id = id;
128
128
  this.data = data;
129
129
  this.unwatch = unwatch;
130
130
  this.started = new Date();
131
131
  if (!data)
132
132
  throw 'invalid data';
133
- // if a previous instance is present, we are going to overwrite it, but first call its unload callback
134
- const old = plugins[id];
135
- try {
136
- (_b = (_a = old === null || old === void 0 ? void 0 : old.data) === null || _a === void 0 ? void 0 : _a.unload) === null || _b === void 0 ? void 0 : _b.call(_a);
137
- } // we don't want all the effects of the Plugin.unload
138
- catch (e) {
139
- console.debug('error unloading plugin', id, String(e));
140
- }
141
- // track this
142
- const wasStopped = availablePlugins[id];
143
- if (wasStopped)
144
- delete availablePlugins[id];
133
+ if (plugins[id])
134
+ throw "unload first: " + id;
145
135
  plugins[id] = this;
146
136
  this.data = data = { ...data }; // clone to make object modifiable. Objects coming from import are not.
147
137
  // some validation
@@ -154,7 +144,6 @@ class Plugin {
154
144
  console.warn('invalid', k);
155
145
  }
156
146
  }
157
- events_1.default.emit(old || wasStopped ? 'pluginStarted' : 'pluginInstalled', this);
158
147
  }
159
148
  get middleware() {
160
149
  var _a;
@@ -175,17 +164,19 @@ class Plugin {
175
164
  getData() {
176
165
  return { ...this.data };
177
166
  }
178
- async unload() {
167
+ async unload(reloading = false) {
179
168
  var _a, _b;
180
169
  const { id } = this;
181
- console.log('unloading plugin', id);
182
170
  try {
183
171
  await ((_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.unload) === null || _b === void 0 ? void 0 : _b.call(_a));
172
+ console.log('unloaded plugin', id);
184
173
  }
185
174
  catch (e) {
186
- console.debug('error unloading plugin', id, String(e));
175
+ console.log('error unloading plugin', id, String(e));
187
176
  }
188
177
  delete plugins[id];
178
+ if (reloading)
179
+ return;
189
180
  this.unwatch();
190
181
  if (availablePlugins[id])
191
182
  events_1.default.emit('pluginStopped', availablePlugins[id]);
@@ -213,8 +204,8 @@ async function rescan() {
213
204
  console.debug('scanning plugins');
214
205
  const found = [];
215
206
  const foundDisabled = {};
216
- const MASK = (0, path_1.join)(exports.PATH, '*', 'plugin.js');
217
- for (const f of await (0, fast_glob_1.default)([(0, path_1.join)(const_1.APP_PATH, MASK), MASK])) {
207
+ const MASK = exports.PATH + '/*/plugin.js'; // be sure to not use path.join as fast-glob doesn't work with \
208
+ for (const f of await (0, fast_glob_1.default)([(0, misc_1.adjustStaticPathForGlob)(const_1.APP_PATH) + '/' + MASK, MASK])) {
218
209
  const id = f.split('/').slice(-2)[0];
219
210
  if (id.endsWith(exports.DISABLING_POSTFIX))
220
211
  continue;
@@ -232,14 +223,16 @@ async function rescan() {
232
223
  const module = path_1.default.resolve(f);
233
224
  const { unwatch } = (0, watchLoad_1.watchLoad)(f, async () => {
234
225
  try {
235
- const reloading = plugins[id];
236
- console.log(reloading ? "reloading plugin" : "loading plugin", id);
226
+ const alreadyRunning = plugins[id];
227
+ console.log(alreadyRunning ? "reloading plugin" : "loading plugin", id);
237
228
  const { init, ...data } = await Promise.resolve().then(() => __importStar(require(module)));
238
229
  delete data.default;
239
230
  deleteModule(require.resolve(module)); // avoid caching at next import
240
231
  calculateBadApi(data);
241
232
  if (data.badApi)
242
233
  console.log("plugin", id, data.badApi);
234
+ await (alreadyRunning === null || alreadyRunning === void 0 ? void 0 : alreadyRunning.unload(true));
235
+ console.debug("starting plugin", id);
243
236
  const res = await (init === null || init === void 0 ? void 0 : init.call(null, {
244
237
  srcDir: __dirname,
245
238
  const: Const,
@@ -256,16 +249,28 @@ async function rescan() {
256
249
  cb(last);
257
250
  return exports.pluginsConfig.sub(() => {
258
251
  const now = this.getConfig(cfgKey);
259
- if (!(0, misc_1.same)(now, last))
252
+ if ((0, misc_1.same)(now, last))
253
+ return;
254
+ try {
260
255
  cb(last = now);
256
+ }
257
+ catch (e) {
258
+ console.log('plugin', id, String(e));
259
+ }
261
260
  });
262
261
  },
263
262
  getHfsConfig: config_1.getConfig,
264
263
  }));
265
264
  Object.assign(data, res);
266
- new Plugin(id, data, unwatch);
267
- if (reloading)
268
- events_1.default.emit('pluginUpdated', getPluginInfo(id));
265
+ const plugin = new Plugin(id, data, unwatch);
266
+ if (alreadyRunning)
267
+ events_1.default.emit('pluginUpdated', Object.assign(lodash_1.default.pick(plugin, 'started'), getPluginInfo(id)));
268
+ else {
269
+ const wasInstalled = availablePlugins[id];
270
+ if (wasInstalled)
271
+ delete availablePlugins[id];
272
+ events_1.default.emit(wasInstalled ? 'pluginStarted' : 'pluginInstalled', plugin);
273
+ }
269
274
  }
270
275
  catch (e) {
271
276
  console.log("plugin error:", e);
package/src/serveFile.js CHANGED
@@ -38,7 +38,7 @@ function serveFileNode(node) {
38
38
  };
39
39
  }
40
40
  exports.serveFileNode = serveFileNode;
41
- const mimeCfg = (0, config_1.defineConfig)('mime', { '*.jpg|*.png|*.mp3|*.txt': 'auto' });
41
+ const mimeCfg = (0, config_1.defineConfig)('mime', { '*': 'auto' });
42
42
  function serveFile(source, mime, content) {
43
43
  return async (ctx) => {
44
44
  if (!source)
package/src/util-files.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.unzip = exports.run = exports.dirStream = exports.isWindowsDrive = exports.dirTraversal = exports.watchDir = exports.readFileBusy = exports.isFile = exports.isDirectory = void 0;
6
+ exports.unzip = exports.run = exports.dirStream = exports.adjustStaticPathForGlob = exports.isWindowsDrive = exports.dirTraversal = exports.watchDir = exports.readFileBusy = exports.isFile = exports.isDirectory = void 0;
7
7
  const promises_1 = __importDefault(require("fs/promises"));
8
8
  const misc_1 = require("./misc");
9
9
  const fs_1 = require("fs");
@@ -86,6 +86,11 @@ function isWindowsDrive(s) {
86
86
  return s && /^[a-zA-Z]:$/.test(s);
87
87
  }
88
88
  exports.isWindowsDrive = isWindowsDrive;
89
+ // apply this to paths that may contain \ as separator (not supported by fast-glob) and other special chars to be escaped (parenthesis)
90
+ function adjustStaticPathForGlob(path) {
91
+ return fast_glob_1.default.escapePath(path.replace(/\\/g, '/'));
92
+ }
93
+ exports.adjustStaticPathForGlob = adjustStaticPathForGlob;
89
94
  async function* dirStream(path) {
90
95
  const stats = await promises_1.default.stat(path);
91
96
  if (!stats.isDirectory())