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.
- package/admin/assets/index-V0udjUIR.js +808 -0
- package/admin/assets/{index-49472c6b.css → index-rD-EYdVL.css} +1 -1
- package/{frontend/assets/sha512-l_aqBm0S.js → admin/assets/sha512-kdrIq7UN.js} +1 -1
- package/admin/index.html +2 -3
- package/frontend/assets/index-EdGoLuWB.css +1 -0
- package/frontend/assets/index-r8i8c_PX.js +101 -0
- package/{admin/assets/sha512-c9d3d113.js → frontend/assets/sha512-4UUUqqOn.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +9 -7
- package/src/adminApis.js +4 -86
- package/src/api.accounts.js +12 -15
- package/src/api.auth.js +9 -35
- package/src/api.log.js +74 -0
- package/src/api.monitor.js +5 -12
- package/src/api.net.js +1 -1
- package/src/api.vfs.js +4 -4
- package/src/apiMiddleware.js +1 -1
- package/src/auth.js +17 -8
- package/src/block.js +1 -1
- package/src/commands.js +1 -3
- package/src/const.js +1 -1
- package/src/cross.js +25 -7
- package/src/customHtml.js +1 -1
- package/src/ddns.js +53 -0
- package/src/github.js +2 -2
- package/src/langs/hfs-lang-it.json +10 -1
- package/src/log.js +9 -7
- package/src/middlewares.js +14 -10
- package/src/misc.js +4 -0
- package/src/nat.js +4 -8
- package/src/perm.js +28 -34
- package/src/plugins.js +3 -3
- package/src/serveGuiFiles.js +2 -1
- package/src/util-http.js +12 -8
- package/admin/assets/index-30c70443.js +0 -808
- package/frontend/assets/index-Bsm0eh8l.js +0 -101
- package/frontend/assets/index-tdvGCFPn.css +0 -1
- package/src/api.helpers.js +0 -28
- package/src/crypt.js +0 -21
- package/src/pbkdf2.js +0 -74
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{g as OF,c as UF}from"./index-
|
|
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
|
package/frontend/index.html
CHANGED
|
@@ -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-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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.
|
|
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
|
|
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 &&
|
|
28
|
-
"dist-modules": "cp package*.json central.json dist && cd dist && npm ci --omit=dev &&
|
|
29
|
-
"dist-
|
|
30
|
-
"dist-win": "npm run dist-
|
|
31
|
-
"dist-mac": "npm run dist-
|
|
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) => {
|
package/src/api.accounts.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
63
|
+
async change_srp({ username, salt, verifier }) {
|
|
61
64
|
const a = (0, perm_1.getAccount)(username);
|
|
62
|
-
return a ? (0,
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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,
|
|
90
|
+
: (0, perm_1.changeSrpHelper)(a, salt, verifier);
|
|
117
91
|
};
|
|
118
|
-
exports.
|
|
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
|
+
};
|
package/src/api.monitor.js
CHANGED
|
@@ -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
|
|
15
|
-
var _a, _b;
|
|
14
|
+
async disconnect({ ip, port }) {
|
|
16
15
|
const match = lodash_1.default.matches({ ip, port });
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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 =
|
|
32
|
-
const isDir = !(0, vfs_1.nodeIsLink)(node) && (!source || stats
|
|
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((
|
|
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),
|
package/src/apiMiddleware.js
CHANGED
|
@@ -17,7 +17,7 @@ class ApiError extends Error {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
exports.ApiError = ApiError;
|
|
20
|
-
const logApi = (0, config_1.defineConfig)(
|
|
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.
|
|
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
|
|
26
|
-
const
|
|
27
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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,
|
|
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-
|
|
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('-');
|