hfs 0.51.1 → 0.52.0-alpha2

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 (40) hide show
  1. package/admin/assets/index-V0udjUIR.js +808 -0
  2. package/admin/assets/{index-49472c6b.css → index-rD-EYdVL.css} +1 -1
  3. package/{frontend/assets/sha512-l_aqBm0S.js → admin/assets/sha512-kdrIq7UN.js} +1 -1
  4. package/admin/index.html +2 -3
  5. package/frontend/assets/index-EdGoLuWB.css +1 -0
  6. package/frontend/assets/index-r8i8c_PX.js +101 -0
  7. package/{admin/assets/sha512-c9d3d113.js → frontend/assets/sha512-4UUUqqOn.js} +1 -1
  8. package/frontend/index.html +2 -2
  9. package/package.json +9 -7
  10. package/src/adminApis.js +4 -86
  11. package/src/api.accounts.js +12 -15
  12. package/src/api.auth.js +9 -35
  13. package/src/api.log.js +74 -0
  14. package/src/api.monitor.js +5 -12
  15. package/src/api.net.js +1 -1
  16. package/src/api.vfs.js +4 -4
  17. package/src/apiMiddleware.js +1 -1
  18. package/src/auth.js +17 -8
  19. package/src/block.js +1 -1
  20. package/src/commands.js +1 -3
  21. package/src/const.js +1 -1
  22. package/src/cross.js +25 -7
  23. package/src/customHtml.js +1 -1
  24. package/src/ddns.js +53 -0
  25. package/src/github.js +2 -2
  26. package/src/langs/hfs-lang-it.json +10 -1
  27. package/src/log.js +9 -7
  28. package/src/middlewares.js +14 -10
  29. package/src/misc.js +4 -0
  30. package/src/nat.js +4 -8
  31. package/src/perm.js +28 -34
  32. package/src/plugins.js +3 -3
  33. package/src/serveGuiFiles.js +2 -1
  34. package/src/util-http.js +12 -8
  35. package/admin/assets/index-30c70443.js +0 -808
  36. package/frontend/assets/index-Bsm0eh8l.js +0 -101
  37. package/frontend/assets/index-tdvGCFPn.css +0 -1
  38. package/src/api.helpers.js +0 -28
  39. package/src/crypt.js +0 -21
  40. package/src/pbkdf2.js +0 -74
