hfs 0.55.0-alpha2 → 0.55.0-beta4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/assets/index-ErTf5p78.js +809 -0
- package/admin/assets/{sha512-DAt8ySU7.js → sha512-CqnbmZS9.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/index-legacy-BoMy1vEB.js +60 -0
- package/frontend/assets/{sha512-legacy-B6NU-5wp.js → sha512-legacy-CP10jckG.js} +1 -1
- package/frontend/index.html +1 -1
- package/package.json +2 -2
- package/src/api.accounts.js +10 -0
- package/src/api.plugins.js +5 -1
- package/src/auth.js +4 -4
- package/src/config.js +26 -1
- package/src/consoleLog.js +6 -1
- package/src/const.js +3 -4
- package/src/cross-const.js +2 -1
- package/src/cross.js +15 -7
- package/src/expiringCache.js +19 -0
- package/src/i18n.js +125 -0
- package/src/lang.js +41 -33
- package/src/langs/hfs-lang-fi.json +5 -3
- package/src/langs/hfs-lang-it.json +2 -1
- package/src/langs/hfs-lang-ro.json +20 -18
- package/src/langs/hfs-lang-ru.json +3 -2
- package/src/listen.js +11 -9
- package/src/perm.js +8 -1
- package/src/plugins.js +12 -2
- package/src/serveFile.js +1 -5
- package/src/serveGuiFiles.js +2 -0
- package/src/update.js +4 -1
- package/src/upload.js +6 -8
- package/src/util-os.js +4 -3
- package/src/vfs.js +2 -7
- package/admin/assets/index-CjuGUtTj.js +0 -809
- package/frontend/assets/index-legacy-XBchFTt7.js +0 -60
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
System.register(["./index-legacy-
|
|
1
|
+
System.register(["./index-legacy-BoMy1vEB.js"],(function(h,t){"use strict";var i,s;return{setters:[function(h){i=h.g,s=h.c}],execute:function(){function t(h,t){for(var i=function(){var i=t[s];if("string"!=typeof i&&!Array.isArray(i)){var r=function(t){if("default"!==t&&!(t in h)){var s=Object.getOwnPropertyDescriptor(i,t);s&&Object.defineProperty(h,t,s.get?s:{enumerable:!0,get:function(){return i[t]}})}};for(var e in i)r(e)}},s=0;s<t.length;s++)i();return Object.freeze(Object.defineProperty(h,Symbol.toStringTag,{value:"Module"}))}var r={exports:{}};
|
|
2
2
|
/*
|
|
3
3
|
* [js-sha512]{@link https://github.com/emn178/js-sha512}
|
|
4
4
|
*
|
package/frontend/index.html
CHANGED
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
<style class="removeAtBoot">@media (prefers-color-scheme: dark) { html { background-color: #000; color: #888; } }</style>
|
|
11
11
|
<div id="root">Wait for loading or <a href="?get=basic">use basic interface now</a></div>
|
|
12
12
|
<script crossorigin id="vite-legacy-polyfill" src="/assets/polyfills-legacy-DMrMt_pQ.js"></script>
|
|
13
|
-
<script crossorigin id="vite-legacy-entry" data-src="/assets/index-legacy-
|
|
13
|
+
<script crossorigin id="vite-legacy-entry" data-src="/assets/index-legacy-BoMy1vEB.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
|
|
14
14
|
</body>
|
|
15
15
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hfs",
|
|
3
|
-
"version": "0.55.0-
|
|
3
|
+
"version": "0.55.0-beta4",
|
|
4
4
|
"description": "HTTP File Server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file server",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@gregoranders/csv": "^0.0.13",
|
|
74
74
|
"@koa/router": "^13.0.1",
|
|
75
|
-
"@node-rs/crc32": "^1.10.
|
|
75
|
+
"@node-rs/crc32": "^1.10.6",
|
|
76
76
|
"@rejetto/kvstorage": "^0.13.0",
|
|
77
77
|
"acme-client": "^5.4.0",
|
|
78
78
|
"buffer-crc32": "^1.0.0",
|
package/src/api.accounts.js
CHANGED
|
@@ -16,7 +16,17 @@ function prepareAccount(ac) {
|
|
|
16
16
|
username: ac.username,
|
|
17
17
|
hasPassword: (0, perm_1.accountHasPassword)(ac),
|
|
18
18
|
adminActualAccess: (0, perm_1.accountCanLoginAdmin)(ac),
|
|
19
|
+
canLogin: (0, perm_1.accountHasPassword)(ac) ? (0, perm_1.accountCanLogin)(ac) : undefined,
|
|
19
20
|
invalidated: auth_1.invalidateSessionBefore.get(ac.username),
|
|
21
|
+
members: (0, misc_1.with_)(Object.values(perm_1.accountsConfig.get()), accounts => {
|
|
22
|
+
const ret = [];
|
|
23
|
+
let news = [ac.username];
|
|
24
|
+
while (news.length) {
|
|
25
|
+
news = accounts.filter(a => { var _a; return (_a = a.belongs) === null || _a === void 0 ? void 0 : _a.some(x => news.includes(x)); }).map(x => x.username);
|
|
26
|
+
ret.push(...news);
|
|
27
|
+
}
|
|
28
|
+
return ret.sort();
|
|
29
|
+
})
|
|
20
30
|
};
|
|
21
31
|
}
|
|
22
32
|
exports.default = {
|
package/src/api.plugins.js
CHANGED
|
@@ -14,6 +14,7 @@ const promises_1 = require("fs/promises");
|
|
|
14
14
|
const github_1 = require("./github");
|
|
15
15
|
const const_1 = require("./const");
|
|
16
16
|
const SendList_1 = require("./SendList");
|
|
17
|
+
const immer_1 = __importDefault(require("immer"));
|
|
17
18
|
const apis = {
|
|
18
19
|
get_plugins({}, ctx) {
|
|
19
20
|
const list = new SendList_1.SendListReadable({ addAtStart: [...(0, plugins_1.mapPlugins)(serialize, false), ...(0, plugins_1.getAvailablePlugins)().map(serialize)] });
|
|
@@ -168,10 +169,13 @@ const apis = {
|
|
|
168
169
|
};
|
|
169
170
|
exports.default = apis;
|
|
170
171
|
function serialize(p) {
|
|
171
|
-
|
|
172
|
+
let o = 'getData' in p ? Object.assign(lodash_1.default.pick(p, ['id', 'started']), p.getData())
|
|
172
173
|
: { ...p }; // _.defaults mutates object, and we don't want that
|
|
173
174
|
if (typeof o.repo === 'object') // custom repo
|
|
174
175
|
o.repo = o.repo.web;
|
|
176
|
+
o = (0, immer_1.default)(o, (o) => {
|
|
177
|
+
lodash_1.default.each(o.config, x => x.showIf && (x.showIf = String(x.showIf)));
|
|
178
|
+
});
|
|
175
179
|
return lodash_1.default.defaults(o, { started: null, badApi: null }); // nulls should be used to be sure to overwrite previous values,
|
|
176
180
|
}
|
|
177
181
|
async function checkDependencies(plugin) {
|
package/src/auth.js
CHANGED
|
@@ -9,6 +9,7 @@ const cross_const_1 = require("./cross-const");
|
|
|
9
9
|
const tssrp6a_1 = require("tssrp6a");
|
|
10
10
|
const srp_1 = require("./srp");
|
|
11
11
|
const cross_1 = require("./cross");
|
|
12
|
+
const expiringCache_1 = require("./expiringCache");
|
|
12
13
|
const node_crypto_1 = require("node:crypto");
|
|
13
14
|
const events_1 = __importDefault(require("./events"));
|
|
14
15
|
const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters());
|
|
@@ -23,17 +24,16 @@ async function srpServerStep1(account) {
|
|
|
23
24
|
return { srpServer, salt, pubKey: String(srpServer.B) }; // cast to string cause bigint can't be jsonized
|
|
24
25
|
}
|
|
25
26
|
exports.srpServerStep1 = srpServerStep1;
|
|
26
|
-
const cache =
|
|
27
|
+
const cache = (0, expiringCache_1.expiringCache)(60000);
|
|
27
28
|
async function srpCheck(username, password) {
|
|
28
29
|
const account = (0, perm_1.getAccount)(username);
|
|
29
30
|
if (!(account === null || account === void 0 ? void 0 : account.srp) || !password)
|
|
30
31
|
return;
|
|
31
32
|
const k = (0, node_crypto_1.createHash)('sha256').update(username + password + account.srp).digest("hex");
|
|
32
|
-
const good = await
|
|
33
|
+
const good = await cache.try(k, async () => {
|
|
33
34
|
const { srpServer, salt, pubKey } = await srpServerStep1(account);
|
|
34
35
|
const client = await (0, srp_1.srpClientPart)(username, password, salt, pubKey);
|
|
35
|
-
|
|
36
|
-
return srpServer.step2(client.A, client.M1).then(() => 1, () => 0);
|
|
36
|
+
return srpServer.step2(client.A, client.M1).then(() => true, () => false);
|
|
37
37
|
});
|
|
38
38
|
return good ? account : undefined;
|
|
39
39
|
}
|
package/src/config.js
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
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
|
+
};
|
|
3
26
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
27
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
28
|
};
|
|
@@ -14,6 +37,8 @@ const fs_1 = require("fs");
|
|
|
14
37
|
const path_1 = require("path");
|
|
15
38
|
const events_1 = __importDefault(require("./events"));
|
|
16
39
|
const promises_1 = require("fs/promises");
|
|
40
|
+
const immer_1 = __importStar(require("immer"));
|
|
41
|
+
(0, immer_1.setAutoFreeze)(false); // we still want to mess with objects later (eg: account.belongs)
|
|
17
42
|
// keep definition of config properties
|
|
18
43
|
const configProps = {};
|
|
19
44
|
let started = false; // this will tell the difference for subscribeConfig()s that are called before or after config is loaded
|
|
@@ -78,7 +103,7 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
78
103
|
},
|
|
79
104
|
set(v) {
|
|
80
105
|
if (typeof v === 'function')
|
|
81
|
-
this.set(
|
|
106
|
+
this.set((0, immer_1.default)(this.get(), v));
|
|
82
107
|
else
|
|
83
108
|
setConfig1(k, v);
|
|
84
109
|
},
|
package/src/consoleLog.js
CHANGED
|
@@ -6,16 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.consoleLog = void 0;
|
|
7
7
|
const events_1 = __importDefault(require("./events"));
|
|
8
8
|
const cross_1 = require("./cross");
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const const_1 = require("./const");
|
|
9
11
|
exports.consoleLog = [];
|
|
12
|
+
const f = const_1.argv.consoleFile ? (0, fs_1.createWriteStream)(const_1.argv.consoleFile, 'utf-8') : null;
|
|
10
13
|
for (const k of ['log', 'warn', 'error']) {
|
|
11
14
|
const original = console[k];
|
|
12
15
|
console[k] = (...args) => {
|
|
13
16
|
const ts = new Date();
|
|
14
|
-
const
|
|
17
|
+
const msg = args.join(' ');
|
|
18
|
+
const rec = { ts, k, msg };
|
|
15
19
|
exports.consoleLog.push(rec);
|
|
16
20
|
if (exports.consoleLog.length > 100000) // limit to avoid infinite space
|
|
17
21
|
exports.consoleLog.splice(0, 1000);
|
|
18
22
|
events_1.default.emit('console', rec);
|
|
23
|
+
f === null || f === void 0 ? void 0 : f.write(`${(0, cross_1.formatTimestamp)(ts)} [${k}] ${msg}\n`);
|
|
19
24
|
return original((0, cross_1.formatTime)(ts), ...args); // bundled nodejs doesn't have locales (and apparently uses en-US)
|
|
20
25
|
};
|
|
21
26
|
}
|
package/src/const.js
CHANGED
|
@@ -30,7 +30,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
30
30
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
31
|
};
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.CONFIG_FILE = exports.MIME_AUTO = exports.APP_PATH = exports.IS_BINARY = exports.IS_MAC = exports.IS_WINDOWS = exports.HFS_REPO_BRANCH = exports.RUNNING_BETA = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.ARGS_FILE = exports.argv = exports.
|
|
33
|
+
exports.CONFIG_FILE = exports.MIME_AUTO = exports.APP_PATH = exports.IS_BINARY = exports.IS_MAC = exports.IS_WINDOWS = exports.HFS_REPO_BRANCH = exports.RUNNING_BETA = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.ARGS_FILE = exports.argv = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = void 0;
|
|
34
34
|
const minimist_1 = __importDefault(require("minimist"));
|
|
35
35
|
const fs = __importStar(require("fs"));
|
|
36
36
|
const os_1 = require("os");
|
|
@@ -38,9 +38,8 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
|
38
38
|
const path_1 = require("path");
|
|
39
39
|
const cross_1 = require("./cross");
|
|
40
40
|
__exportStar(require("./cross-const"), exports);
|
|
41
|
-
exports.API_VERSION = 10.
|
|
41
|
+
exports.API_VERSION = 10.3;
|
|
42
42
|
exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
|
|
43
|
-
exports.HFS_REPO = 'rejetto/hfs';
|
|
44
43
|
exports.argv = (0, minimist_1.default)(process.argv.slice(2));
|
|
45
44
|
// you can add arguments with this file, currently used for the update process on mac/linux
|
|
46
45
|
exports.ARGS_FILE = (0, path_1.join)((0, os_1.homedir)(), 'hfs-args');
|
|
@@ -55,7 +54,7 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
|
|
|
55
54
|
exports.ORIGINAL_CWD = process.cwd();
|
|
56
55
|
exports.HFS_STARTED = new Date();
|
|
57
56
|
const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
|
|
58
|
-
exports.BUILD_TIMESTAMP = "2024-12-
|
|
57
|
+
exports.BUILD_TIMESTAMP = "2024-12-19T15:58:01.475Z";
|
|
59
58
|
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
60
59
|
exports.VERSION = pkg.version;
|
|
61
60
|
exports.RUNNING_BETA = exports.VERSION.includes('-');
|
package/src/cross-const.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HTTP_MESSAGES = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_TOO_MANY_REQUESTS = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGIN_CUSTOM_REST_PREFIX = exports.NBSP = exports.PORT_DISABLED = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = void 0;
|
|
3
|
+
exports.HTTP_MESSAGES = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_TOO_MANY_REQUESTS = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.HFS_REPO = exports.PLUGIN_CUSTOM_REST_PREFIX = exports.NBSP = exports.PORT_DISABLED = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = void 0;
|
|
4
4
|
exports.SPECIAL_URI = '/~/';
|
|
5
5
|
exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
|
|
6
6
|
exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
|
|
@@ -9,6 +9,7 @@ exports.PLUGINS_PUB_URI = exports.SPECIAL_URI + 'plugins/';
|
|
|
9
9
|
exports.PORT_DISABLED = -1;
|
|
10
10
|
exports.NBSP = '\xA0';
|
|
11
11
|
exports.PLUGIN_CUSTOM_REST_PREFIX = '_';
|
|
12
|
+
exports.HFS_REPO = 'rejetto/hfs';
|
|
12
13
|
exports.HTTP_OK = 200;
|
|
13
14
|
exports.HTTP_NO_CONTENT = 204;
|
|
14
15
|
exports.HTTP_PARTIAL_CONTENT = 206;
|
package/src/cross.js
CHANGED
|
@@ -18,14 +18,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.waitFor = exports.newObj = exports.findDefined = exports.isOrderedEqual = exports.swap = exports.tryJson = exports.dirname = exports.basename = exports.pendingPromise = exports._dbg = exports._log = exports.wantArray = exports.formatPerc = exports.with_ = exports.try_ = exports.setHidden = exports.onlyTruthy = exports.truthy = exports.stringAfter = exports.splitAt = exports.strinsert = exports.removeStarting = exports.enforceStarting = exports.enforceFinal = exports.objFromKeys = exports.objSameKeys = exports.haveTimeout = exports.wait = exports.join = exports.prefix = exports.formatSpeed = 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 = exports.WEBSITE = void 0;
|
|
21
|
-
exports.shortenAgent = exports.popKey = exports.safeDecodeURIComponent = exports.callable = exports.mapFilter = exports.inCommon = exports.replace = exports.matches = exports.makeMatcher = exports.runAt = exports.pathEncode = exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.isIpLan = exports.isIpLocalHost = exports.xlate = exports.isEqualLax = exports.isTimestampString = exports.isWindowsDrive = exports.isIP = exports.isPrimitive = exports.isNumeric = exports.formatDate = exports.formatTime = exports.formatTimestamp = exports.repeat = exports.asyncGeneratorToArray = exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = exports.objRenameKey = exports.randomId = exports.getOrSet = void 0;
|
|
21
|
+
exports.shortenAgent = exports.popKey = exports.safeDecodeURIComponent = exports.callable = exports.mapFilter = exports.inCommon = exports.replace = exports.matches = exports.makeMatcher = exports.runAt = exports.pathEncode = exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.isIpLan = exports.isIpLocalHost = exports.normalizeHost = exports.xlate = exports.isEqualLax = exports.isTimestampString = exports.isWindowsDrive = exports.isIP = exports.isPrimitive = exports.isNumeric = exports.formatDate = exports.formatTime = exports.formatTimestamp = exports.repeat = exports.asyncGeneratorToArray = exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = exports.objRenameKey = exports.randomId = exports.getOrSet = 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"));
|
|
25
|
-
const picomatch_1 = __importDefault(require("picomatch/lib/picomatch"));
|
|
25
|
+
const picomatch_1 = __importDefault(require("picomatch/lib/picomatch"));
|
|
26
|
+
const cross_const_1 = require("./cross-const"); // point directly to the browser-compatible source
|
|
26
27
|
__exportStar(require("./cross-const"), exports);
|
|
27
28
|
exports.WEBSITE = 'https://rejetto.com/hfs/';
|
|
28
|
-
exports.REPO_URL =
|
|
29
|
+
exports.REPO_URL = `https://github.com/${cross_const_1.HFS_REPO}/`;
|
|
29
30
|
exports.WIKI_URL = exports.REPO_URL + 'wiki/';
|
|
30
31
|
exports.MINUTE = 60000;
|
|
31
32
|
exports.HOUR = 60 * exports.MINUTE;
|
|
@@ -38,15 +39,17 @@ exports.FRONTEND_OPTIONS = {
|
|
|
38
39
|
invert_order: false,
|
|
39
40
|
folders_first: true,
|
|
40
41
|
sort_numerics: false,
|
|
42
|
+
title_with_path: true,
|
|
41
43
|
theme: '',
|
|
42
44
|
auto_play_seconds: 5,
|
|
45
|
+
disableTranslation: false,
|
|
43
46
|
};
|
|
44
47
|
exports.SORT_BY_OPTIONS = ['name', 'extension', 'size', 'time'];
|
|
45
48
|
exports.THEME_OPTIONS = { auto: '', light: 'light', dark: 'dark' };
|
|
46
49
|
exports.CFG = constMap(['geo_enable', 'geo_allow', 'geo_list', 'geo_allow_unknown', 'dynamic_dns_url',
|
|
47
50
|
'log', 'error_log', 'log_rotation', 'dont_log_net', 'log_gui', 'log_api', 'log_ua', 'log_spam', 'track_ips',
|
|
48
51
|
'max_downloads', 'max_downloads_per_ip', 'max_downloads_per_account', 'roots', 'force_address', 'split_uploads',
|
|
49
|
-
'allow_session_ip_change', 'force_lang', 'suspend_plugins']);
|
|
52
|
+
'allow_session_ip_change', 'force_lang', 'suspend_plugins', 'base_url', 'size_1024']);
|
|
50
53
|
exports.LIST = { add: '+', remove: '-', update: '=', props: 'props', ready: 'ready', error: 'e' };
|
|
51
54
|
exports.WHO_ANYONE = true;
|
|
52
55
|
exports.WHO_NO_ONE = false;
|
|
@@ -68,9 +71,11 @@ function isWhoObject(v) {
|
|
|
68
71
|
}
|
|
69
72
|
exports.isWhoObject = isWhoObject;
|
|
70
73
|
const MULTIPLIERS = ['', 'K', 'M', 'G', 'T'];
|
|
71
|
-
function formatBytes(n, { post = 'B', k =
|
|
74
|
+
function formatBytes(n, { post = 'B', k = 0, digits = NaN, sep = ' ' } = {}) {
|
|
75
|
+
var _a;
|
|
72
76
|
if (isNaN(Number(n)) || n < 0)
|
|
73
77
|
return '';
|
|
78
|
+
k || (k = (_a = formatBytes.k) !== null && _a !== void 0 ? _a : 1024); // default value
|
|
74
79
|
const i = n && Math.min(MULTIPLIERS.length - 1, Math.floor(Math.log2(n) / Math.log2(k)));
|
|
75
80
|
n /= k ** i;
|
|
76
81
|
const nAsString = i && !isNaN(digits) ? n.toFixed(digits)
|
|
@@ -79,8 +84,7 @@ function formatBytes(n, { post = 'B', k = 1024, digits = NaN, sep = ' ' } = {})
|
|
|
79
84
|
} // formatBytes
|
|
80
85
|
exports.formatBytes = formatBytes;
|
|
81
86
|
function formatSpeed(n, options = {}) {
|
|
82
|
-
return formatBytes(n, { post: 'B/s',
|
|
83
|
-
.replace('K', 'k'); // ref https://en.wikipedia.org/wiki/Data-rate_units
|
|
87
|
+
return formatBytes(n, { post: 'B/s', ...options });
|
|
84
88
|
}
|
|
85
89
|
exports.formatSpeed = formatSpeed;
|
|
86
90
|
function prefix(pre, v, post = '') {
|
|
@@ -386,6 +390,10 @@ function xlate(input, table) {
|
|
|
386
390
|
return (_a = table[input]) !== null && _a !== void 0 ? _a : input;
|
|
387
391
|
}
|
|
388
392
|
exports.xlate = xlate;
|
|
393
|
+
function normalizeHost(host) {
|
|
394
|
+
return host[0] === '[' ? host.slice(1, host.indexOf(']')) : host === null || host === void 0 ? void 0 : host.split(':')[0];
|
|
395
|
+
}
|
|
396
|
+
exports.normalizeHost = normalizeHost;
|
|
389
397
|
function isIpLocalHost(ip) {
|
|
390
398
|
return ip === '::1' || ip.endsWith('127.0.0.1');
|
|
391
399
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.expiringCache = void 0;
|
|
4
|
+
function expiringCache(ttl) {
|
|
5
|
+
const o = new Map();
|
|
6
|
+
return Object.assign(o, {
|
|
7
|
+
try(k, creator) {
|
|
8
|
+
let ret = o.get(k);
|
|
9
|
+
if (ret === undefined) {
|
|
10
|
+
ret = creator();
|
|
11
|
+
o.set(k, ret);
|
|
12
|
+
Promise.resolve(ret).then(() => // in case of async, wait for it to be done before starting the timer
|
|
13
|
+
setTimeout(() => o.delete(k), ttl));
|
|
14
|
+
}
|
|
15
|
+
return ret;
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
exports.expiringCache = expiringCache;
|
package/src/i18n.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
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.i18nFromTranslations = void 0;
|
|
5
|
+
const valtio_1 = require("valtio");
|
|
6
|
+
const utils_1 = require("valtio/utils");
|
|
7
|
+
function i18nFromTranslations(translations, embedded = 'en') {
|
|
8
|
+
const state = (0, valtio_1.proxy)({
|
|
9
|
+
embedded,
|
|
10
|
+
disabled: false,
|
|
11
|
+
translations, // all dictionaries
|
|
12
|
+
});
|
|
13
|
+
const searchLangs = [];
|
|
14
|
+
(0, utils_1.watch)(get => {
|
|
15
|
+
const snapshot = get(state);
|
|
16
|
+
searchLangs.splice(0, Infinity, 'all', ...Object.keys(snapshot.translations), snapshot.embedded); // replace completely
|
|
17
|
+
});
|
|
18
|
+
const warns = new Set(); // avoid duplicates
|
|
19
|
+
function getLangs() {
|
|
20
|
+
return Object.keys(state.translations);
|
|
21
|
+
}
|
|
22
|
+
// If one of the keys is an "id", that should be the first. If one of the keys should work as a fallback, that should be the last. Use 'fallback' parameter if you don't want the fallback to work as a key.
|
|
23
|
+
function t(keyOrTpl, params, fallback) {
|
|
24
|
+
var _a, _b;
|
|
25
|
+
if (!keyOrTpl)
|
|
26
|
+
return '';
|
|
27
|
+
// memoize?
|
|
28
|
+
const keys = isTemplateStringsArray(keyOrTpl) ? [(fallback !== null && fallback !== void 0 ? fallback : (fallback = keyOrTpl[0]))]
|
|
29
|
+
: Array.isArray(keyOrTpl) ? keyOrTpl : [keyOrTpl];
|
|
30
|
+
if (typeof params === 'string' && !fallback) {
|
|
31
|
+
fallback = params;
|
|
32
|
+
params = null;
|
|
33
|
+
}
|
|
34
|
+
let found;
|
|
35
|
+
let selectedLang = ''; // keep track of where we find the translation
|
|
36
|
+
const { embedded } = state;
|
|
37
|
+
const langs = Object.keys(state.translations);
|
|
38
|
+
if (!state.disabled)
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
for (const lang of searchLangs)
|
|
41
|
+
if (found = (_b = (_a = state.translations[selectedLang = lang]) === null || _a === void 0 ? void 0 : _a.translate) === null || _b === void 0 ? void 0 : _b[key])
|
|
42
|
+
break;
|
|
43
|
+
if (!warns.has(key) && langs.length && langs[0] !== embedded) {
|
|
44
|
+
warns.add(key);
|
|
45
|
+
console.debug("miss i18n:", key);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!found) {
|
|
49
|
+
found = fallback || keys[keys.length - 1];
|
|
50
|
+
selectedLang = embedded;
|
|
51
|
+
}
|
|
52
|
+
return Array.from(tokenizer(found)).map(([s, inside]) => {
|
|
53
|
+
if (!inside)
|
|
54
|
+
return s;
|
|
55
|
+
const [k, cmd, rest] = s.split(',').map(x => x.trim());
|
|
56
|
+
if (!params)
|
|
57
|
+
throw "missing params on " + keys[0];
|
|
58
|
+
const v = k && params[k];
|
|
59
|
+
if (cmd === 'plural' && rest)
|
|
60
|
+
return plural(v, rest);
|
|
61
|
+
return v || v === 0 ? v : '';
|
|
62
|
+
}).join('');
|
|
63
|
+
function plural(v, rest) {
|
|
64
|
+
const plural = !Intl.PluralRules ? 'other'
|
|
65
|
+
: new Intl.PluralRules(selectedLang || embedded).select(Number(v));
|
|
66
|
+
let other = '';
|
|
67
|
+
let pickNext = false;
|
|
68
|
+
let collectOther = false;
|
|
69
|
+
for (const [s, inside] of tokenizer(rest)) {
|
|
70
|
+
if (pickNext)
|
|
71
|
+
return pick(s);
|
|
72
|
+
if (collectOther) {
|
|
73
|
+
other = s;
|
|
74
|
+
collectOther = false;
|
|
75
|
+
}
|
|
76
|
+
if (inside)
|
|
77
|
+
continue;
|
|
78
|
+
const selectors = s.trim().split(/\s+/);
|
|
79
|
+
pickNext = selectors.some(sel => sel[0] === '=' && v === Number(sel.slice(1))
|
|
80
|
+
|| sel === plural);
|
|
81
|
+
collectOther = !pickNext && selectors.includes('other');
|
|
82
|
+
}
|
|
83
|
+
return pick(other);
|
|
84
|
+
function pick(s) {
|
|
85
|
+
return s.replace('#', String(v));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function* tokenizer(s) {
|
|
90
|
+
let ofs = 0;
|
|
91
|
+
while (1) {
|
|
92
|
+
const open = s.indexOf('{', ofs);
|
|
93
|
+
if (open < 0)
|
|
94
|
+
break;
|
|
95
|
+
yield [s.slice(ofs, open), false];
|
|
96
|
+
let stack = 1;
|
|
97
|
+
ofs = open + 1;
|
|
98
|
+
while (stack && ofs < s.length) {
|
|
99
|
+
if (s[ofs] === '{')
|
|
100
|
+
stack++;
|
|
101
|
+
else if (s[ofs] === '}')
|
|
102
|
+
stack--;
|
|
103
|
+
ofs++;
|
|
104
|
+
}
|
|
105
|
+
if (stack)
|
|
106
|
+
return console.debug('tokenizer: unclosed'); // invalid, abort
|
|
107
|
+
yield [s.slice(open + 1, ofs - 1), true];
|
|
108
|
+
}
|
|
109
|
+
yield [s.slice(ofs), false];
|
|
110
|
+
}
|
|
111
|
+
function isTemplateStringsArray(x) {
|
|
112
|
+
return (x === null || x === void 0 ? void 0 : x.raw) && Array.isArray(x);
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
t,
|
|
116
|
+
state,
|
|
117
|
+
getLangs,
|
|
118
|
+
i18nWrapperProps: () => ({ lang: getLangs()[0] || state.embedded }),
|
|
119
|
+
useI18N() {
|
|
120
|
+
(0, valtio_1.useSnapshot)(state);
|
|
121
|
+
return { t };
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
exports.i18nFromTranslations = i18nFromTranslations;
|
package/src/lang.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getLangData = exports.file2code = exports.code2file = void 0;
|
|
7
7
|
const misc_1 = require("./misc");
|
|
8
|
+
const expiringCache_1 = require("./expiringCache");
|
|
8
9
|
const promises_1 = require("fs/promises");
|
|
9
10
|
const config_1 = require("./config");
|
|
10
11
|
const watchLoad_1 = require("./watchLoad");
|
|
@@ -23,42 +24,49 @@ function file2code(fn) {
|
|
|
23
24
|
return fn.replace(PREFIX, '').replace(SUFFIX, '');
|
|
24
25
|
}
|
|
25
26
|
exports.file2code = file2code;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (k)
|
|
51
|
-
|
|
52
|
-
|
|
27
|
+
const cache = (0, expiringCache_1.expiringCache)(3000); // 3 seconds for both a good dx and acceptable performance
|
|
28
|
+
async function getLangData(ctxOrLangCsv) {
|
|
29
|
+
if (typeof ctxOrLangCsv !== 'string') {
|
|
30
|
+
const ctx = ctxOrLangCsv;
|
|
31
|
+
const param = String(ctx.query.lang || '');
|
|
32
|
+
if (!param && forceLangData)
|
|
33
|
+
return forceLangData;
|
|
34
|
+
ctxOrLangCsv = param || ctx.get('Accept-Language') || '';
|
|
35
|
+
}
|
|
36
|
+
const csv = ctxOrLangCsv.toLowerCase();
|
|
37
|
+
return cache.try(csv, async () => {
|
|
38
|
+
var _a;
|
|
39
|
+
const langs = csv.split(',');
|
|
40
|
+
const ret = {};
|
|
41
|
+
let i = 0;
|
|
42
|
+
while (i < langs.length) {
|
|
43
|
+
let k = langs[i] || ''; // shut up ts
|
|
44
|
+
if (!k || k === EMBEDDED_LANGUAGE)
|
|
45
|
+
break;
|
|
46
|
+
const fn = code2file(k);
|
|
47
|
+
try {
|
|
48
|
+
ret[k] = JSON.parse(await (0, promises_1.readFile)(fn, 'utf8'));
|
|
49
|
+
} // allow external files to override embedded translations
|
|
50
|
+
catch (_b) {
|
|
51
|
+
if ((0, misc_1.hasProp)(embedded_1.default, k))
|
|
52
|
+
ret[k] = lodash_1.default.cloneDeep(embedded_1.default[k]);
|
|
53
|
+
else {
|
|
54
|
+
do {
|
|
55
|
+
k = k.substring(0, k.lastIndexOf('-'));
|
|
56
|
+
} while (k && langs.includes(k));
|
|
57
|
+
if (k) {
|
|
58
|
+
langs[i] = k; // overwrite and retry
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
53
61
|
}
|
|
54
62
|
}
|
|
63
|
+
const fromPlugins = (0, misc_1.onlyTruthy)(await Promise.all((0, plugins_1.mapPlugins)(async (pl) => { var _a; return (_a = (0, misc_1.tryJson)(await (0, promises_1.readFile)((0, path_1.join)(pl.folder, fn), 'utf8').catch(() => ''))) === null || _a === void 0 ? void 0 : _a.translate; })));
|
|
64
|
+
if (fromPlugins.length)
|
|
65
|
+
lodash_1.default.defaults((_a = (ret[k] || (ret[k] = {}))).translate || (_a.translate = {}), ...fromPlugins); // be sure we have an entry for k
|
|
66
|
+
i++;
|
|
55
67
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
lodash_1.default.defaults((_a = (ret[k] || (ret[k] = {}))).translate || (_a.translate = {}), ...fromPlugins); // be sure we have an entry for k
|
|
59
|
-
i++;
|
|
60
|
-
}
|
|
61
|
-
return ret;
|
|
68
|
+
return ret;
|
|
69
|
+
});
|
|
62
70
|
}
|
|
63
71
|
exports.getLangData = getLangData;
|
|
64
72
|
let forceLangData;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Pultsari & Win_7",
|
|
3
|
-
"version": 1.
|
|
4
|
-
"hfs_version": "0.
|
|
3
|
+
"version": 1.3,
|
|
4
|
+
"hfs_version": "0.55.0",
|
|
5
5
|
"translate": {
|
|
6
6
|
"Select": "Valitse",
|
|
7
7
|
"n_files": "{n,plural,one{# tiedosto} other{# tiedostoa}}",
|
|
@@ -167,6 +167,8 @@
|
|
|
167
167
|
"Logged in": "Kirjauduttu sisään",
|
|
168
168
|
"Logged out": "Kirjauduttu ulos",
|
|
169
169
|
"Cancel": "Peruuta",
|
|
170
|
-
"allow_session_ip_change": "Salli IP vaihto tämän istunnon aikana"
|
|
170
|
+
"allow_session_ip_change": "Salli IP vaihto tämän istunnon aikana",
|
|
171
|
+
"focus_hint": "Kirjoittamalla näppäimistölläsi, voit etsiä ja korostaa elementtejä listalta",
|
|
172
|
+
"copy_links": "Kopioi linkit"
|
|
171
173
|
}
|
|
172
174
|
}
|
|
@@ -156,6 +156,7 @@
|
|
|
156
156
|
"Logged out": "Arrivederci",
|
|
157
157
|
"Cancel": "Annulla",
|
|
158
158
|
"allow_session_ip_change": "Permetti cambio IP in questa sessione",
|
|
159
|
-
"focus_hint": "Scrivendo sulla tastiera cerchi tra gli elementi della lista."
|
|
159
|
+
"focus_hint": "Scrivendo sulla tastiera cerchi tra gli elementi della lista.",
|
|
160
|
+
"copy_links": "Copia i link"
|
|
160
161
|
}
|
|
161
162
|
}
|