hfs 0.49.2 → 0.50.0-alpha3
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/README.md +2 -0
- package/admin/assets/{index-79c48481.js → index-18a6b5e2.js} +319 -61
- package/admin/assets/{index-1c2b890b.css → index-f3fd8783.css} +1 -1
- package/{frontend/assets/sha512-da48f0d4.js → admin/assets/sha512-58cb94a6.js} +1 -1
- package/admin/index.html +2 -2
- package/central.json +5 -0
- package/frontend/assets/index-629107cf.js +94 -0
- package/frontend/assets/{index-b50efd06.css → index-88f77c85.css} +1 -1
- package/{admin/assets/sha512-bdc531ce.js → frontend/assets/sha512-0e660b94.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +3 -2
- package/plugins/antibrute/plugin.js +1 -1
- package/plugins/vhosting/plugin.js +3 -3
- package/src/adminApis.js +18 -12
- package/src/api.accounts.js +7 -1
- package/src/api.auth.js +7 -31
- package/src/api.file_list.js +6 -1
- package/src/api.monitor.js +10 -6
- package/src/api.vfs.js +6 -8
- package/src/apiMiddleware.js +12 -12
- package/src/auth.js +49 -0
- package/src/connections.js +6 -1
- package/src/const.js +3 -2
- package/src/cross.js +27 -3
- package/src/customHtml.js +1 -1
- package/src/geo.js +61 -0
- package/src/index.js +4 -1
- package/src/log.js +2 -2
- package/src/middlewares.js +24 -21
- package/src/perm.js +1 -6
- package/src/plugins.js +1 -3
- package/src/serveFile.js +2 -2
- package/src/serveGuiFiles.js +3 -6
- package/src/srp.js +22 -0
- package/src/upload.js +2 -2
- package/src/vfs.js +11 -16
- package/src/zip.js +2 -0
- package/frontend/assets/index-5f1c6cb8.js +0 -94
package/src/cross.js
CHANGED
|
@@ -17,8 +17,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.
|
|
21
|
-
exports.runAt = exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.ipLocalHost = exports.xlate = exports.isEqualLax = void 0;
|
|
20
|
+
exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = exports.objRenameKey = exports.randomId = exports.getOrSet = exports.waitFor = exports.newObj = exports.removeStarting = exports.findDefined = exports.isOrderedEqual = exports.swap = exports.tryJson = exports.basename = exports.pendingPromise = exports._dbg = exports._log = exports.wantArray = exports.formatPerc = exports.with_ = exports.try_ = exports.setHidden = exports.onlyTruthy = exports.truthy = exports.splitAt = exports.enforceFinal = exports.objSameKeys = exports.haveTimeout = exports.wait = exports.prefix = exports.formatBytes = exports.isWhoObject = exports.PERM_KEYS = exports.defaultPerms = exports.WHO_ANY_ACCOUNT = exports.WHO_NO_ONE = exports.WHO_ANYONE = exports.LIST = exports.CFG = exports.THEME_OPTIONS = exports.SORT_BY_OPTIONS = exports.FRONTEND_OPTIONS = exports.MAX_TILE_SIZE = exports.DAY = exports.HOUR = exports.MINUTE = exports.WIKI_URL = exports.REPO_URL = void 0;
|
|
21
|
+
exports.runAt = exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.ipLocalHost = exports.xlate = exports.isEqualLax = exports.isWindowsDrive = exports.isIP = exports.isPrimitive = exports.formatTimestamp = exports.repeat = exports.asyncGeneratorToArray = void 0;
|
|
22
22
|
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
23
23
|
// all content here is shared between client and server
|
|
24
24
|
const lodash_1 = __importDefault(require("lodash"));
|
|
@@ -28,7 +28,20 @@ exports.WIKI_URL = exports.REPO_URL + 'wiki/';
|
|
|
28
28
|
exports.MINUTE = 60000;
|
|
29
29
|
exports.HOUR = 60 * exports.MINUTE;
|
|
30
30
|
exports.DAY = 24 * exports.HOUR;
|
|
31
|
-
exports.
|
|
31
|
+
exports.MAX_TILE_SIZE = 10;
|
|
32
|
+
exports.FRONTEND_OPTIONS = {
|
|
33
|
+
file_menu_on_link: true,
|
|
34
|
+
tile_size: 0,
|
|
35
|
+
sort_by: 'name',
|
|
36
|
+
invert_order: false,
|
|
37
|
+
folders_first: true,
|
|
38
|
+
sort_numerics: false,
|
|
39
|
+
theme: '',
|
|
40
|
+
};
|
|
41
|
+
exports.SORT_BY_OPTIONS = ['name', 'extension', 'size', 'time'];
|
|
42
|
+
exports.THEME_OPTIONS = { auto: '', light: 'light', dark: 'dark' };
|
|
43
|
+
exports.CFG = constMap(['geo_enable', 'geo_allow', 'geo_list', 'geo_allow_unknown']);
|
|
44
|
+
exports.LIST = { add: '+', remove: '-', update: '=', props: 'props', ready: 'ready', error: 'e' };
|
|
32
45
|
exports.WHO_ANYONE = true;
|
|
33
46
|
exports.WHO_NO_ONE = false;
|
|
34
47
|
exports.WHO_ANY_ACCOUNT = '*';
|
|
@@ -41,6 +54,9 @@ exports.defaultPerms = {
|
|
|
41
54
|
can_archive: 'can_read'
|
|
42
55
|
};
|
|
43
56
|
exports.PERM_KEYS = typedKeys(exports.defaultPerms);
|
|
57
|
+
function constMap(a) {
|
|
58
|
+
return Object.fromEntries(a.map(x => [x, x]));
|
|
59
|
+
}
|
|
44
60
|
function isWhoObject(v) {
|
|
45
61
|
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
46
62
|
}
|
|
@@ -76,6 +92,13 @@ function enforceFinal(sub, s, evenEmpty = false) {
|
|
|
76
92
|
return !evenEmpty && !s || s.endsWith(sub) ? s : s + sub;
|
|
77
93
|
}
|
|
78
94
|
exports.enforceFinal = enforceFinal;
|
|
95
|
+
function splitAt(sub, all) {
|
|
96
|
+
if (typeof sub === 'number')
|
|
97
|
+
return [all.slice(0, sub), all.slice(sub + 1)];
|
|
98
|
+
const i = all.indexOf(sub);
|
|
99
|
+
return i < 0 ? [all, ''] : [all.slice(0, i), all.slice(i + sub.length)];
|
|
100
|
+
}
|
|
101
|
+
exports.splitAt = splitAt;
|
|
79
102
|
function truthy(value) {
|
|
80
103
|
return Boolean(value);
|
|
81
104
|
}
|
|
@@ -312,6 +335,7 @@ async function promiseBestEffort(promises) {
|
|
|
312
335
|
return res.filter(x => x.status === 'fulfilled').map((x) => x.value);
|
|
313
336
|
}
|
|
314
337
|
exports.promiseBestEffort = promiseBestEffort;
|
|
338
|
+
// run at a specific point in time, also solving the limit of setTimeout, which doesn't work with +32bit delays
|
|
315
339
|
function runAt(ts, cb) {
|
|
316
340
|
let cancel = false;
|
|
317
341
|
let t;
|
package/src/customHtml.js
CHANGED
|
@@ -8,7 +8,7 @@ const promises_1 = require("fs/promises");
|
|
|
8
8
|
const plugins_1 = require("./plugins");
|
|
9
9
|
const FILE = 'custom.html';
|
|
10
10
|
exports.customHtmlSections = ['beforeHeader', 'afterHeader', 'afterMenuBar', 'afterList',
|
|
11
|
-
'top', 'bottom', 'afterEntryName', 'beforeLogin'];
|
|
11
|
+
'top', 'bottom', 'afterEntryName', 'beforeLogin', 'unauthorized'];
|
|
12
12
|
exports.customHtmlState = (0, vanilla_1.proxy)({
|
|
13
13
|
sections: watchLoadCustomHtml().state
|
|
14
14
|
});
|
package/src/geo.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
exports.geoFilter = exports.ip2country = void 0;
|
|
7
|
+
const config_1 = require("./config");
|
|
8
|
+
const misc_1 = require("./misc");
|
|
9
|
+
const promises_1 = require("node:fs/promises");
|
|
10
|
+
const ip2location_nodejs_1 = require("ip2location-nodejs");
|
|
11
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
12
|
+
const connections_1 = require("./connections");
|
|
13
|
+
const ip2location = new ip2location_nodejs_1.IP2Location();
|
|
14
|
+
const enabled = (0, config_1.defineConfig)(misc_1.CFG.geo_enable, false);
|
|
15
|
+
const allow = (0, config_1.defineConfig)(misc_1.CFG.geo_allow, null);
|
|
16
|
+
const list = (0, config_1.defineConfig)(misc_1.CFG.geo_list, []);
|
|
17
|
+
const allowUnknown = (0, config_1.defineConfig)(misc_1.CFG.geo_allow_unknown, false);
|
|
18
|
+
enabled.sub(checkFiles);
|
|
19
|
+
setInterval(checkFiles, misc_1.DAY); // keep updated at run-time
|
|
20
|
+
exports.ip2country = lodash_1.default.memoize((ip) => ip2location.getCountryShortAsync(ip).then(v => v === '-' ? '' : v, () => ''));
|
|
21
|
+
const geoFilter = async (ctx, next) => {
|
|
22
|
+
var _a;
|
|
23
|
+
if (allow.get() !== null && !(0, misc_1.isLocalHost)(ctx)) {
|
|
24
|
+
const { connection } = ctx.state;
|
|
25
|
+
const country = (_a = connection.country) !== null && _a !== void 0 ? _a : (connection.country = await (0, exports.ip2country)(ctx.ip));
|
|
26
|
+
(0, connections_1.updateConnection)(connection, { country });
|
|
27
|
+
if (country ? list.get().includes(country) !== allow.get() : !allowUnknown.get())
|
|
28
|
+
return ctx.socket.destroy();
|
|
29
|
+
}
|
|
30
|
+
return next();
|
|
31
|
+
};
|
|
32
|
+
exports.geoFilter = geoFilter;
|
|
33
|
+
function isOpen() {
|
|
34
|
+
return Boolean(ip2location.getPackageVersion());
|
|
35
|
+
}
|
|
36
|
+
async function checkFiles() {
|
|
37
|
+
var _a, _b;
|
|
38
|
+
if (!enabled.get())
|
|
39
|
+
return;
|
|
40
|
+
const ZIP_FILE = 'IP2LOCATION-LITE-DB1.IPV6.BIN';
|
|
41
|
+
const URL = `https://download.ip2location.com/lite/${ZIP_FILE}.ZIP`;
|
|
42
|
+
const LOCAL_FILE = 'geo_ip.bin';
|
|
43
|
+
const TEMP = LOCAL_FILE + '.downloading';
|
|
44
|
+
const { mtime = 0 } = await (0, promises_1.stat)(LOCAL_FILE).catch(() => ({ mtime: 0 }));
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
if (mtime < now - 31 * misc_1.DAY) { // month-old or non-existing
|
|
47
|
+
console.log('downloading geo-ip db');
|
|
48
|
+
await (0, misc_1.unzip)(await (0, misc_1.httpStream)(URL), path => path.toUpperCase().endsWith(ZIP_FILE) && TEMP);
|
|
49
|
+
if (await (0, promises_1.stat)(TEMP))
|
|
50
|
+
if (isOpen())
|
|
51
|
+
ip2location.close();
|
|
52
|
+
await (0, promises_1.unlink)(LOCAL_FILE).catch(() => { });
|
|
53
|
+
await (0, promises_1.rename)(TEMP, LOCAL_FILE);
|
|
54
|
+
(_b = (_a = exports.ip2country.cache).clear) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
55
|
+
console.log('download geo-ip db completed');
|
|
56
|
+
}
|
|
57
|
+
else if (isOpen())
|
|
58
|
+
return;
|
|
59
|
+
console.debug('loading geo-ip db');
|
|
60
|
+
ip2location.open(LOCAL_FILE); // using openAsync causes a DEP0137 error within 10 seconds
|
|
61
|
+
}
|
package/src/index.js
CHANGED
|
@@ -27,16 +27,19 @@ const misc_1 = require("./misc");
|
|
|
27
27
|
const koa_session_1 = __importDefault(require("koa-session"));
|
|
28
28
|
const selfCheck_1 = require("./selfCheck");
|
|
29
29
|
const acme_1 = require("./acme");
|
|
30
|
+
require("./geo");
|
|
31
|
+
const geo_1 = require("./geo");
|
|
30
32
|
(0, assert_1.ok)(lodash_1.default.intersection(Object.keys(frontEndApis_1.frontEndApis), Object.keys(adminApis_1.adminApis)).length === 0); // they share same endpoints, don't clash
|
|
31
33
|
process.title = 'HFS ' + const_1.VERSION;
|
|
32
34
|
const keys = ((_a = process.env.COOKIE_SIGN_KEYS) === null || _a === void 0 ? void 0 : _a.split(','))
|
|
33
35
|
|| [(0, misc_1.randomId)(30)]; // randomness at start gives some extra security, btu also invalidates existing sessions
|
|
34
36
|
exports.app = new koa_1.default({ keys });
|
|
35
37
|
exports.app.use(middlewares_1.someSecurity)
|
|
36
|
-
.use(selfCheck_1.selfCheckMiddleware)
|
|
37
38
|
.use(acme_1.acmeMiddleware)
|
|
38
39
|
.use((0, koa_session_1.default)({ key: 'hfs_$id', signed: true, rolling: true, sameSite: 'lax' }, exports.app))
|
|
39
40
|
.use(middlewares_1.prepareState)
|
|
41
|
+
.use(geo_1.geoFilter)
|
|
42
|
+
.use(selfCheck_1.selfCheckMiddleware)
|
|
40
43
|
.use(middlewares_1.gzipper)
|
|
41
44
|
.use(middlewares_1.paramsDecoder) // must be done before plugins, so they can manipulate params
|
|
42
45
|
.use(plugins_1.pluginsMiddleware)
|
package/src/log.js
CHANGED
|
@@ -35,7 +35,7 @@ const util = __importStar(require("util"));
|
|
|
35
35
|
const promises_1 = require("fs/promises");
|
|
36
36
|
const lodash_1 = __importDefault(require("lodash"));
|
|
37
37
|
const util_files_1 = require("./util-files");
|
|
38
|
-
const
|
|
38
|
+
const auth_1 = require("./auth");
|
|
39
39
|
const misc_1 = require("./misc");
|
|
40
40
|
const events_1 = __importDefault(require("./events"));
|
|
41
41
|
class Logger {
|
|
@@ -122,7 +122,7 @@ const logMw = async (ctx, next) => {
|
|
|
122
122
|
const format = '%s - %s [%s] "%s %s HTTP/%s" %d %s %s\n'; // Apache's Common Log Format
|
|
123
123
|
const a = now.toString().split(' ');
|
|
124
124
|
const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + ((_b = a[5]) === null || _b === void 0 ? void 0 : _b.slice(3));
|
|
125
|
-
const user = (0,
|
|
125
|
+
const user = (0, auth_1.getCurrentUsername)(ctx);
|
|
126
126
|
const length = (_c = ctx.state.length) !== null && _c !== void 0 ? _c : ctx.length;
|
|
127
127
|
const uri = ctx.originalUrl;
|
|
128
128
|
const extra = ctx.state.includesLastByte && ctx.vfsNode && ctx.res.finished && { dl: 1 }
|
package/src/middlewares.js
CHANGED
|
@@ -19,8 +19,7 @@ const block_1 = require("./block");
|
|
|
19
19
|
const perm_1 = require("./perm");
|
|
20
20
|
const connections_1 = require("./connections");
|
|
21
21
|
const basic_auth_1 = __importDefault(require("basic-auth"));
|
|
22
|
-
const
|
|
23
|
-
const api_auth_1 = require("./api.auth");
|
|
22
|
+
const auth_1 = require("./auth");
|
|
24
23
|
const path_1 = require("path");
|
|
25
24
|
const promises_1 = require("stream/promises");
|
|
26
25
|
const formidable_1 = __importDefault(require("formidable"));
|
|
@@ -202,36 +201,40 @@ function getProxyDetected() {
|
|
|
202
201
|
}
|
|
203
202
|
exports.getProxyDetected = getProxyDetected;
|
|
204
203
|
const prepareState = async (ctx, next) => {
|
|
205
|
-
var _a
|
|
206
|
-
if (ctx.session)
|
|
204
|
+
var _a;
|
|
205
|
+
if (ctx.session) {
|
|
206
|
+
if (auth_1.invalidSessions.delete(ctx.session.username))
|
|
207
|
+
delete ctx.session.username;
|
|
207
208
|
ctx.session.maxAge = exports.sessionDuration.compiled();
|
|
209
|
+
}
|
|
208
210
|
// calculate these once and for all
|
|
209
|
-
const a = ctx.state.account = (
|
|
211
|
+
const a = ctx.state.account = await urlLogin() || await getHttpAccount() || (0, perm_1.getAccount)((_a = ctx.session) === null || _a === void 0 ? void 0 : _a.username, false);
|
|
210
212
|
if (a && !(0, perm_1.accountCanLogin)(a))
|
|
211
213
|
ctx.state.account = undefined;
|
|
212
214
|
const conn = ctx.state.connection = (0, connections_1.socket2connection)(ctx.socket);
|
|
213
215
|
ctx.state.revProxyPath = ctx.get('x-forwarded-prefix');
|
|
216
|
+
ctx.state.browsing = undefined;
|
|
214
217
|
if (conn)
|
|
215
218
|
(0, connections_1.updateConnection)(conn, { ctx, op: undefined });
|
|
216
219
|
await next();
|
|
220
|
+
async function urlLogin() {
|
|
221
|
+
const { login } = ctx.query;
|
|
222
|
+
if (!login)
|
|
223
|
+
return;
|
|
224
|
+
const [u, p] = (0, misc_1.splitAt)(':', String(login));
|
|
225
|
+
const a = await (0, auth_1.srpCheck)(u, p);
|
|
226
|
+
if (a) {
|
|
227
|
+
ctx.session.username = a.username;
|
|
228
|
+
ctx.redirect(ctx.originalUrl.slice(0, -ctx.querystring.length - 1));
|
|
229
|
+
}
|
|
230
|
+
return a;
|
|
231
|
+
}
|
|
232
|
+
async function getHttpAccount() {
|
|
233
|
+
const credentials = (0, basic_auth_1.default)(ctx.req);
|
|
234
|
+
return (0, auth_1.srpCheck)((credentials === null || credentials === void 0 ? void 0 : credentials.name) || '', (credentials === null || credentials === void 0 ? void 0 : credentials.pass) || '');
|
|
235
|
+
}
|
|
217
236
|
};
|
|
218
237
|
exports.prepareState = prepareState;
|
|
219
|
-
async function getHttpAccount(ctx) {
|
|
220
|
-
const credentials = (0, basic_auth_1.default)(ctx.req);
|
|
221
|
-
const account = (0, perm_1.getAccount)((credentials === null || credentials === void 0 ? void 0 : credentials.name) || '');
|
|
222
|
-
if (account && await srpCheck(account.username, credentials.pass))
|
|
223
|
-
return account;
|
|
224
|
-
}
|
|
225
|
-
async function srpCheck(username, password) {
|
|
226
|
-
const account = (0, perm_1.getAccount)(username);
|
|
227
|
-
if (!(account === null || account === void 0 ? void 0 : account.srp) || !password)
|
|
228
|
-
return false;
|
|
229
|
-
const { step1, salt, pubKey } = await (0, api_auth_1.srpStep1)(account);
|
|
230
|
-
const client = new tssrp6a_1.SRPClientSession(new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters()));
|
|
231
|
-
const clientRes1 = await client.step1(username, password);
|
|
232
|
-
const clientRes2 = await clientRes1.step2(BigInt(salt), BigInt(pubKey));
|
|
233
|
-
return await step1.step2(clientRes2.A, clientRes2.M1).then(() => true, () => false);
|
|
234
|
-
}
|
|
235
238
|
const paramsDecoder = async (ctx, next) => {
|
|
236
239
|
ctx.params = ctx.method === 'POST' && ctx.originalUrl.startsWith(const_1.API_URI)
|
|
237
240
|
&& ((0, misc_1.tryJson)(await (0, misc_1.stream2string)(ctx.req)) || {});
|
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.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.createAdmin = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.expandUsername =
|
|
7
|
+
exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.createAdmin = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.expandUsername = void 0;
|
|
8
8
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
9
|
const crypt_1 = require("./crypt");
|
|
10
10
|
const misc_1 = require("./misc");
|
|
@@ -12,11 +12,6 @@ const config_1 = require("./config");
|
|
|
12
12
|
const tssrp6a_1 = require("tssrp6a");
|
|
13
13
|
const events_1 = __importDefault(require("./events"));
|
|
14
14
|
let accounts = {};
|
|
15
|
-
function getCurrentUsername(ctx) {
|
|
16
|
-
var _a;
|
|
17
|
-
return ((_a = ctx.state.account) === null || _a === void 0 ? void 0 : _a.username) || '';
|
|
18
|
-
}
|
|
19
|
-
exports.getCurrentUsername = getCurrentUsername;
|
|
20
15
|
// provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
|
|
21
16
|
function expandUsername(who) {
|
|
22
17
|
const ret = [];
|
package/src/plugins.js
CHANGED
|
@@ -35,7 +35,6 @@ const const_1 = require("./const");
|
|
|
35
35
|
const Const = __importStar(require("./const"));
|
|
36
36
|
const misc_1 = require("./misc");
|
|
37
37
|
const config_1 = require("./config");
|
|
38
|
-
const vfs_1 = require("./vfs");
|
|
39
38
|
const serveFile_1 = require("./serveFile");
|
|
40
39
|
const events_1 = __importDefault(require("./events"));
|
|
41
40
|
const promises_1 = require("fs/promises");
|
|
@@ -120,7 +119,6 @@ exports.getPluginConfigFields = getPluginConfigFields;
|
|
|
120
119
|
async function initPlugin(pl, more) {
|
|
121
120
|
var _a;
|
|
122
121
|
return Object.assign(pl, await ((_a = pl.init) === null || _a === void 0 ? void 0 : _a.call(pl, {
|
|
123
|
-
const: Const,
|
|
124
122
|
Const,
|
|
125
123
|
require,
|
|
126
124
|
getConnections: connections_1.getConnections,
|
|
@@ -154,7 +152,7 @@ const pluginsMiddleware = async (ctx, next) => {
|
|
|
154
152
|
const a = path.substring(const_1.PLUGINS_PUB_URI.length).split('/');
|
|
155
153
|
const name = a.shift();
|
|
156
154
|
if (plugins.hasOwnProperty(name)) // do it only if the plugin is loaded
|
|
157
|
-
await (0, serveFile_1.serveFile)(ctx, plugins[name].folder + '/public/' + a.join('/'),
|
|
155
|
+
await (0, serveFile_1.serveFile)(ctx, plugins[name].folder + '/public/' + a.join('/'), const_1.MIME_AUTO);
|
|
158
156
|
return;
|
|
159
157
|
}
|
|
160
158
|
await next();
|
package/src/serveFile.js
CHANGED
|
@@ -38,13 +38,13 @@ function serveFileNode(ctx, node) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
exports.serveFileNode = serveFileNode;
|
|
41
|
-
const mimeCfg = (0, config_1.defineConfig)('mime', { '*':
|
|
41
|
+
const mimeCfg = (0, config_1.defineConfig)('mime', { '*': const_1.MIME_AUTO });
|
|
42
42
|
async function serveFile(ctx, source, mime, content) {
|
|
43
43
|
if (!source)
|
|
44
44
|
return;
|
|
45
45
|
const fn = path_1.default.basename(source);
|
|
46
46
|
mime = mime !== null && mime !== void 0 ? mime : lodash_1.default.find(mimeCfg.get(), (v, k) => (0, misc_1.matches)(fn, k));
|
|
47
|
-
if (mime ===
|
|
47
|
+
if (mime === const_1.MIME_AUTO)
|
|
48
48
|
mime = mime_types_1.default.lookup(source) || '';
|
|
49
49
|
if (mime)
|
|
50
50
|
ctx.type = mime;
|
package/src/serveGuiFiles.js
CHANGED
|
@@ -42,8 +42,8 @@ const customHtml_1 = require("./customHtml");
|
|
|
42
42
|
const lodash_1 = __importDefault(require("lodash"));
|
|
43
43
|
const config_1 = require("./config");
|
|
44
44
|
const lang_1 = require("./lang");
|
|
45
|
-
const vfs_1 = require("./vfs");
|
|
46
45
|
const logGui = (0, config_1.defineConfig)('log_gui', false);
|
|
46
|
+
lodash_1.default.each(misc_1.FRONTEND_OPTIONS, (v, k) => (0, config_1.defineConfig)(k, v)); // define default values
|
|
47
47
|
// in case of dev env we have our static files within the 'dist' folder'
|
|
48
48
|
const DEV_STATIC = process.env.DEV ? 'dist/' : '';
|
|
49
49
|
function serveStatic(uri) {
|
|
@@ -70,7 +70,7 @@ function serveStatic(uri) {
|
|
|
70
70
|
if (content === null)
|
|
71
71
|
return ctx.status = const_1.HTTP_NOT_FOUND;
|
|
72
72
|
if (!serveApp)
|
|
73
|
-
return (0, serveFile_1.serveFile)(ctx, fullPath,
|
|
73
|
+
return (0, serveFile_1.serveFile)(ctx, fullPath, const_1.MIME_AUTO, content);
|
|
74
74
|
// we don't cache the index as it's small and may prevent plugins change to apply
|
|
75
75
|
ctx.body = await treatIndex(ctx, uri, String(content));
|
|
76
76
|
};
|
|
@@ -119,8 +119,7 @@ async function treatIndex(ctx, filesUri, body) {
|
|
|
119
119
|
plugins,
|
|
120
120
|
prefixUrl: ctx.state.revProxyPath,
|
|
121
121
|
customHtml: lodash_1.default.omit(Object.fromEntries(customHtml_1.customHtmlState.sections), ['top', 'bottom']),
|
|
122
|
-
|
|
123
|
-
tilesSize: tilesSize.get(),
|
|
122
|
+
...(0, misc_1.newObj)(misc_1.FRONTEND_OPTIONS, (v, k) => (0, config_1.getConfig)(k)),
|
|
124
123
|
lang
|
|
125
124
|
}, null, 4)
|
|
126
125
|
.replace(/<(\/script)/g, '<"+"$1') /*avoid breaking our script container*/}
|
|
@@ -175,5 +174,3 @@ function serveGuiFiles(proxyPort, uri) {
|
|
|
175
174
|
return serveProxied(proxyPort, uri) || serveStatic(uri);
|
|
176
175
|
}
|
|
177
176
|
exports.serveGuiFiles = serveGuiFiles;
|
|
178
|
-
const fileMenuOnLink = (0, config_1.defineConfig)('file_menu_on_link', true);
|
|
179
|
-
const tilesSize = (0, config_1.defineConfig)('tiles_size', 0);
|
package/src/srp.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.srpClientPart = exports.srpClientSequence = void 0;
|
|
5
|
+
const tssrp6a_1 = require("tssrp6a");
|
|
6
|
+
async function srpClientSequence(username, password, apiCall) {
|
|
7
|
+
const { pubKey, salt } = await apiCall('loginSrp1', { username });
|
|
8
|
+
if (!salt)
|
|
9
|
+
throw Error('salt');
|
|
10
|
+
const client = await srpClientPart(username, password, salt, pubKey);
|
|
11
|
+
const res = await apiCall('loginSrp2', { pubKey: String(client.A), proof: String(client.M1) }); // bigint-s must be cast to string to be json-ed
|
|
12
|
+
await client.step3(BigInt(res.proof)).catch(() => Promise.reject('trust'));
|
|
13
|
+
return res;
|
|
14
|
+
}
|
|
15
|
+
exports.srpClientSequence = srpClientSequence;
|
|
16
|
+
async function srpClientPart(username, password, salt, pubKey) {
|
|
17
|
+
const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters());
|
|
18
|
+
const srp = new tssrp6a_1.SRPClientSession(srp6aNimbusRoutines);
|
|
19
|
+
const res = await srp.step1(username, password);
|
|
20
|
+
return await res.step2(BigInt(salt), BigInt(pubKey));
|
|
21
|
+
}
|
|
22
|
+
exports.srpClientPart = srpClientPart;
|
package/src/upload.js
CHANGED
|
@@ -14,7 +14,7 @@ const config_1 = require("./config");
|
|
|
14
14
|
const util_os_1 = require("./util-os");
|
|
15
15
|
const connections_1 = require("./connections");
|
|
16
16
|
const throttler_1 = require("./throttler");
|
|
17
|
-
const
|
|
17
|
+
const auth_1 = require("./auth");
|
|
18
18
|
const comments_1 = require("./comments");
|
|
19
19
|
exports.deleteUnfinishedUploadsAfter = (0, config_1.defineConfig)('delete_unfinished_uploads_after', 86400);
|
|
20
20
|
exports.minAvailableMb = (0, config_1.defineConfig)('min_available_mb', 100);
|
|
@@ -27,7 +27,7 @@ function getUploadMeta(path) {
|
|
|
27
27
|
exports.getUploadMeta = getUploadMeta;
|
|
28
28
|
function setUploadMeta(path, ctx) {
|
|
29
29
|
return (0, misc_1.storeFileAttr)(path, ATTR_UPLOADER, {
|
|
30
|
-
username: (0,
|
|
30
|
+
username: (0, auth_1.getCurrentUsername)(ctx) || undefined,
|
|
31
31
|
ip: ctx.ip,
|
|
32
32
|
});
|
|
33
33
|
}
|
package/src/vfs.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.parentMaskApplier = exports.masksCouldGivePermission = exports.walkNode = exports.statusCodeForMissingPerm = exports.hasPermission = exports.nodeIsDirectory = exports.getNodeName = exports.saveVfs = exports.vfs = exports.urlToNode = exports.applyParentToChild = exports.isSameFilenameAs = exports.permsFromParent =
|
|
7
|
+
exports.parentMaskApplier = exports.masksCouldGivePermission = exports.walkNode = exports.statusCodeForMissingPerm = exports.hasPermission = exports.nodeIsLink = exports.nodeIsDirectory = exports.getNodeName = exports.saveVfs = exports.vfs = exports.urlToNode = exports.applyParentToChild = exports.isSameFilenameAs = exports.permsFromParent = void 0;
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
9
|
const path_1 = require("path");
|
|
10
10
|
const misc_1 = require("./misc");
|
|
@@ -13,7 +13,7 @@ const config_1 = require("./config");
|
|
|
13
13
|
const const_1 = require("./const");
|
|
14
14
|
const events_1 = __importDefault(require("./events"));
|
|
15
15
|
const perm_1 = require("./perm");
|
|
16
|
-
|
|
16
|
+
const auth_1 = require("./auth");
|
|
17
17
|
function permsFromParent(parent, child) {
|
|
18
18
|
const ret = {};
|
|
19
19
|
for (const k of misc_1.PERM_KEYS) {
|
|
@@ -100,7 +100,7 @@ async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
|
|
|
100
100
|
}
|
|
101
101
|
ret.source = (0, misc_1.enforceFinal)('/', parent.source) + onDisk;
|
|
102
102
|
if (parent.default)
|
|
103
|
-
inheritFromParent({ mime: { '*':
|
|
103
|
+
inheritFromParent({ mime: { '*': const_1.MIME_AUTO } }, ret);
|
|
104
104
|
if (rest)
|
|
105
105
|
return urlToNode(rest, ctx, ret, getRest);
|
|
106
106
|
if (ret.source)
|
|
@@ -119,12 +119,6 @@ async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
|
|
|
119
119
|
exports.urlToNode = urlToNode;
|
|
120
120
|
exports.vfs = {};
|
|
121
121
|
(0, config_1.defineConfig)('vfs', {}).sub(data => exports.vfs = (function recur(node) {
|
|
122
|
-
if (node.propagate) { // legacy pre-0.47
|
|
123
|
-
for (const [k, v] of (0, misc_1.typedEntries)(node.propagate))
|
|
124
|
-
if (v === false)
|
|
125
|
-
node[k] = { this: node[k] };
|
|
126
|
-
delete node.propagate;
|
|
127
|
-
}
|
|
128
122
|
if (node.children)
|
|
129
123
|
for (const c of node.children)
|
|
130
124
|
recur(c);
|
|
@@ -154,14 +148,15 @@ async function nodeIsDirectory(node) {
|
|
|
154
148
|
var _a;
|
|
155
149
|
if (node.isFolder !== undefined)
|
|
156
150
|
return node.isFolder;
|
|
157
|
-
const isFolder = Boolean(((_a = node.children) === null || _a === void 0 ? void 0 : _a.length) || !node.source || await (0, misc_1.isDirectory)(node.source));
|
|
158
|
-
|
|
159
|
-
node.isFolder = isFolder;
|
|
160
|
-
else
|
|
161
|
-
(0, misc_1.setHidden)(node, { isFolder }); // don't make it to the storage
|
|
151
|
+
const isFolder = Boolean(((_a = node.children) === null || _a === void 0 ? void 0 : _a.length) || !nodeIsLink(node) && (!node.source || await (0, misc_1.isDirectory)(node.source)));
|
|
152
|
+
(0, misc_1.setHidden)(node, { isFolder }); // don't make it to the storage (a node.isTemp doesn't need it to be hidden)
|
|
162
153
|
return isFolder;
|
|
163
154
|
}
|
|
164
155
|
exports.nodeIsDirectory = nodeIsDirectory;
|
|
156
|
+
function nodeIsLink(node) {
|
|
157
|
+
return node.url;
|
|
158
|
+
}
|
|
159
|
+
exports.nodeIsLink = nodeIsLink;
|
|
165
160
|
function hasPermission(node, perm, ctx) {
|
|
166
161
|
return !statusCodeForMissingPerm(node, perm, ctx, false);
|
|
167
162
|
}
|
|
@@ -189,12 +184,12 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
|
189
184
|
if (Array.isArray(who)) {
|
|
190
185
|
const arr = who; // shut up ts
|
|
191
186
|
// check if I or any ancestor match `who`, but cache ancestors' usernames inside context state
|
|
192
|
-
const some = (0, misc_1.getOrSet)(ctx.state, 'usernames', () => (0, perm_1.expandUsername)((0,
|
|
187
|
+
const some = (0, misc_1.getOrSet)(ctx.state, 'usernames', () => (0, perm_1.expandUsername)((0, auth_1.getCurrentUsername)(ctx)))
|
|
193
188
|
.some((u) => arr.includes(u));
|
|
194
189
|
return some ? 0 : const_1.HTTP_UNAUTHORIZED;
|
|
195
190
|
}
|
|
196
191
|
return typeof who === 'boolean' ? (who ? 0 : const_1.HTTP_FORBIDDEN)
|
|
197
|
-
: who === misc_1.WHO_ANY_ACCOUNT ? (ctx
|
|
192
|
+
: who === misc_1.WHO_ANY_ACCOUNT ? ((0, auth_1.getCurrentUsername)(ctx) ? 0 : const_1.HTTP_UNAUTHORIZED)
|
|
198
193
|
: (0, misc_1.throw_)(Error('invalid permission: ' + JSON.stringify(who)));
|
|
199
194
|
}
|
|
200
195
|
}
|
package/src/zip.js
CHANGED
|
@@ -43,6 +43,8 @@ async function zipStreamFromFolder(node, ctx) {
|
|
|
43
43
|
}
|
|
44
44
|
})();
|
|
45
45
|
const mappedWalker = (0, misc_1.filterMapGenerator)(walker, async (el) => {
|
|
46
|
+
if ((0, vfs_1.nodeIsLink)(el))
|
|
47
|
+
return;
|
|
46
48
|
if (!(0, vfs_1.hasPermission)(el, 'can_archive', ctx))
|
|
47
49
|
return; // the fact you see it doesn't mean you can get it
|
|
48
50
|
const { source } = el;
|