@@ -1,4 +1,4 @@
1
- import{g as OF,c as UF}from"./index-30c70443.js";function gF(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 dF={exports:{}};/*
1
+ import{g as OF,c as UF}from"./index-r8i8c_PX.js";function gF(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 dF={exports:{}};/*
2
2
  * [js-sha512]{@link https://github.com/emn178/js-sha512}
3
3
  *
4
4
  * @version 0.8.0
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8" />
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
- <script type="module" crossorigin src="/assets/index-Bsm0eh8l.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-tdvGCFPn.css">
7
+ <script type="module" crossorigin src="/assets/index-r8i8c_PX.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-EdGoLuWB.css">
9
9
  </head>
10
10
  <body>
11
11
  <noscript>You need to enable JavaScript to run this app.</noscript>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hfs",
3
- "version": "0.51.1",
3
+ "version": "0.52.0-alpha2",
4
4
  "description": "HTTP File Server",
5
5
  "keywords": [
6
6
  "file server",
@@ -15,7 +15,7 @@
15
15
  "watch-server-proxied": "cross-env FRONTEND_PROXY=3005 ADMIN_PROXY=3006 npm run watch-server",
16
16
  "start-frontend": "npm run start --workspace=frontend",
17
17
  "start-admin": "npm run start --workspace=admin",
18
- "build-all": "npm audit --omit=dev --audit-level=moderate && rm -rf dist && npm i && npm run build-server && npm run build-frontend && npm run build-admin && echo COMPLETED",
18
+ "build-all": "npm audit --omit=dev --audit-level=moderate && rm -rf dist && npm i && npm run build-server & npm run build-frontend & npm run build-admin & wait && echo COMPLETED",
19
19
  "build-server": "rm -rf dist/src dist/plugins && tsc --target es2018 && touch package.json && cp -v -r package.json central.json README* LICENSE* plugins dist && find dist -name .DS_Store -delete && node afterbuild.js",
20
20
  "build-frontend": "npm run build --workspace=frontend",
21
21
  "build-admin": "npm run build --workspace=admin",
@@ -24,11 +24,13 @@
24
24
  "test": "mocha -r ts-node/register 'tests/**/*.ts'",
25
25
  "pub": "cd dist && npm publish",
26
26
  "dist": "npm run build-all && npm run dist-bin",
27
- "dist-bin": "npm run dist-modules && cd dist && pkg . --public -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",
28
- "dist-modules": "cp package*.json central.json dist && cd dist && npm ci --omit=dev && npm run dist-crclib && rm package-lock.json && cd .. && node prune_modules",
29
- "dist-crclib": "npm i -f --no-save --omit=dev @node-rs/crc32-win32-x64-msvc @node-rs/crc32-darwin-arm64 @node-rs/crc32-darwin-x64 @node-rs/crc32-linux-x64-gnu",
30
- "dist-win": "npm run dist-modules && cd dist && pkg . --public -C gzip -t node18-win-x64",
31
- "dist-mac": "npm run dist-modules && cd dist && pkg . --public -C gzip -t node18-macos-arm64",
27
+ "dist-bin": "npm run dist-modules && npm run dist-win && npm run dist-linux && npm run dist-mac && npm run dist-mac-arm",
28
+ "dist-modules": "cp package*.json central.json dist && cd dist && npm ci --omit=dev && cd .. && node prune_modules",
29
+ "dist-pre": "cd dist && rm -rf node_modules/@node-rs/crc32-*",
30
+ "dist-win": "npm run dist-pre && cd dist && npm i -f --no-save --omit=dev @node-rs/crc32-win32-x64-msvc && pkg . --public -C gzip -t node18-win-x64 && zip hfs-windows.zip hfs.exe -r plugins && cd ..",
31
+ "dist-mac-arm": "npm run dist-pre && cd dist && npm i -f --no-save --omit=dev @node-rs/crc32-darwin-arm64 && pkg . --public -C gzip -t node18-macos-arm64 && zip hfs-mac-arm.zip hfs -r plugins && cd ..",
32
+ "dist-mac": "npm run dist-pre && cd dist && npm i -f --no-save --omit=dev @node-rs/crc32-darwin-x64 && pkg . --public -C gzip -t node18-macos-x64 && zip hfs-mac.zip hfs -r plugins && cd ..",
33
+ "dist-linux": "npm run dist-pre && cd dist && npm i -f --no-save --omit=dev @node-rs/crc32-linux-x64-gnu && pkg . --public -C gzip -t node18-linux-x64 && zip hfs-linux.zip hfs -r plugins && cd ..",
32
34
  "dist-node": "npm run dist-modules && cd dist && zip hfs-node.zip -r * -x *.zip *.exe hfs-* *.log logs"
33
35
  },
34
36
  "engines": {
package/src/adminApis.js CHANGED
@@ -1,28 +1,5 @@
1
1
  "use strict";
2
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
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || function (mod) {
20
- if (mod && mod.__esModule) return mod;
21
- var result = {};
22
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
- __setModuleDefault(result, mod);
24
- return result;
25
- };
26
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
27
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
5
  };
@@ -38,26 +15,23 @@ const api_plugins_1 = __importDefault(require("./api.plugins"));
38
15
  const api_monitor_1 = __importDefault(require("./api.monitor"));
39
16
  const api_lang_1 = __importDefault(require("./api.lang"));
40
17
  const api_net_1 = __importDefault(require("./api.net"));
18
+ const api_log_1 = __importDefault(require("./api.log"));
41
19
  const connections_1 = require("./connections");
42
20
  const misc_1 = require("./misc");
43
- const events_1 = __importDefault(require("./events"));
44
21
  const perm_1 = require("./perm");
45
22
  const middlewares_1 = require("./middlewares");
46
23
  const promises_1 = require("fs/promises");
