hfs 0.55.0-alpha2 → 0.55.0-beta3
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-DHCaIJ9p.js +809 -0
- package/admin/assets/{sha512-DAt8ySU7.js → sha512-DicKEgNv.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/index-legacy-CBj6uwzd.js +60 -0
- package/frontend/assets/{sha512-legacy-B6NU-5wp.js → sha512-legacy-CxvBXzw1.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/const.js +3 -4
- package/src/cross-const.js +2 -1
- package/src/cross.js +14 -7
- package/src/expiringCache.js +19 -0
- package/src/i18n.js +125 -0
- package/src/lang.js +41 -33
- package/src/langs/hfs-lang-ro.json +20 -18
- package/src/listen.js +11 -9
- package/src/plugins.js +8 -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/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-CBj6uwzd.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-CBj6uwzd.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-beta3",
|
|
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/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.2;
|
|
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-14T14:24:01.619Z";
|
|
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;
|
|
@@ -40,13 +41,14 @@ exports.FRONTEND_OPTIONS = {
|
|
|
40
41
|
sort_numerics: false,
|
|
41
42
|
theme: '',
|
|
42
43
|
auto_play_seconds: 5,
|
|
44
|
+
disableTranslation: false,
|
|
43
45
|
};
|
|
44
46
|
exports.SORT_BY_OPTIONS = ['name', 'extension', 'size', 'time'];
|
|
45
47
|
exports.THEME_OPTIONS = { auto: '', light: 'light', dark: 'dark' };
|
|
46
48
|
exports.CFG = constMap(['geo_enable', 'geo_allow', 'geo_list', 'geo_allow_unknown', 'dynamic_dns_url',
|
|
47
49
|
'log', 'error_log', 'log_rotation', 'dont_log_net', 'log_gui', 'log_api', 'log_ua', 'log_spam', 'track_ips',
|
|
48
50
|
'max_downloads', 'max_downloads_per_ip', 'max_downloads_per_account', 'roots', 'force_address', 'split_uploads',
|
|
49
|
-
'allow_session_ip_change', 'force_lang', 'suspend_plugins']);
|
|
51
|
+
'allow_session_ip_change', 'force_lang', 'suspend_plugins', 'base_url', 'size_1024']);
|
|
50
52
|
exports.LIST = { add: '+', remove: '-', update: '=', props: 'props', ready: 'ready', error: 'e' };
|
|
51
53
|
exports.WHO_ANYONE = true;
|
|
52
54
|
exports.WHO_NO_ONE = false;
|
|
@@ -68,9 +70,11 @@ function isWhoObject(v) {
|
|
|
68
70
|
}
|
|
69
71
|
exports.isWhoObject = isWhoObject;
|
|
70
72
|
const MULTIPLIERS = ['', 'K', 'M', 'G', 'T'];
|
|
71
|
-
function formatBytes(n, { post = 'B', k =
|
|
73
|
+
function formatBytes(n, { post = 'B', k = 0, digits = NaN, sep = ' ' } = {}) {
|
|
74
|
+
var _a;
|
|
72
75
|
if (isNaN(Number(n)) || n < 0)
|
|
73
76
|
return '';
|
|
77
|
+
k || (k = (_a = formatBytes.k) !== null && _a !== void 0 ? _a : 1024); // default value
|
|
74
78
|
const i = n && Math.min(MULTIPLIERS.length - 1, Math.floor(Math.log2(n) / Math.log2(k)));
|
|
75
79
|
n /= k ** i;
|
|
76
80
|
const nAsString = i && !isNaN(digits) ? n.toFixed(digits)
|
|
@@ -79,8 +83,7 @@ function formatBytes(n, { post = 'B', k = 1024, digits = NaN, sep = ' ' } = {})
|
|
|
79
83
|
} // formatBytes
|
|
80
84
|
exports.formatBytes = formatBytes;
|
|
81
85
|
function formatSpeed(n, options = {}) {
|
|
82
|
-
return formatBytes(n, { post: 'B/s',
|
|
83
|
-
.replace('K', 'k'); // ref https://en.wikipedia.org/wiki/Data-rate_units
|
|
86
|
+
return formatBytes(n, { post: 'B/s', ...options });
|
|
84
87
|
}
|
|
85
88
|
exports.formatSpeed = formatSpeed;
|
|
86
89
|
function prefix(pre, v, post = '') {
|
|
@@ -386,6 +389,10 @@ function xlate(input, table) {
|
|
|
386
389
|
return (_a = table[input]) !== null && _a !== void 0 ? _a : input;
|
|
387
390
|
}
|
|
388
391
|
exports.xlate = xlate;
|
|
392
|
+
function normalizeHost(host) {
|
|
393
|
+
return host[0] === '[' ? host.slice(1, host.indexOf(']')) : host === null || host === void 0 ? void 0 : host.split(':')[0];
|
|
394
|
+
}
|
|
395
|
+
exports.normalizeHost = normalizeHost;
|
|
389
396
|
function isIpLocalHost(ip) {
|
|
390
397
|
return ip === '::1' || ip.endsWith('127.0.0.1');
|
|
391
398
|
}
|
|
@@ -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
|
-
"author": "
|
|
2
|
+
"author": "<0x1g3n>",
|
|
3
3
|
"version": 1.0,
|
|
4
|
-
"hfs_version": "0.
|
|
4
|
+
"hfs_version": "0.54.0",
|
|
5
5
|
"translate": {
|
|
6
6
|
"Select": "Selectează",
|
|
7
7
|
"n_files": "{n,plural,one{# fișier} other{# fișiere}}",
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
"select_count": "{n,plural, one{# selectat} other{# selectate}}",
|
|
11
11
|
"filter_placeholder": "Tastează aici pentru a filtra lista de mai jos (caută după cuvinte cheie)",
|
|
12
12
|
"Select some files": "Selectează câteva fișiere",
|
|
13
|
-
"zip_checkboxes": "Folosește căsuțele de selectare pentru a alege fișierele, apoi poți folosi
|
|
14
|
-
"zip_tooltip_selected": "Descarcă elementele selectate
|
|
15
|
-
"zip_tooltip_whole": "Descarcă întreaga listă (nefiltrată) ca un singur fișier
|
|
16
|
-
"zip_confirm_search": "Ești sigur că vrei să descarci TOATE rezultatele acestei căutări sub formă de arhivă ZIP?",
|
|
17
|
-
"zip_confirm_folder": "Ești sigur că vrei să descarci ÎNTREGUL folder sub formă de arhivă ZIP?",
|
|
18
|
-
"select_tooltip": "Selecția se aplică pentru \"
|
|
13
|
+
"zip_checkboxes": "Folosește căsuțele de selectare pentru a alege fișierele, apoi poți folosi ZIP din nou",
|
|
14
|
+
"zip_tooltip_selected": "Descarcă elementele selectate sub un singur fișier .ZIP",
|
|
15
|
+
"zip_tooltip_whole": "Descarcă întreaga listă (nefiltrată) ca un singur fișier .ZIP. Dacă selectezi câteva elemente, doar acelea vor fi descărcate.",
|
|
16
|
+
"zip_confirm_search": "Ești sigur că vrei să descarci TOATE rezultatele acestei căutări sub formă de arhivă .ZIP?",
|
|
17
|
+
"zip_confirm_folder": "Ești sigur că vrei să descarci ÎNTREGUL folder sub formă de arhivă .ZIP?",
|
|
18
|
+
"select_tooltip": "Selecția se aplică pentru \"ZIP\" și \"Șterge\" (când sunt disponibile), dar poți, de asemenea, filtra lista",
|
|
19
19
|
"delete_hint": "Pentru a șterge, prima dată fă clic pe Selectează",
|
|
20
20
|
"delete_confirm": "Ștergi {n,plural, one{# element} other{# elemente}}?",
|
|
21
21
|
"delete_completed": "Ștergere: {n} completată",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"Delete": "Șterge",
|
|
25
25
|
"Options": "Opțiuni",
|
|
26
26
|
"Search": "Căutare",
|
|
27
|
-
"Zip": "
|
|
27
|
+
"Zip": "Arhivă ZIP",
|
|
28
28
|
"search_msg": "Caută în acest folder și în sub-foldere",
|
|
29
29
|
"Searching": "Căutare",
|
|
30
30
|
"Searched": "Căutat",
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"stopped_before": "Oprit înainte de a găsi ceva",
|
|
34
34
|
"empty_list": "Lista este goală. Nu este nimic aici",
|
|
35
35
|
"filter_none": "Niciun rezultat pentru filtrul aplicat",
|
|
36
|
-
"Admin-panel": "Panoul
|
|
36
|
+
"Admin-panel": "Panoul Administratorului",
|
|
37
37
|
"Login": "Autentificare",
|
|
38
|
-
"Username": "Nume utilizator",
|
|
38
|
+
"Username": "Nume de utilizator",
|
|
39
39
|
"Password": "Parolă",
|
|
40
40
|
"login_untrusted": "Autentificare anulată: identitatea serverului nu poate fi de încredere",
|
|
41
|
-
"login_bad_credentials": "Numele de utilizator sau parola sunt incorecte. Te
|
|
42
|
-
"login_bad_cookies": "
|
|
41
|
+
"login_bad_credentials": "Numele de utilizator sau parola sunt incorecte. Te rog să re-efectuezi logarea.",
|
|
42
|
+
"login_bad_cookies": "Cookie-uri nefuncționale - autentificare eșuată",
|
|
43
43
|
"User panel": "Panoul utilizatorului",
|
|
44
44
|
"Change password": "Schimbă parola",
|
|
45
45
|
"enter_pass": "Introdu noua parolă",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"Enter folder name": "Introdu numele folderului",
|
|
66
66
|
"Successfully created": "Creat cu succes",
|
|
67
67
|
"enter_folder": "Intră în folder",
|
|
68
|
-
"folder_exists": "Există deja un
|
|
69
|
-
"Sort by:": "
|
|
68
|
+
"folder_exists": "Există deja un folder cu același nume",
|
|
69
|
+
"Sort by:": "Sortează după: {by}",
|
|
70
70
|
"name": "nume",
|
|
71
71
|
"extension": "extensie",
|
|
72
72
|
"size": "dimensiune",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"auto": "automat",
|
|
79
79
|
"light": "luminos",
|
|
80
80
|
"dark": "întunecat",
|
|
81
|
-
"parent folder": "Folder sursă",
|
|
81
|
+
"parent folder": "Folder sursă (rădăcină)",
|
|
82
82
|
"home": "acasă",
|
|
83
83
|
"Continue": "Continuă",
|
|
84
84
|
"Confirm": "Confirmă",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"in_queue": "{n} în așteptare",
|
|
128
128
|
"enter_comment": "Comentariu pentru {name}",
|
|
129
129
|
"Comment": "Comentariu",
|
|
130
|
-
"upload_dd_hint": "Poți încărca fișiere făcând drag&drop
|
|
130
|
+
"upload_dd_hint": "Poți încărca fișiere făcând drag&drop în lista cu fișierele deja existente",
|
|
131
131
|
"Upload not available": "Încărcarea nu este disponibilă",
|
|
132
132
|
"Cut": "Decupează",
|
|
133
133
|
"n_items": "{n,plural, one{# element} other{# elemente}}",
|
|
@@ -161,6 +161,8 @@
|
|
|
161
161
|
"Show details": "Arată detalii",
|
|
162
162
|
"upload_conflict": "deja există",
|
|
163
163
|
"Logged in": "Autentificat cu succes",
|
|
164
|
-
"Logged out": "Deconectat cu succes"
|
|
164
|
+
"Logged out": "Deconectat cu succes",
|
|
165
|
+
"Cancel": "Anulează",
|
|
166
|
+
"allow_session_ip_change": "Permite schimbarea adresei IP pe durata sesiunilor de logare"
|
|
165
167
|
}
|
|
166
168
|
}
|