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,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
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Mefistofell, SanokKule, ItsNeverLight",
|
|
3
3
|
"version": 2.6,
|
|
4
|
-
"hfs_version": "0.
|
|
4
|
+
"hfs_version": "0.55.0",
|
|
5
5
|
"translate": {
|
|
6
6
|
"Select": "Выбор",
|
|
7
7
|
"n_files": "{n,plural, one{# файл} few{# файла} many{# файлов}}",
|
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
"Logged out": "Выход выполнен",
|
|
169
169
|
"Cancel": "Отмена",
|
|
170
170
|
"allow_session_ip_change": "Разрешить смену IP в этой сессии",
|
|
171
|
-
"focus_hint": "Набирая текст на клавиатуре, вы можете выполнять поиск и выбирать элементы из списка."
|
|
171
|
+
"focus_hint": "Набирая текст на клавиатуре, вы можете выполнять поиск и выбирать элементы из списка.",
|
|
172
|
+
"copy_links": "Копировать ссылки"
|
|
172
173
|
}
|
|
173
174
|
}
|
package/src/listen.js
CHANGED
|
@@ -49,7 +49,7 @@ const persistence_1 = require("./persistence");
|
|
|
49
49
|
let httpSrv;
|
|
50
50
|
let httpsSrv;
|
|
51
51
|
const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', !const_1.DEV);
|
|
52
|
-
exports.baseUrl = (0, config_1.defineConfig)(
|
|
52
|
+
exports.baseUrl = (0, config_1.defineConfig)(misc_1.CFG.base_url, '', x => { var _a; return (_a = /(?<=\/\/)[^\/]+/.exec(x)) === null || _a === void 0 ? void 0 : _a[0]; }); // compiled is host only
|
|
53
53
|
async function getBaseUrlOrDefault() {
|
|
54
54
|
return exports.baseUrl.get() || await nat_1.defaultBaseUrl.get();
|
|
55
55
|
}
|
|
@@ -163,7 +163,7 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
|
163
163
|
events_1.default.emit('httpsReady');
|
|
164
164
|
nat_1.defaultBaseUrl.proto = 'https';
|
|
165
165
|
nat_1.defaultBaseUrl.port = (_f = getCurrentPort(httpsSrv)) !== null && _f !== void 0 ? _f : 0;
|
|
166
|
-
});
|
|
166
|
+
}, { wait: 200 }); // give time to have key and cert ready
|
|
167
167
|
exports.cert = (0, config_1.defineConfig)('cert', '');
|
|
168
168
|
exports.privateKey = (0, config_1.defineConfig)('private_key', '');
|
|
169
169
|
const httpsNeeds = [exports.cert, exports.privateKey];
|
|
@@ -180,7 +180,6 @@ for (const cfg of httpsNeeds) {
|
|
|
180
180
|
httpsOptions[k] = '';
|
|
181
181
|
unwatch = (0, watchLoad_1.watchLoad)(v, async (data) => {
|
|
182
182
|
httpsOptions[k] = data;
|
|
183
|
-
await (0, misc_1.wait)(500); // when a file is written, give some time for the other to be ready
|
|
184
183
|
await considerHttps();
|
|
185
184
|
}, { immediateFirst: true }).unwatch;
|
|
186
185
|
await considerHttps();
|
|
@@ -201,8 +200,10 @@ function startServer(srv, { port, host }) {
|
|
|
201
200
|
if (!srv)
|
|
202
201
|
return resolve(0);
|
|
203
202
|
try {
|
|
204
|
-
if (port === const_1.PORT_DISABLED
|
|
203
|
+
if (port === const_1.PORT_DISABLED)
|
|
205
204
|
return resolve(0);
|
|
205
|
+
if (!host && !await testIpV4()) // !host means ipV4+6, and if v4 port alone is busy we won't be notified of the failure, so we'll first test it on its own
|
|
206
|
+
throw srv.error;
|
|
206
207
|
// from a few tests, this seems enough to support the expect-100 http/1.1 mechanism, at least with curl -T, not used by chrome|firefox anyway
|
|
207
208
|
srv.on('checkContinue', (req, res) => srv.emit('request', req, res));
|
|
208
209
|
port = await listen(host);
|
|
@@ -212,16 +213,16 @@ function startServer(srv, { port, host }) {
|
|
|
212
213
|
}
|
|
213
214
|
catch (e) {
|
|
214
215
|
srv.error = String(e);
|
|
215
|
-
console.error(srv.name,
|
|
216
|
+
console.error(srv.name, `couldn't listen on port ${port}:`, srv.error);
|
|
216
217
|
resolve(0);
|
|
217
218
|
}
|
|
218
219
|
});
|
|
219
220
|
async function testIpV4() {
|
|
220
|
-
const res = await listen('0.0.0.0');
|
|
221
|
-
await new Promise(res => srv === null || srv === void 0 ? void 0 : srv.close(res));
|
|
221
|
+
const res = await listen('0.0.0.0', true);
|
|
222
|
+
await new Promise(res => srv === null || srv === void 0 ? void 0 : srv.close(res)); // close, if any, and wait
|
|
222
223
|
return res > 0;
|
|
223
224
|
}
|
|
224
|
-
function listen(host) {
|
|
225
|
+
function listen(host, silence = false) {
|
|
225
226
|
return new Promise(async (resolve, reject) => {
|
|
226
227
|
srv === null || srv === void 0 ? void 0 : srv.on('error', onError).listen({ port, host }, () => {
|
|
227
228
|
const ad = srv.address();
|
|
@@ -246,7 +247,8 @@ function startServer(srv, { port, host }) {
|
|
|
246
247
|
srv.busy = (0, find_process_1.default)('port', port).then(res => res === null || res === void 0 ? void 0 : res.map(x => { var _a; return (0, misc_1.prefix)("Service", x.name === 'svchost.exe' && ((_a = x.cmd.split(x.name)[1]) === null || _a === void 0 ? void 0 : _a.trim())) || x.name; }).join(' + '), () => '');
|
|
247
248
|
srv.error = `port ${port} busy: ${await srv.busy || "unknown process"}`;
|
|
248
249
|
}
|
|
249
|
-
|
|
250
|
+
if (!silence)
|
|
251
|
+
console.error(srv.name, srv.error);
|
|
250
252
|
resolve(0);
|
|
251
253
|
}
|
|
252
254
|
});
|
package/src/perm.js
CHANGED
|
@@ -4,13 +4,14 @@ 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.changeSrpHelper = exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.createAdmin = exports.saveSrpInfo = exports.getAccount = exports.expandUsername = void 0;
|
|
7
|
+
exports.changeSrpHelper = exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.createAdmin = exports.saveSrpInfo = exports.getAccount = exports.ctxBelongsTo = exports.expandUsername = void 0;
|
|
8
8
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
9
|
const misc_1 = require("./misc");
|
|
10
10
|
const config_1 = require("./config");
|
|
11
11
|
const tssrp6a_1 = require("tssrp6a");
|
|
12
12
|
const events_1 = __importDefault(require("./events"));
|
|
13
13
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
14
|
+
const auth_1 = require("./auth");
|
|
14
15
|
let accounts = {};
|
|
15
16
|
// provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
|
|
16
17
|
function expandUsername(who) {
|
|
@@ -27,6 +28,12 @@ function expandUsername(who) {
|
|
|
27
28
|
return ret;
|
|
28
29
|
}
|
|
29
30
|
exports.expandUsername = expandUsername;
|
|
31
|
+
// check if current username or any ancestor match the provided usernames
|
|
32
|
+
function ctxBelongsTo(ctx, usernames) {
|
|
33
|
+
return (0, misc_1.getOrSet)(ctx.state, 'usernames', () => expandUsername((0, auth_1.getCurrentUsername)(ctx))) // cache ancestors' usernames inside context state
|
|
34
|
+
.some((u) => usernames.includes(u));
|
|
35
|
+
}
|
|
36
|
+
exports.ctxBelongsTo = ctxBelongsTo;
|
|
30
37
|
function getAccount(username, normalize = true) {
|
|
31
38
|
if (normalize)
|
|
32
39
|
username = normalizeUsername(username);
|
package/src/plugins.js
CHANGED
|
@@ -48,6 +48,10 @@ const first_1 = require("./first");
|
|
|
48
48
|
const frontEndApis_1 = require("./frontEndApis");
|
|
49
49
|
const index_1 = require("./index");
|
|
50
50
|
const block_1 = require("./block");
|
|
51
|
+
const lang_1 = require("./lang");
|
|
52
|
+
const i18n_1 = require("./i18n");
|
|
53
|
+
const perm_1 = require("./perm");
|
|
54
|
+
const auth_1 = require("./auth");
|
|
51
55
|
exports.PATH = 'plugins';
|
|
52
56
|
exports.DISABLING_SUFFIX = '-disabled';
|
|
53
57
|
exports.STORAGE_FOLDER = 'storage';
|
|
@@ -129,7 +133,7 @@ function getPluginConfigFields(id) {
|
|
|
129
133
|
exports.getPluginConfigFields = getPluginConfigFields;
|
|
130
134
|
async function initPlugin(pl, morePassedToInit) {
|
|
131
135
|
var _a;
|
|
132
|
-
|
|
136
|
+
const res = await ((_a = pl.init) === null || _a === void 0 ? void 0 : _a.call(pl, {
|
|
133
137
|
Const,
|
|
134
138
|
require,
|
|
135
139
|
getConnections: connections_1.getConnections,
|
|
@@ -140,8 +144,11 @@ async function initPlugin(pl, morePassedToInit) {
|
|
|
140
144
|
notifyClient: frontEndApis_1.notifyClient,
|
|
141
145
|
addBlock: block_1.addBlock,
|
|
142
146
|
misc,
|
|
147
|
+
ctxBelongsTo: perm_1.ctxBelongsTo,
|
|
148
|
+
getCurrentUsername: auth_1.getCurrentUsername,
|
|
143
149
|
...morePassedToInit
|
|
144
|
-
}))
|
|
150
|
+
}));
|
|
151
|
+
return Object.assign(pl, typeof res === 'function' ? { unload: res } : res);
|
|
145
152
|
}
|
|
146
153
|
const already = new Set();
|
|
147
154
|
function warnOnce(msg) {
|
|
@@ -502,6 +509,9 @@ function watchPlugin(id, path) {
|
|
|
502
509
|
}
|
|
503
510
|
});
|
|
504
511
|
},
|
|
512
|
+
async i18n(ctx) {
|
|
513
|
+
return (0, i18n_1.i18nFromTranslations)(await (0, lang_1.getLangData)(ctx));
|
|
514
|
+
},
|
|
505
515
|
});
|
|
506
516
|
const folder = (0, path_1.dirname)(module);
|
|
507
517
|
const { sections, unwatch } = (0, customHtml_1.watchLoadCustomHtml)(folder);
|
package/src/serveFile.js
CHANGED
|
@@ -41,7 +41,7 @@ async function serveFileNode(ctx, node) {
|
|
|
41
41
|
: lodash_1.default.find(mime, (val, mask) => (0, misc_1.matches)(name, mask));
|
|
42
42
|
if (allowedReferer.get()) {
|
|
43
43
|
const ref = (_a = /\/\/([^:/]+)/.exec(ctx.get('referer'))) === null || _a === void 0 ? void 0 : _a[1]; // extract host from url
|
|
44
|
-
if (ref && ref !==
|
|
44
|
+
if (ref && ref !== (0, misc_1.normalizeHost)(ctx.host) // automatically accept if referer is basically the hosting domain
|
|
45
45
|
&& !(0, misc_1.matches)(ref, allowedReferer.get()))
|
|
46
46
|
return ctx.status = const_1.HTTP_FORBIDDEN;
|
|
47
47
|
}
|
|
@@ -54,10 +54,6 @@ async function serveFileNode(ctx, node) {
|
|
|
54
54
|
await serveFile(ctx, source || '', mimeString);
|
|
55
55
|
if (await maxDownloadsPerAccount(ctx) === undefined) // returning false will not execute other limits
|
|
56
56
|
await maxDownloads(ctx) || await maxDownloadsPerIp(ctx);
|
|
57
|
-
function host() {
|
|
58
|
-
const s = ctx.host;
|
|
59
|
-
return s[0] === '[' ? s.slice(1, s.indexOf(']')) : s === null || s === void 0 ? void 0 : s.split(':')[0];
|
|
60
|
-
}
|
|
61
57
|
}
|
|
62
58
|
exports.serveFileNode = serveFileNode;
|
|
63
59
|
const mimeCfg = (0, config_1.defineConfig)('mime', {}, obj => {
|
package/src/serveGuiFiles.js
CHANGED
|
@@ -19,6 +19,7 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
|
19
19
|
const config_1 = require("./config");
|
|
20
20
|
const lang_1 = require("./lang");
|
|
21
21
|
const upload_1 = require("./upload");
|
|
22
|
+
const size1024 = (0, config_1.defineConfig)(misc_1.CFG.size_1024, false, x => misc_1.formatBytes.k = x ? 1024 : 1000); // we both configure formatBytes, and also provide a compiled version (number instead of boolean)
|
|
22
23
|
const splitUploads = (0, config_1.defineConfig)(misc_1.CFG.split_uploads, 0);
|
|
23
24
|
exports.logGui = (0, config_1.defineConfig)(misc_1.CFG.log_gui, false);
|
|
24
25
|
lodash_1.default.each(misc_1.FRONTEND_OPTIONS, (v, k) => (0, config_1.defineConfig)(k, v)); // define default values
|
|
@@ -102,6 +103,7 @@ async function treatIndex(ctx, filesUri, body) {
|
|
|
102
103
|
prefixUrl: ctx.state.revProxyPath,
|
|
103
104
|
dontOverwriteUploading: upload_1.dontOverwriteUploading.get(),
|
|
104
105
|
splitUploads: splitUploads.get(),
|
|
106
|
+
kb: size1024.compiled(),
|
|
105
107
|
forceTheme: (0, plugins_1.mapPlugins)(p => lodash_1.default.isString(p.isTheme) ? p.isTheme : undefined).find(Boolean),
|
|
106
108
|
customHtml: lodash_1.default.omit((0, customHtml_1.getAllSections)(), ['top', 'bottom', 'htmlHead', 'style']),
|
|
107
109
|
...(0, misc_1.newObj)(misc_1.FRONTEND_OPTIONS, (v, k) => (0, config_1.getConfig)(k)),
|
package/src/update.js
CHANGED
|
@@ -127,7 +127,10 @@ async function update(tagOrUrl = '') {
|
|
|
127
127
|
const bin = process.execPath;
|
|
128
128
|
const binPath = (0, path_1.dirname)(bin);
|
|
129
129
|
const binFile = 'hfs' + (const_1.IS_WINDOWS ? '.exe' : ''); // currently running bin could have been renamed
|
|
130
|
-
|
|
130
|
+
let newBinFile = binFile;
|
|
131
|
+
do {
|
|
132
|
+
newBinFile = 'new-' + newBinFile;
|
|
133
|
+
} while ((0, fs_1.existsSync)((0, path_1.join)(binPath, newBinFile)));
|
|
131
134
|
plugins_1.pluginsWatcher.pause();
|
|
132
135
|
try {
|
|
133
136
|
await (0, misc_1.unzip)(updateSource, path => (0, path_1.join)(binPath, path === binFile ? newBinFile : path));
|
package/src/upload.js
CHANGED
|
@@ -19,6 +19,7 @@ const comments_1 = require("./comments");
|
|
|
19
19
|
const lodash_1 = __importDefault(require("lodash"));
|
|
20
20
|
const events_1 = __importDefault(require("./events"));
|
|
21
21
|
const promises_1 = require("fs/promises");
|
|
22
|
+
const expiringCache_1 = require("./expiringCache");
|
|
22
23
|
exports.deleteUnfinishedUploadsAfter = (0, config_1.defineConfig)('delete_unfinished_uploads_after', 86400);
|
|
23
24
|
exports.minAvailableMb = (0, config_1.defineConfig)('min_available_mb', 100);
|
|
24
25
|
exports.dontOverwriteUploading = (0, config_1.defineConfig)('dont_overwrite_uploading', true);
|
|
@@ -35,7 +36,7 @@ function setUploadMeta(path, ctx) {
|
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
// stay sync because we use this function with formidable()
|
|
38
|
-
const diskSpaceCache =
|
|
39
|
+
const diskSpaceCache = (0, expiringCache_1.expiringCache)(3000); // invalidate shortly
|
|
39
40
|
const openFiles = new Set();
|
|
40
41
|
function uploadWriter(base, baseUri, path, ctx) {
|
|
41
42
|
let fullPath = '';
|
|
@@ -65,13 +66,10 @@ function uploadWriter(base, baseUri, path, ctx) {
|
|
|
65
66
|
while ((closestVfsNode === null || closestVfsNode === void 0 ? void 0 : closestVfsNode.parent) && !closestVfsNode.original)
|
|
66
67
|
closestVfsNode = closestVfsNode.parent; // if it's not original, it surely has a parent
|
|
67
68
|
const statDir = closestVfsNode.source;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
setTimeout(() => delete diskSpaceCache[statDir], 3000); // invalidate shortly
|
|
73
|
-
}
|
|
74
|
-
const { free } = diskSpaceCache[statDir];
|
|
69
|
+
const res = diskSpaceCache.try(statDir, () => (0, util_os_1.getDiskSpaceSync)(statDir));
|
|
70
|
+
if (!res)
|
|
71
|
+
throw 'miss';
|
|
72
|
+
const { free } = res;
|
|
75
73
|
if (typeof free !== 'number' || isNaN(free))
|
|
76
74
|
throw '';
|
|
77
75
|
if (reqSize > free - (min || 0))
|
package/src/util-os.js
CHANGED
|
@@ -76,17 +76,18 @@ async function runCmd(cmd, args = [], options = {}) {
|
|
|
76
76
|
return (stderr || stdout).replace(/\r/g, '');
|
|
77
77
|
}
|
|
78
78
|
exports.runCmd = runCmd;
|
|
79
|
+
// returns pid-to-name object
|
|
79
80
|
async function getWindowsServicePids() {
|
|
80
81
|
var _a;
|
|
81
82
|
const res = await runCmd('tasklist /svc /fo csv');
|
|
82
83
|
const parsed = new csv_1.default().parse(res);
|
|
83
84
|
const no = (_a = parsed === null || parsed === void 0 ? void 0 : parsed[1]) === null || _a === void 0 ? void 0 : _a[2];
|
|
84
|
-
return parsed.slice(2).filter(x => x[2] !== no).map(x =>
|
|
85
|
+
return Object.fromEntries(parsed.slice(2).filter(x => x[2] !== no).map(x => [x[1], x[2]]));
|
|
85
86
|
}
|
|
86
87
|
exports.RUNNING_AS_SERVICE = const_1.IS_WINDOWS && getWindowsServicePids().then(x => {
|
|
87
|
-
const ret = x.
|
|
88
|
+
const ret = x[node_process_1.pid] || x[node_process_1.ppid];
|
|
88
89
|
if (ret)
|
|
89
|
-
console.log("running as service");
|
|
90
|
+
console.log("running as service", ret);
|
|
90
91
|
return ret;
|
|
91
92
|
}, e => {
|
|
92
93
|
console.log("couldn't determine if we are running as a service");
|
package/src/vfs.js
CHANGED
|
@@ -224,13 +224,8 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
|
224
224
|
}
|
|
225
225
|
cur = who;
|
|
226
226
|
} while (1);
|
|
227
|
-
if (Array.isArray(who))
|
|
228
|
-
|
|
229
|
-
// check if I or any ancestor match `who`, but cache ancestors' usernames inside context state
|
|
230
|
-
const some = (0, misc_1.getOrSet)(ctx.state, 'usernames', () => (0, perm_1.expandUsername)((0, auth_1.getCurrentUsername)(ctx)))
|
|
231
|
-
.some((u) => arr.includes(u));
|
|
232
|
-
return some ? 0 : const_1.HTTP_UNAUTHORIZED;
|
|
233
|
-
}
|
|
227
|
+
if (Array.isArray(who))
|
|
228
|
+
return (0, perm_1.ctxBelongsTo)(ctx, who) ? 0 : const_1.HTTP_UNAUTHORIZED;
|
|
234
229
|
return typeof who === 'boolean' ? (who ? 0 : const_1.HTTP_FORBIDDEN)
|
|
235
230
|
: who === misc_1.WHO_ANY_ACCOUNT ? ((0, auth_1.getCurrentUsername)(ctx) ? 0 : const_1.HTTP_UNAUTHORIZED)
|
|
236
231
|
: (0, misc_1.throw_)(Error(`invalid permission: ${perm}=${(0, misc_1.try_)(() => JSON.stringify(who))}`));
|