47
- const fs_1 = require("fs");
48
- const readline = __importStar(require("readline"));
49
- const log_1 = require("./log");
50
24
  const child_process_1 = require("child_process");
51
25
  const util_1 = require("util");
52
26
  const customHtml_1 = require("./customHtml");
53
27
  const lodash_1 = __importDefault(require("lodash"));
54
28
  const update_1 = require("./update");
55
- const consoleLog_1 = require("./consoleLog");
56
29
  const path_1 = require("path");
57
30
  const errorPages_1 = require("./errorPages");
58
31
  const geo_1 = require("./geo");
59
32
  const roots_1 = require("./roots");
60
33
  const SendList_1 = require("./SendList");
34
+ const ddns_1 = require("./ddns");
61
35
  exports.adminApis = {
62
36
  ...api_vfs_1.default,
63
37
  ...api_accounts_1.default,
@@ -65,6 +39,8 @@ exports.adminApis = {
65
39
  ...api_monitor_1.default,
66
40
  ...api_lang_1.default,
67
41
  ...api_net_1.default,
42
+ ...api_log_1.default,
43
+ get_dynamic_dns_error: ddns_1.get_dynamic_dns_error,
68
44
  async set_config({ values }) {
69
45
  var _a;
70
46
  (0, misc_1.apiAssertTypes)({ object: { values } });
@@ -142,64 +118,6 @@ exports.adminApis = {
142
118
  await (0, promises_1.writeFile)(files.cert, cert);
143
119
  return files;
144
120
  },
145
- get_log({ file = 'log' }, ctx) {
146
- return new SendList_1.SendListReadable({
147
- bufferTime: 10,
148
- async doAtStart(list) {
149
- if (file === 'console') {
150
- for (const chunk of lodash_1.default.chunk(consoleLog_1.consoleLog, 1000)) { // avoid occupying the thread too long
151
- for (const x of chunk)
152
- list.add(x);
153
- await (0, misc_1.wait)(0);
154
- }
155
- list.ready();
156
- events_1.default.on('console', x => list.add(x));
157
- return;
158
- }
159
- const logger = log_1.loggers.find(l => l.name === file);
160
- if (!logger)
161
- return list.error(const_1.HTTP_NOT_FOUND, true);
162
- const input = (0, fs_1.createReadStream)(logger.path);
163
- input.on('error', async (e) => {
164
- if (e.code === 'ENOENT') // ignore ENOENT, consider it an empty log
165
- return list.ready();
166
- list.error(e.code || e.message);
167
- });
168
- input.on('end', () => list.ready());
169
- input.on('ready', () => {
170
- readline.createInterface({ input }).on('line', line => {
171
- if (ctx.aborted)
172
- return input.close();
173
- const obj = parse(line);
174
- if (obj)
175
- list.add(obj);
176
- }).on('close', () => {
177
- ctx.res.once('close', (0, misc_1.onOff)(events_1.default, {
178
- [logger.name](entry) {
179
- list.add(entry);
180
- }
181
- }));
182
- });
183
- });
184
- }
185
- });
186
- function parse(line) {
187
- const m = /^(.+?) (.+?) (.+?) \[(.{11}):(.{14})] "(\w+) ([^"]+) HTTP\/\d.\d" (\d+) (-|\d+) ?(.*)/.exec(line);
188
- if (!m)
189
- return;
190
- const [, ip, , user, date, time, method, uri, status, length, extra] = m;
191
- return {
192
- ip,
193
- user: user === '-' ? undefined : user,
194
- ts: new Date(date + ' ' + time),
195
- method,
196
- uri,
197
- status: Number(status),
198
- length: length === '-' ? undefined : Number(length),
199
- extra: (0, misc_1.tryJson)((0, misc_1.tryJson)(extra)) || undefined,
200
- };
201
- }
202
- },
203
121
  };
204
122
  for (const [k, was] of Object.entries(exports.adminApis))
205
123
  exports.adminApis[k] = (params, ctx) => {
@@ -4,7 +4,6 @@ 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
- const api_helpers_1 = require("./api.helpers");
8
7
  const apiMiddleware_1 = require("./apiMiddleware");
9
8
  const perm_1 = require("./perm");
10
9
  const lodash_1 = __importDefault(require("lodash"));
@@ -19,7 +18,7 @@ function prepareAccount(ac) {
19
18
  invalidated: auth_1.invalidSessions.has(ac.username),
20
19
  };
21
20
  }
22
- const apis = {
21
+ exports.default = {
23
22
  get_usernames() {
24
23
  return { list: Object.keys(perm_1.accountsConfig.get()) };
25
24
  },
@@ -33,20 +32,24 @@ const apis = {
33
32
  get_admins() {
34
33
  return { list: lodash_1.default.filter(perm_1.accountsConfig.get(), perm_1.accountCanLoginAdmin).map(ac => ac.username) };
35
34
  },
36
- set_account({ username, changes }, ctx) {
35
+ async set_account({ username, changes }, ctx) {
37
36
  var _a;
38
37
  const acc = (0, perm_1.getAccount)(username);
39
38
  if (!acc)
40
39
  return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
41
- (0, perm_1.setAccount)(acc, changes);
40
+ await (0, perm_1.updateAccount)(acc, changes);
42
41
  if (changes.username && ((_a = ctx.session) === null || _a === void 0 ? void 0 : _a.username) === username)
43
42
  ctx.session.username = changes.username;
44
43
  return lodash_1.default.pick(acc, 'username');
45
44
  },
46
- add_account({ overwrite, username, ...rest }) {
45
+ async add_account({ overwrite, username, ...rest }) {
47
46
  const existing = (0, perm_1.getAccount)(username);
48
- if (existing)
49
- return overwrite ? (0, perm_1.setAccount)(existing, rest) : new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT);
47
+ if (existing) {
48
+ if (overwrite)
49
+ return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT);
50
+ await (0, perm_1.updateAccount)(existing, rest);
51
+ return lodash_1.default.pick(existing, 'username');
52
+ }
50
53
  const acc = (0, perm_1.addAccount)(username, rest);
51
54
  return acc ? lodash_1.default.pick(acc, 'username') : new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
52
55
  },
@@ -57,15 +60,9 @@ const apis = {
57
60
  auth_1.invalidSessions.add(username);
58
61
  return {};
59
62
  },
60
- async change_password_others({ username, newPassword }) {
63
+ async change_srp({ username, salt, verifier }) {
61
64
  const a = (0, perm_1.getAccount)(username);
62
- return a ? (0, api_helpers_1.changePasswordHelper)(a, newPassword)
63
- : new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
64
- },
65
- async change_srp_others({ username, salt, verifier }) {
66
- const a = (0, perm_1.getAccount)(username);
67
- return a ? (0, api_helpers_1.changeSrpHelper)(a, salt, verifier)
65
+ return a ? (0, perm_1.changeSrpHelper)(a, salt, verifier)
68
66
  : new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
69
67
  }
70
68
  };
71
- exports.default = apis;
package/src/api.auth.js CHANGED
@@ -1,38 +1,16 @@
1
1
  "use strict";
2
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
- exports.change_srp = exports.change_password = exports.refresh_session = exports.logout = exports.loginSrp2 = exports.loginSrp1 = exports.login = void 0;
4
+ exports.change_my_srp = exports.refresh_session = exports.logout = exports.loginSrp2 = exports.loginSrp1 = void 0;
5
5
  const perm_1 = require("./perm");
6
- const crypt_1 = require("./crypt");
7
6
  const apiMiddleware_1 = require("./apiMiddleware");
8
7
  const const_1 = require("./const");
9
- const api_helpers_1 = require("./api.helpers");
10
8
  const adminApis_1 = require("./adminApis");
11
9
  const middlewares_1 = require("./middlewares");
12
10
  const auth_1 = require("./auth");
13
11
  const config_1 = require("./config");
14
12
  const ongoingLogins = {}; // store data that doesn't fit session object
15
13
  const keepSessionAlive = (0, config_1.defineConfig)('keep_session_alive', true);
16
- function makeExp() {
17
- return !keepSessionAlive.get() ? undefined
18
- : { exp: new Date(Date.now() + middlewares_1.sessionDuration.compiled()) };
19
- }
20
- const login = async ({ username, password }, ctx) => {
21
- if (!username || !password) // some validation
22
- return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
23
- const account = (0, perm_1.getAccount)(username);
24
- if (!account || !(0, perm_1.accountCanLogin)(account))
25
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
26
- if (!account.hashed_password)
27
- return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE);
28
- if (!await (0, crypt_1.verifyPassword)(account.hashed_password, password))
29
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
30
- if (!ctx.session)
31
- return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
32
- await (0, auth_1.loggedIn)(ctx, username);
33
- return { ...makeExp(), redirect: account.redirect };
34
- };
35
- exports.login = login;
36
14
  const loginSrp1 = async ({ username }, ctx) => {
37
15
  if (!username)
38
16
  return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
@@ -69,7 +47,7 @@ const loginSrp2 = async ({ pubKey, proof }, ctx) => {
69
47
  return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
70
48
  try {
71
49
  const M2 = await step1.step2(BigInt(pubKey), BigInt(proof));
72
- await (0, auth_1.loggedIn)(ctx, username);
50
+ await (0, auth_1.setLoggedIn)(ctx, username);
73
51
  delete ctx.session.loggingIn;
74
52
  return {
75
53
  proof: String(M2),
@@ -90,32 +68,28 @@ exports.loginSrp2 = loginSrp2;
90
68
  const logout = async ({}, ctx) => {
91
69
  if (!ctx.session)
92
70
  return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
93
- await (0, auth_1.loggedIn)(ctx, false);
71
+ await (0, auth_1.setLoggedIn)(ctx, false);
94
72
  // 401 is a convenient code for OK: the browser clears a possible http authentication (hopefully), and Admin automatically triggers login dialog
95
73
  return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
96
74
  };
97
75
  exports.logout = logout;
98
76
  const refresh_session = async ({}, ctx) => {
77
+ var _a;
99
78
  return !ctx.session ? new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR) : {
100
79
  username: (0, auth_1.getCurrentUsername)(ctx),
101
80
  adminUrl: (0, adminApis_1.ctxAdminAccess)(ctx) ? ctx.state.revProxyPath + const_1.ADMIN_URI : undefined,
102
81
  canChangePassword: canChangePassword(ctx.state.account),
103
- ...makeExp(),
82
+ exp: keepSessionAlive.get() ? new Date(Date.now() + middlewares_1.sessionDuration.compiled()) : undefined,
83
+ accountExp: (_a = ctx.state.account) === null || _a === void 0 ? void 0 : _a.expire,
104
84
  };
105
85
  };
106
86
  exports.refresh_session = refresh_session;
107
- const change_password = async ({ newPassword }, ctx) => {
108
- const a = ctx.state.account;
109
- return !a || !canChangePassword(a) ? new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED)
110
- : (0, api_helpers_1.changePasswordHelper)(a, newPassword);
111
- };
112
- exports.change_password = change_password;
113
- const change_srp = async ({ salt, verifier }, ctx) => {
87
+ const change_my_srp = async ({ salt, verifier }, ctx) => {
114
88
  const a = ctx.state.account;
115
89
  return !a || !canChangePassword(a) ? new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED)
116
- : (0, api_helpers_1.changeSrpHelper)(a, salt, verifier);
90
+ : (0, perm_1.changeSrpHelper)(a, salt, verifier);
117
91
  };
118
- exports.change_srp = change_srp;
92
+ exports.change_my_srp = change_my_srp;
119
93
  function canChangePassword(account) {
120
94
  return account && !(0, perm_1.getFromAccount)(account, a => a.disable_password_change);
121
95
  }
package/src/api.log.js ADDED
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const lodash_1 = __importDefault(require("lodash"));
7
+ const consoleLog_1 = require("./consoleLog");
8
+ const cross_1 = require("./cross");
9
+ const events_1 = __importDefault(require("./events"));
10
+ const log_1 = require("./log");
11
+ const fs_1 = require("fs");
12
+ const readline_1 = __importDefault(require("readline"));
13
+ const misc_1 = require("./misc");
14
+ const SendList_1 = require("./SendList");
15
+ exports.default = {
16
+ get_log({ file = 'log' }, ctx) {
17
+ return new SendList_1.SendListReadable({
18
+ bufferTime: 10,
19
+ async doAtStart(list) {
20
+ if (file === 'console') {
21
+ for (const chunk of lodash_1.default.chunk(consoleLog_1.consoleLog, 1000)) { // avoid occupying the thread too long
22
+ for (const x of chunk)
23
+ list.add(x);
24
+ await (0, cross_1.wait)(0);
25
+ }
26
+ list.ready();
27
+ events_1.default.on('console', x => list.add(x));
28
+ return;
29
+ }
30
+ const logger = log_1.loggers.find(l => l.name === file);
31
+ if (!logger)
32
+ return list.error(cross_1.HTTP_NOT_FOUND, true);
33
+ const input = (0, fs_1.createReadStream)(logger.path);
34
+ input.on('error', async (e) => {
35
+ if (e.code === 'ENOENT') // ignore ENOENT, consider it an empty log
36
+ return list.ready();
37
+ list.error(e.code || e.message);
38
+ });
39
+ input.on('end', () => list.ready());
40
+ input.on('ready', () => {
41
+ readline_1.default.createInterface({ input }).on('line', line => {
42
+ if (ctx.aborted)
43
+ return input.close();
44
+ const obj = parse(line);
45
+ if (obj)
46
+ list.add(obj);
47
+ }).on('close', () => {
48
+ ctx.res.once('close', (0, misc_1.onOff)(events_1.default, {
49
+ [logger.name](entry) {
50
+ list.add(entry);
51
+ }
52
+ }));
53
+ });
54
+ });
55
+ }
56
+ });
57
+ function parse(line) {
58
+ const m = /^(.+?) (.+?) (.+?) \[(.{11}):(.{14})] "(\w+) ([^"]+) HTTP\/\d.\d" (\d+) (-|\d+) ?(.*)/.exec(line);
59
+ if (!m)
60
+ return;
61
+ const [, ip, , user, date, time, method, uri, status, length, extra] = m;
62
+ return {
63
+ ip,
64
+ user: user === '-' ? undefined : user,
65
+ ts: new Date(date + ' ' + time),
66
+ method,
67
+ uri,
68
+ status: Number(status),
69
+ length: length === '-' ? undefined : Number(length),
70
+ extra: (0, cross_1.tryJson)((0, cross_1.tryJson)(extra)) || undefined,
71
+ };
72
+ }
73
+ },
74
+ };
@@ -11,19 +11,12 @@ const throttler_1 = require("./throttler");
11
11
  const auth_1 = require("./auth");
12
12
  const SendList_1 = require("./SendList");
13
13
  exports.default = {
14
- async disconnect({ ip, port, wait }) {
15
- var _a, _b;
14
+ async disconnect({ ip, port }) {
16
15
  const match = lodash_1.default.matches({ ip, port });
17
- const c = (0, connections_1.getConnections)().find(c => match(getConnAddress(c)));
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
- }
26
- return { result: Boolean(c) };
16
+ const found = (0, connections_1.getConnections)().filter(c => match(getConnAddress(c)));
17
+ for (const c of found)
18
+ c.socket.destroy();
19
+ return { result: found.length };
27
20
  },
28
21
  get_connections({}, ctx) {
29
22
  const list = new SendList_1.SendListReadable({
package/src/api.net.js CHANGED
@@ -33,7 +33,7 @@ const apis = {
33
33
  const domainIps = lodash_1.default.uniq((0, misc_1.onlyTruthy)(settled.map(x => x.status === 'fulfilled' && x.value)).flat());
34
34
  if (!domainIps.length)
35
35
  throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "domain not working");
36
- const { publicIps } = await (0, nat_1.getNatInfo)(); // do this before stopping the server
36
+ const publicIps = await (0, nat_1.getPublicIps)(); // do this before stopping the server
37
37
  for (const v6 of [false, true]) {
38
38
  const domainIpsThisVersion = domainIps.filter(x => (0, net_1.isIPv6)(x) === v6);
39
39
  const ipsThisVersion = publicIps.filter(x => (0, net_1.isIPv6)(x) === v6);
package/src/api.vfs.js CHANGED
@@ -26,10 +26,10 @@ const apis = {
26
26
  async get_vfs() {
27
27
  return { root: await recur() };
28
28
  async function recur(node = vfs_1.vfs) {
29
- var _a;
29
+ var _a, _b, _c;
30
30
  const { source } = node;
31
- const stats = Boolean(source) && await (0, promises_1.stat)(source).catch(() => false);
32
- const isDir = !(0, vfs_1.nodeIsLink)(node) && (!source || stats && stats.isDirectory());
31
+ const stats = !source ? undefined : await (0, promises_1.stat)(source).catch(() => undefined);
32
+ const isDir = !(0, vfs_1.nodeIsLink)(node) && (!source || ((_a = stats === null || stats === void 0 ? void 0 : stats.isDirectory()) !== null && _a !== void 0 ? _a : ((_b = node.children) === null || _b === void 0 ? void 0 : _b.length) > 0));
33
33
  const copyStats = stats ? lodash_1.default.pick(stats, ['size', 'ctime', 'mtime'])
34
34
  : { size: source ? -1 : undefined };
35
35
  if (copyStats.mtime && Number(copyStats.mtime) === Number(copyStats.ctime))
@@ -43,7 +43,7 @@ const apis = {
43
43
  ...node.original || node,
44
44
  inherited,
45
45
  byMasks: lodash_1.default.isEmpty(byMasks) ? undefined : byMasks,
46
- website: Boolean((_a = node.children) === null || _a === void 0 ? void 0 : _a.find((0, vfs_1.isSameFilenameAs)('index.html')))
46
+ website: Boolean((_c = node.children) === null || _c === void 0 ? void 0 : _c.find((0, vfs_1.isSameFilenameAs)('index.html')))
47
47
  || isDir && source && await (0, promises_1.stat)((0, path_1.join)(source, 'index.html')).then(() => true, () => undefined)
48
48
  || undefined,
49
49
  name: node === vfs_1.vfs ? '' : (0, vfs_1.getNodeName)(node),
@@ -17,7 +17,7 @@ class ApiError extends Error {
17
17
  }
18
18
  }
19
19
  exports.ApiError = ApiError;
20
- const logApi = (0, config_1.defineConfig)('log_api', true);
20
+ const logApi = (0, config_1.defineConfig)(misc_1.CFG.log_api, true);
21
21
  function apiMiddleware(apis) {
22
22
  return async (ctx) => {
23
23
  if (!logApi.get())
package/src/auth.js CHANGED
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.invalidSessions = exports.loggedIn = exports.getCurrentUsername = exports.srpCheck = exports.srpStep1 = void 0;
3
+ exports.invalidSessions = exports.setLoggedIn = exports.getCurrentUsername = exports.srpCheck = exports.srpStep1 = void 0;
4
4
  const perm_1 = require("./perm");
5
5
  const cross_const_1 = require("./cross-const");
6
6
  const tssrp6a_1 = require("tssrp6a");
7
- const middlewares_1 = require("./middlewares");
8
7
  const srp_1 = require("./srp");
8
+ const cross_1 = require("./cross");
9
+ const node_crypto_1 = require("node:crypto");
9
10
  const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters());
10
11
  async function srpStep1(account) {
11
12
  if (!account.srp)
@@ -18,13 +19,19 @@ async function srpStep1(account) {
18
19
  return { step1, salt, pubKey: String(step1.B) }; // cast to string cause bigint can't be jsonized
19
20
  }
20
21
  exports.srpStep1 = srpStep1;
22
+ const cache = {};
21
23
  async function srpCheck(username, password) {
22
24
  const account = (0, perm_1.getAccount)(username);
23
25
  if (!(account === null || account === void 0 ? void 0 : account.srp) || !password)
24
26
  return;
25
- const { step1, salt, pubKey } = await srpStep1(account);
26
- const client = await (0, srp_1.srpClientPart)(username, password, salt, pubKey);
27
- return await step1.step2(client.A, client.M1).then(() => account, () => { });
27
+ const k = (0, node_crypto_1.createHash)('sha256').update(username + password + account.srp).digest("hex");
28
+ const good = await (0, cross_1.getOrSet)(cache, k, async () => {
29
+ const { step1, salt, pubKey } = await srpStep1(account);
30
+ const client = await (0, srp_1.srpClientPart)(username, password, salt, pubKey);
31
+ setTimeout(() => delete cache[k], 60000);
32
+ return step1.step2(client.A, client.M1).then(() => 1, () => 0);
33
+ });
34
+ return good ? account : undefined;
28
35
  }
29
36
  exports.srpCheck = srpCheck;
30
37
  function getCurrentUsername(ctx) {
@@ -33,7 +40,7 @@ function getCurrentUsername(ctx) {
33
40
  }
34
41
  exports.getCurrentUsername = getCurrentUsername;
35
42
  // centralized log-in state
36
- async function loggedIn(ctx, username) {
43
+ async function setLoggedIn(ctx, username) {
37
44
  const s = ctx.session;
38
45
  if (!s)
39
46
  return ctx.throw(cross_const_1.HTTP_SERVER_ERROR, 'session');
@@ -43,7 +50,9 @@ async function loggedIn(ctx, username) {
43
50
  }
44
51
  exports.invalidSessions.delete(username);
45
52
  s.username = (0, perm_1.normalizeUsername)(username);
46
- await (0, middlewares_1.prepareState)(ctx, async () => { }); // updating the state is necessary to send complete session data so that frontend shows admin button
53
+ const a = ctx.state.account = (0, perm_1.getAccount)(username);
54
+ if (a && !a.expire && a.days_to_live)
55
+ (0, perm_1.updateAccount)(a, { expire: new Date(Date.now() + a.days_to_live * cross_1.DAY) });
47
56
  }
48
- exports.loggedIn = loggedIn;
57
+ exports.setLoggedIn = setLoggedIn;
49
58
  exports.invalidSessions = new Set(); // since session are currently stored in cookies, we need to memorize this until we meet again
package/src/block.js CHANGED
@@ -10,7 +10,7 @@ const block = (0, config_1.defineConfig)('block', [], rules => {
10
10
  const ret = !Array.isArray(rules) ? []
11
11
  : (0, misc_1.onlyTruthy)(rules.map(rule => {
12
12
  rule.expire && (rule.expire = new Date(rule.expire));
13
- return !(rule.expire > now) && (0, misc_1.makeNetMatcher)(rule.ip);
13
+ return !rule.disabled && (rule.expire || now) <= now && (0, misc_1.makeNetMatcher)(rule.ip);
14
14
  }));
15
15
  // reapply new block to existing connections
16
16
  for (const { socket, ip } of (0, connections_1.getConnections)())
package/src/commands.js CHANGED
@@ -65,9 +65,7 @@ const commands = {
65
65
  const acc = (0, perm_1.getAccount)(user);
66
66
  if (!acc)
67
67
  throw "user doesn't exist";
68
- await (0, perm_1.updateAccount)(acc, acc => {
69
- acc.password = password;
70
- });
68
+ await (0, perm_1.updateAccount)(acc, { password });
71
69
  }
72
70
  },
73
71
  config: {
package/src/const.js CHANGED
@@ -45,7 +45,7 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
45
45
  exports.ORIGINAL_CWD = process.cwd();
46
46
  exports.HFS_STARTED = new Date();
47
47
  const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
48
- exports.BUILD_TIMESTAMP = "2024-01-29T11:24:53.433Z";
48
+ exports.BUILD_TIMESTAMP = "2024-01-31T22:47:53.249Z";
49
49
  const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
50
50
  exports.VERSION = pkg.version;
51
51
  exports.RUNNING_BETA = exports.VERSION.includes('-');