hfs 0.53.0 → 0.54.0-alpha3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/assets/index-CcUio6cm.css +1 -0
- package/admin/assets/index-DCsMhd8s.js +807 -0
- package/admin/assets/{sha512-rpja4qx0.js → sha512-VvQJXb6I.js} +1 -1
- package/admin/index.html +4 -2
- package/frontend/assets/index-legacy-vQymU_EM.js +60 -0
- package/frontend/assets/{sha512-legacy-pz5im6D1.js → sha512-legacy-BD2cMJIT.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +8 -6
- package/plugins/antibrute/plugin.js +1 -1
- package/plugins/download-counter/plugin.js +0 -15
- package/plugins/list-uploader/public/main.js +3 -2
- package/src/acme.js +2 -2
- package/src/adminApis.js +23 -3
- package/src/api.accounts.js +1 -1
- package/src/api.auth.js +6 -4
- package/src/api.get_file_list.js +18 -9
- package/src/api.monitor.js +1 -1
- package/src/api.net.js +4 -1
- package/src/api.plugins.js +2 -0
- package/src/api.vfs.js +6 -4
- package/src/apiMiddleware.js +4 -1
- package/src/auth.js +7 -7
- package/src/basicWeb.js +1 -1
- package/src/block.js +13 -1
- package/src/commands.js +8 -2
- package/src/const.js +13 -10
- package/src/cross-const.js +2 -1
- package/src/cross.js +25 -8
- package/src/debounceAsync.js +3 -5
- package/src/dirStream.js +138 -0
- package/src/events.js +18 -8
- package/src/frontEndApis.js +32 -15
- package/src/github.js +21 -3
- package/src/langs/embedded.js +4 -1
- package/src/langs/hfs-lang-fi.json +5 -3
- package/src/langs/hfs-lang-hu.json +27 -19
- package/src/langs/hfs-lang-it.json +2 -1
- package/src/langs/hfs-lang-th.json +166 -0
- package/src/langs/hfs-lang-tr.json +166 -0
- package/src/langs/hfs-lang-uk.json +170 -0
- package/src/langs/hfs-lang-vi.json +5 -5
- package/src/listen.js +12 -4
- package/src/log.js +1 -1
- package/src/makeQ.js +31 -0
- package/src/middlewares.js +11 -8
- package/src/misc.js +2 -1
- package/src/nat.js +4 -4
- package/src/perm.js +1 -1
- package/src/persistence.js +2 -1
- package/src/plugins.js +22 -3
- package/src/serveFile.js +16 -14
- package/src/serveGuiAndSharedFiles.js +9 -4
- package/src/serveGuiFiles.js +8 -6
- package/src/srp.js +2 -2
- package/src/update.js +48 -7
- package/src/upload.js +33 -18
- package/src/util-files.js +7 -29
- package/src/vfs.js +23 -11
- package/src/watchLoad.js +1 -1
- package/src/zip.js +4 -4
- package/README.md +0 -232
- package/admin/assets/index-Ly9xr1F6.js +0 -811
- package/admin/assets/index-gNE0rRfC.css +0 -1
- package/frontend/assets/index-legacy-5MEl9EM8.js +0 -60
- /package/frontend/assets/{polyfills-legacy-31b7nyOi.js → polyfills-legacy-DMrMt_pQ.js} +0 -0
package/src/cross.js
CHANGED
|
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.objRenameKey = exports.randomId = exports.getOrSet = 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.splitAt = exports.strinsert = exports.removeStarting = exports.enforceStarting = exports.enforceFinal = 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 = void 0;
|
|
21
|
-
exports.shortenAgent = 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.formatTimestamp = exports.repeat = exports.asyncGeneratorToArray = exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = 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.formatTimestamp = exports.repeat = exports.asyncGeneratorToArray = exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = 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"));
|
|
@@ -44,7 +44,7 @@ exports.SORT_BY_OPTIONS = ['name', 'extension', 'size', 'time'];
|
|
|
44
44
|
exports.THEME_OPTIONS = { auto: '', light: 'light', dark: 'dark' };
|
|
45
45
|
exports.CFG = constMap(['geo_enable', 'geo_allow', 'geo_list', 'geo_allow_unknown', 'dynamic_dns_url',
|
|
46
46
|
'log', 'error_log', 'log_rotation', 'dont_log_net', 'log_gui', 'log_api', 'log_ua', 'log_spam', 'track_ips',
|
|
47
|
-
'max_downloads', 'max_downloads_per_ip', 'max_downloads_per_account', 'roots', 'force_address']);
|
|
47
|
+
'max_downloads', 'max_downloads_per_ip', 'max_downloads_per_account', 'roots', 'force_address', 'split_uploads']);
|
|
48
48
|
exports.LIST = { add: '+', remove: '-', update: '=', props: 'props', ready: 'ready', error: 'e' };
|
|
49
49
|
exports.WHO_ANYONE = true;
|
|
50
50
|
exports.WHO_NO_ONE = false;
|
|
@@ -69,7 +69,7 @@ const MULTIPLIERS = ['', 'K', 'M', 'G', 'T'];
|
|
|
69
69
|
function formatBytes(n, { post = 'B', k = 1024, digits = NaN, sep = ' ' } = {}) {
|
|
70
70
|
if (isNaN(Number(n)) || n < 0)
|
|
71
71
|
return '';
|
|
72
|
-
const i = n && Math.floor(Math.log2(n) / Math.log2(k));
|
|
72
|
+
const i = n && Math.min(MULTIPLIERS.length - 1, Math.floor(Math.log2(n) / Math.log2(k)));
|
|
73
73
|
n /= k ** i;
|
|
74
74
|
const nAsString = i && !isNaN(digits) ? n.toFixed(digits)
|
|
75
75
|
: lodash_1.default.round(n, isNaN(digits) ? (n >= 100 ? 0 : 1) : digits);
|
|
@@ -347,11 +347,11 @@ function isTimestampString(v) {
|
|
|
347
347
|
return typeof v === 'string' && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?Z*$/.test(v);
|
|
348
348
|
}
|
|
349
349
|
exports.isTimestampString = isTimestampString;
|
|
350
|
-
function isEqualLax(a, b) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
350
|
+
function isEqualLax(a, b, overrideRule) {
|
|
351
|
+
var _a;
|
|
352
|
+
return (_a = overrideRule === null || overrideRule === void 0 ? void 0 : overrideRule(a, b)) !== null && _a !== void 0 ? _a : (a == b || a && b && typeof a === 'object' && typeof b === 'object'
|
|
353
|
+
&& Object.entries(a).every(([k, v]) => isEqualLax(v, b[k], overrideRule))
|
|
354
|
+
&& Object.entries(b).every(([k, v]) => k in a /*already checked*/ || isEqualLax(v, a[k], overrideRule)));
|
|
355
355
|
}
|
|
356
356
|
exports.isEqualLax = isEqualLax;
|
|
357
357
|
function xlate(input, table) {
|
|
@@ -381,6 +381,7 @@ async function promiseBestEffort(promises) {
|
|
|
381
381
|
return res.filter(x => x.status === 'fulfilled').map((x) => x.value);
|
|
382
382
|
}
|
|
383
383
|
exports.promiseBestEffort = promiseBestEffort;
|
|
384
|
+
// encode paths leaving / separator unencoded (not like encodeURIComponent), but still encode #
|
|
384
385
|
function pathEncode(s) {
|
|
385
386
|
return encodeURI(s).replace(/#/g, encodeURIComponent);
|
|
386
387
|
}
|
|
@@ -456,6 +457,14 @@ function safeDecodeURIComponent(s) {
|
|
|
456
457
|
}
|
|
457
458
|
}
|
|
458
459
|
exports.safeDecodeURIComponent = safeDecodeURIComponent;
|
|
460
|
+
function popKey(o, k) {
|
|
461
|
+
if (!o)
|
|
462
|
+
return;
|
|
463
|
+
const x = o[k];
|
|
464
|
+
delete o[k];
|
|
465
|
+
return x;
|
|
466
|
+
}
|
|
467
|
+
exports.popKey = popKey;
|
|
459
468
|
function shortenAgent(agent) {
|
|
460
469
|
var _a;
|
|
461
470
|
return lodash_1.default.findKey(BROWSERS, re => re.test(agent))
|
|
@@ -490,3 +499,11 @@ const BROWSERS = {
|
|
|
490
499
|
Xbox: /xbox/i,
|
|
491
500
|
UC: /UCBrowser/i,
|
|
492
501
|
};
|
|
502
|
+
setHidden(Object.prototype, {
|
|
503
|
+
__log(pause = false) {
|
|
504
|
+
const val = this instanceof Number || this instanceof String || this instanceof Boolean ? this.valueOf() : this;
|
|
505
|
+
if (pause)
|
|
506
|
+
debugger;
|
|
507
|
+
return _log(val);
|
|
508
|
+
}
|
|
509
|
+
});
|
package/src/debounceAsync.js
CHANGED
|
@@ -5,10 +5,8 @@ exports.singleWorkerFromBatchWorker = exports.debounceAsync = void 0;
|
|
|
5
5
|
// like lodash.debounce, but also avoids async invocations to overlap
|
|
6
6
|
function debounceAsync(
|
|
7
7
|
// the function you want to not call too often, too soon
|
|
8
|
-
callback,
|
|
9
|
-
|
|
10
|
-
wait = 100, options = {}) {
|
|
11
|
-
const { leading = false, maxWait = Infinity, cancelable = false, retain = 0, retainFailure } = options;
|
|
8
|
+
callback, options = {}) {
|
|
9
|
+
const { wait = 0, leading = false, maxWait = Infinity, cancelable = false, retain = 0, retainFailure } = options;
|
|
12
10
|
let started = 0; // latest callback invocation
|
|
13
11
|
let runningCallback; // latest callback invocation result
|
|
14
12
|
let latestDebouncer; // latest wrapper invocation
|
|
@@ -76,7 +74,7 @@ function singleWorkerFromBatchWorker(batchWorker, { maxWait = Infinity } = {}) {
|
|
|
76
74
|
const ret = batchWorker(batch);
|
|
77
75
|
batch = []; // this is reset as batchWorker starts, but without waiting
|
|
78
76
|
return ret;
|
|
79
|
-
}, 100,
|
|
77
|
+
}, { wait: 100, maxWait });
|
|
80
78
|
return (...args) => {
|
|
81
79
|
const idx = batch.push(args) - 1;
|
|
82
80
|
return debounced().then((x) => x[idx]);
|
package/src/dirStream.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DirentFromStats = exports.createDirStream = void 0;
|
|
7
|
+
const makeQ_1 = require("./makeQ");
|
|
8
|
+
const promises_1 = require("fs/promises");
|
|
9
|
+
const const_1 = require("./const");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const stream_1 = require("stream");
|
|
12
|
+
const cross_1 = require("./cross");
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const fswin_1 = __importDefault(require("fswin"));
|
|
15
|
+
const dirQ = (0, makeQ_1.makeQ)(3);
|
|
16
|
+
function createDirStream(startPath, { depth = 0, hidden = true }) {
|
|
17
|
+
let stopped = false;
|
|
18
|
+
let started = false;
|
|
19
|
+
const closingQ = [];
|
|
20
|
+
const stream = new stream_1.Readable({
|
|
21
|
+
objectMode: true,
|
|
22
|
+
read() {
|
|
23
|
+
if (started)
|
|
24
|
+
return;
|
|
25
|
+
started = true;
|
|
26
|
+
dirQ.add(() => readDir('', depth)
|
|
27
|
+
.then(res => {
|
|
28
|
+
Promise.resolve(res === null || res === void 0 ? void 0 : res.branchDone).then(() => {
|
|
29
|
+
stream.push(null);
|
|
30
|
+
});
|
|
31
|
+
}, e => {
|
|
32
|
+
stream.emit('error', e);
|
|
33
|
+
return stream.push(null);
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
stream.on('close', () => stopped = true);
|
|
38
|
+
return Object.assign(stream, {
|
|
39
|
+
stop() {
|
|
40
|
+
stopped = true;
|
|
41
|
+
if (!dirQ.isWorking())
|
|
42
|
+
stream.push(null);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
async function readDir(path, depth) {
|
|
46
|
+
if (stopped)
|
|
47
|
+
return;
|
|
48
|
+
const base = (0, path_1.join)(startPath, path);
|
|
49
|
+
const subDirsDone = [];
|
|
50
|
+
let n = 0;
|
|
51
|
+
let last;
|
|
52
|
+
if (const_1.IS_WINDOWS) { // use native apis to read 'hidden' attribute
|
|
53
|
+
const entries = await new Promise(res => fswin_1.default.find(base + '\\*', res));
|
|
54
|
+
const methods = {
|
|
55
|
+
isDir: false,
|
|
56
|
+
isFile() { return !this.isDir; },
|
|
57
|
+
isDirectory() { return this.isDir; },
|
|
58
|
+
isBlockDevice() { return false; },
|
|
59
|
+
isCharacterDevice() { return false; },
|
|
60
|
+
};
|
|
61
|
+
for (const f of entries) {
|
|
62
|
+
if (stopped)
|
|
63
|
+
break;
|
|
64
|
+
if (!hidden && f.IS_HIDDEN)
|
|
65
|
+
continue;
|
|
66
|
+
work(Object.assign(Object.create(methods), {
|
|
67
|
+
isDir: f.IS_DIRECTORY,
|
|
68
|
+
name: f.LONG_NAME,
|
|
69
|
+
stats: { size: f.SIZE, ctime: f.CREATION_TIME, mtime: f.LAST_WRITE_TIME }
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else
|
|
74
|
+
for await (let entry of await (0, promises_1.readdir)(base, { withFileTypes: true })) {
|
|
75
|
+
if (stopped)
|
|
76
|
+
break;
|
|
77
|
+
if (!hidden && entry.name[0] === '.')
|
|
78
|
+
continue;
|
|
79
|
+
const stats = entry.isSymbolicLink() && await (0, promises_1.stat)((0, path_1.join)(base, entry.name)).catch(() => null);
|
|
80
|
+
if (stats === null)
|
|
81
|
+
continue;
|
|
82
|
+
if (stats)
|
|
83
|
+
entry = new DirentFromStats(entry.name, stats);
|
|
84
|
+
const expanded = entry;
|
|
85
|
+
if (stats)
|
|
86
|
+
expanded.stats = stats;
|
|
87
|
+
work(expanded);
|
|
88
|
+
}
|
|
89
|
+
function work(entry) {
|
|
90
|
+
entry.path = (path && path + '/') + entry.name;
|
|
91
|
+
if (last && closingQ.length) // pending entries
|
|
92
|
+
last.closingBranch = Promise.resolve(closingQ.shift());
|
|
93
|
+
last = entry;
|
|
94
|
+
if (depth > 0 && entry.isDirectory()) {
|
|
95
|
+
const branchDone = (0, cross_1.pendingPromise)(); // per-job
|
|
96
|
+
const job = () => readDir(entry.path, depth - 1) // recur
|
|
97
|
+
.then(x => x, () => { }) // mute errors
|
|
98
|
+
.then(res => {
|
|
99
|
+
if (!(res === null || res === void 0 ? void 0 : res.n))
|
|
100
|
+
closingQ.push(entry.path); // no children to tell i'm done
|
|
101
|
+
Promise.resolve(res === null || res === void 0 ? void 0 : res.branchDone).then(() => branchDone.resolve());
|
|
102
|
+
});
|
|
103
|
+
dirQ.add(job); // this won't start until next tick
|
|
104
|
+
subDirsDone.push(branchDone);
|
|
105
|
+
}
|
|
106
|
+
stream.push(entry);
|
|
107
|
+
n++;
|
|
108
|
+
}
|
|
109
|
+
const branchDone = Promise.allSettled(subDirsDone).then(() => { });
|
|
110
|
+
if (last) // using streams, we don't know when the entries are received, so we need to notify on last item
|
|
111
|
+
last.closingBranch = branchDone.then(() => path);
|
|
112
|
+
else
|
|
113
|
+
closingQ.push(path); // ok, we'll ask next one to carry this info
|
|
114
|
+
// don't return the promise directly, as this job ends here, but communicate to caller the promise for the whole branch
|
|
115
|
+
return { branchDone, n };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.createDirStream = createDirStream;
|
|
119
|
+
const kStats = Symbol('stats');
|
|
120
|
+
// Adapting an internal class in Node.js to mimic the behavior of `Dirent` when creating it manually from `Stats`.
|
|
121
|
+
// https://github.com/nodejs/node/blob/a4cf6b204f0b160480153dc293ae748bf15225f9/lib/internal/fs/utils.js#L199C1-L213
|
|
122
|
+
class DirentFromStats extends node_fs_1.Dirent {
|
|
123
|
+
constructor(name, stats) {
|
|
124
|
+
// @ts-expect-error The constructor has parameters, but they are not represented in types.
|
|
125
|
+
// https://github.com/nodejs/node/blob/a4cf6b204f0b160480153dc293ae748bf15225f9/lib/internal/fs/utils.js#L164
|
|
126
|
+
super(name, null);
|
|
127
|
+
this[kStats] = stats;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.DirentFromStats = DirentFromStats;
|
|
131
|
+
for (const key of Reflect.ownKeys(node_fs_1.Dirent.prototype)) {
|
|
132
|
+
const name = key;
|
|
133
|
+
if (name === 'constructor')
|
|
134
|
+
continue;
|
|
135
|
+
DirentFromStats.prototype[name] = function () {
|
|
136
|
+
return this[kStats][name]();
|
|
137
|
+
};
|
|
138
|
+
}
|
package/src/events.js
CHANGED
|
@@ -6,7 +6,7 @@ const LISTENERS_SUFFIX = '\0listeners';
|
|
|
6
6
|
class BetterEventEmitter {
|
|
7
7
|
constructor() {
|
|
8
8
|
this.listeners = new Map();
|
|
9
|
-
this.
|
|
9
|
+
this.preventDefault = Symbol();
|
|
10
10
|
}
|
|
11
11
|
on(event, listener, { warnAfter = 10 } = {}) {
|
|
12
12
|
if (typeof event === 'string')
|
|
@@ -55,19 +55,29 @@ class BetterEventEmitter {
|
|
|
55
55
|
if (!(cbs === null || cbs === void 0 ? void 0 : cbs.size))
|
|
56
56
|
return;
|
|
57
57
|
const ret = [];
|
|
58
|
+
let prevented = false;
|
|
59
|
+
const extra = {
|
|
60
|
+
preventDefault() { prevented = true; }
|
|
61
|
+
};
|
|
58
62
|
for (const cb of cbs) {
|
|
59
|
-
const res = cb(...args);
|
|
60
|
-
if (res
|
|
63
|
+
const res = cb(...args, extra);
|
|
64
|
+
if (res === this.preventDefault)
|
|
65
|
+
extra.preventDefault();
|
|
66
|
+
else if (res !== undefined)
|
|
61
67
|
ret.push(res);
|
|
62
68
|
}
|
|
63
69
|
return Object.assign(ret, {
|
|
64
|
-
isDefaultPrevented: () =>
|
|
70
|
+
isDefaultPrevented: () => prevented,
|
|
65
71
|
});
|
|
66
72
|
}
|
|
67
|
-
emitAsync(event, ...args) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
async emitAsync(event, ...args) {
|
|
74
|
+
const syncRet = this.emit(event, ...args);
|
|
75
|
+
if (!syncRet)
|
|
76
|
+
return;
|
|
77
|
+
const asyncRet = await Promise.all(syncRet);
|
|
78
|
+
return Object.assign(asyncRet, {
|
|
79
|
+
isDefaultPrevented: () => syncRet.isDefaultPrevented()
|
|
80
|
+
|| asyncRet.some((r) => r === this.preventDefault)
|
|
71
81
|
});
|
|
72
82
|
}
|
|
73
83
|
}
|
package/src/frontEndApis.js
CHANGED
|
@@ -35,12 +35,15 @@ const events_1 = __importDefault(require("./events"));
|
|
|
35
35
|
const util_files_1 = require("./util-files");
|
|
36
36
|
const const_1 = require("./const");
|
|
37
37
|
const vfs_1 = require("./vfs");
|
|
38
|
+
const fs_1 = __importDefault(require("fs"));
|
|
38
39
|
const promises_1 = require("fs/promises");
|
|
39
40
|
const path_1 = require("path");
|
|
40
41
|
const upload_1 = require("./upload");
|
|
41
42
|
const misc_1 = require("./misc");
|
|
42
43
|
const comments_1 = require("./comments");
|
|
43
44
|
const SendList_1 = require("./SendList");
|
|
45
|
+
const adminApis_1 = require("./adminApis");
|
|
46
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
44
47
|
exports.frontEndApis = {
|
|
45
48
|
get_file_list: api_get_file_list_1.get_file_list,
|
|
46
49
|
...api_auth,
|
|
@@ -57,6 +60,7 @@ exports.frontEndApis = {
|
|
|
57
60
|
async get_file_details({ uris }, ctx) {
|
|
58
61
|
if (typeof (uris === null || uris === void 0 ? void 0 : uris[0]) !== 'string')
|
|
59
62
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad uris');
|
|
63
|
+
const isAdmin = (0, adminApis_1.ctxAdminAccess)(ctx);
|
|
60
64
|
return {
|
|
61
65
|
details: await Promise.all(uris.map(async (uri) => {
|
|
62
66
|
if (typeof uri !== 'string')
|
|
@@ -64,8 +68,12 @@ exports.frontEndApis = {
|
|
|
64
68
|
const node = await (0, vfs_1.urlToNode)(uri, ctx);
|
|
65
69
|
if (!node)
|
|
66
70
|
return false;
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
let upload = node.source && await (0, upload_1.getUploadMeta)(node.source).catch(() => undefined);
|
|
72
|
+
if (!upload)
|
|
73
|
+
return;
|
|
74
|
+
if (!isAdmin)
|
|
75
|
+
upload = lodash_1.default.omit(upload, 'ip');
|
|
76
|
+
return { upload };
|
|
69
77
|
}))
|
|
70
78
|
};
|
|
71
79
|
},
|
|
@@ -131,32 +139,41 @@ exports.frontEndApis = {
|
|
|
131
139
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e);
|
|
132
140
|
}
|
|
133
141
|
},
|
|
134
|
-
async move_files({ uri_from, uri_to }, ctx) {
|
|
142
|
+
async move_files({ uri_from, uri_to }, ctx, override) {
|
|
135
143
|
(0, misc_1.apiAssertTypes)({ array: { uri_from }, string: { uri_to } });
|
|
136
144
|
ctx.logExtra(null, { target: uri_from.map(decodeURI), destination: decodeURI(uri_to) });
|
|
137
145
|
const destNode = await (0, vfs_1.urlToNode)(uri_to, ctx);
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
140
|
-
return new apiMiddleware_1.ApiError(
|
|
146
|
+
const err = !destNode ? const_1.HTTP_NOT_FOUND : (0, vfs_1.statusCodeForMissingPerm)(destNode, 'can_upload', ctx);
|
|
147
|
+
if (err)
|
|
148
|
+
return new apiMiddleware_1.ApiError(err);
|
|
141
149
|
return {
|
|
142
|
-
errors: await Promise.all(uri_from.map(async (
|
|
143
|
-
if (typeof
|
|
150
|
+
errors: await Promise.all(uri_from.map(async (from1) => {
|
|
151
|
+
if (typeof from1 !== 'string')
|
|
144
152
|
return const_1.HTTP_BAD_REQUEST;
|
|
145
|
-
const srcNode = await (0, vfs_1.urlToNode)(
|
|
146
|
-
|
|
153
|
+
const srcNode = await (0, vfs_1.urlToNode)(from1, ctx);
|
|
154
|
+
const src = srcNode === null || srcNode === void 0 ? void 0 : srcNode.source;
|
|
155
|
+
if (!src)
|
|
147
156
|
return const_1.HTTP_NOT_FOUND;
|
|
148
|
-
const
|
|
149
|
-
|
|
157
|
+
const dest = (0, path_1.join)(destNode.source, (0, path_1.basename)(src));
|
|
158
|
+
if (lodash_1.default.isFunction(override))
|
|
159
|
+
return override === null || override === void 0 ? void 0 : override(srcNode, dest);
|
|
150
160
|
return (0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_delete', ctx)
|
|
151
|
-
|| (0, promises_1.rename)(
|
|
161
|
+
|| (0, promises_1.rename)(src, dest).catch(async (e) => {
|
|
152
162
|
if (e.code !== 'EXDEV')
|
|
153
163
|
throw e; // exdev = different drive
|
|
154
|
-
await (0, promises_1.copyFile)(
|
|
155
|
-
await (0, promises_1.unlink)(
|
|
164
|
+
await (0, promises_1.copyFile)(src, dest);
|
|
165
|
+
await (0, promises_1.unlink)(src);
|
|
156
166
|
}).catch(e => e.code || String(e));
|
|
157
167
|
}))
|
|
158
168
|
};
|
|
159
169
|
},
|
|
170
|
+
async copy_files(params, ctx) {
|
|
171
|
+
return exports.frontEndApis.move_files(params, ctx, // same parameters
|
|
172
|
+
(srcNode, dest) => // but override behavior
|
|
173
|
+
(0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_read', ctx)
|
|
174
|
+
|| (0, promises_1.copyFile)(srcNode.source, dest, fs_1.default.constants.COPYFILE_EXCL | fs_1.default.constants.COPYFILE_FICLONE)
|
|
175
|
+
.catch(e => e.code || String(e)));
|
|
176
|
+
},
|
|
160
177
|
async comment({ uri, comment }, ctx) {
|
|
161
178
|
(0, misc_1.apiAssertTypes)({ string: { uri, comment } });
|
|
162
179
|
ctx.logExtra(null, { target: decodeURI(uri) });
|
package/src/github.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.getProjectInfo = exports.searchPlugins = exports.getFolder2repo = exports.readOnlineCompatiblePlugin = exports.readOnlinePlugin = exports.readGithubFile = exports.getRepoInfo = exports.downloadPlugin = void 0;
|
|
7
|
+
exports.getProjectInfo = exports.alerts = exports.searchPlugins = exports.getFolder2repo = exports.readOnlineCompatiblePlugin = exports.readOnlinePlugin = exports.readGithubFile = exports.getRepoInfo = exports.downloadPlugin = void 0;
|
|
8
8
|
const events_1 = __importDefault(require("./events"));
|
|
9
9
|
const misc_1 = require("./misc");
|
|
10
10
|
const plugins_1 = require("./plugins");
|
|
@@ -14,6 +14,7 @@ const const_1 = require("./const");
|
|
|
14
14
|
const promises_1 = require("fs/promises");
|
|
15
15
|
const path_1 = require("path");
|
|
16
16
|
const fs_1 = require("fs");
|
|
17
|
+
const persistence_1 = require("./persistence");
|
|
17
18
|
const DIST_ROOT = 'dist';
|
|
18
19
|
const downloading = {};
|
|
19
20
|
function downloadProgress(id, status) {
|
|
@@ -218,10 +219,27 @@ async function searchPlugins(text = '', { skipRepos = [''] } = {}) {
|
|
|
218
219
|
}));
|
|
219
220
|
}
|
|
220
221
|
exports.searchPlugins = searchPlugins;
|
|
222
|
+
exports.alerts = persistence_1.storedMap.singleSync('alerts', []);
|
|
221
223
|
// centralized hosted information, to be used as little as possible
|
|
222
224
|
const FN = 'central.json';
|
|
223
225
|
let builtIn = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', FN), 'utf8'));
|
|
224
226
|
exports.getProjectInfo = (0, misc_1.debounceAsync)(() => readGithubFile(`${const_1.HFS_REPO}/${const_1.HFS_REPO_BRANCH}/${FN}`)
|
|
225
227
|
.then(JSON.parse, () => null)
|
|
226
|
-
.then(
|
|
227
|
-
|
|
228
|
+
.then(o => {
|
|
229
|
+
o = Object.assign({ ...builtIn }, const_1.DEV ? null : o); // fall back to built-in
|
|
230
|
+
// merge byVersions info in the main object, but collect alerts separately, to preserve multiple instances
|
|
231
|
+
const allAlerts = [o.alert];
|
|
232
|
+
for (const [ver, more] of Object.entries((0, misc_1.popKey)(o, 'byVersion') || {}))
|
|
233
|
+
if (const_1.VERSION.match(new RegExp(ver))) {
|
|
234
|
+
allAlerts.push(more.alert);
|
|
235
|
+
Object.assign(o, more);
|
|
236
|
+
}
|
|
237
|
+
lodash_1.default.remove(allAlerts, x => !x);
|
|
238
|
+
exports.alerts.set(was => {
|
|
239
|
+
if (!lodash_1.default.isEqual(was, allAlerts))
|
|
240
|
+
for (const a of allAlerts)
|
|
241
|
+
console.log("ALERT:", a);
|
|
242
|
+
return allAlerts;
|
|
243
|
+
});
|
|
244
|
+
return o;
|
|
245
|
+
}), { retain: misc_1.DAY, retainFailure: 60000 });
|
package/src/langs/embedded.js
CHANGED
|
@@ -20,4 +20,7 @@ const hfs_lang_de_json_1 = __importDefault(require("./hfs-lang-de.json"));
|
|
|
20
20
|
const hfs_lang_fi_json_1 = __importDefault(require("./hfs-lang-fi.json"));
|
|
21
21
|
const hfs_lang_hu_json_1 = __importDefault(require("./hfs-lang-hu.json"));
|
|
22
22
|
const hfs_lang_ja_json_1 = __importDefault(require("./hfs-lang-ja.json"));
|
|
23
|
-
|
|
23
|
+
const hfs_lang_tr_json_1 = __importDefault(require("./hfs-lang-tr.json"));
|
|
24
|
+
const hfs_lang_th_json_1 = __importDefault(require("./hfs-lang-th.json"));
|
|
25
|
+
const hfs_lang_uk_json_1 = __importDefault(require("./hfs-lang-uk.json"));
|
|
26
|
+
exports.default = { it: hfs_lang_it_json_1.default, zh: hfs_lang_zh_json_1.default, ru: hfs_lang_ru_json_1.default, sr: hfs_lang_sr_json_1.default, ko: hfs_lang_ko_json_1.default, ms: hfs_lang_ms_json_1.default, 'zh-tw': hfs_lang_zh_tw_json_1.default, fr: hfs_lang_fr_json_1.default, 'pt-br': hfs_lang_pt_br_json_1.default, vi: hfs_lang_vi_json_1.default, es: hfs_lang_es_json_1.default, nl: hfs_lang_nl_json_1.default, el: hfs_lang_el_json_1.default, de: hfs_lang_de_json_1.default, fi: hfs_lang_fi_json_1.default, hu: hfs_lang_hu_json_1.default, ja: hfs_lang_ja_json_1.default, tr: hfs_lang_tr_json_1.default, th: hfs_lang_th_json_1.default, uk: hfs_lang_uk_json_1.default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Pultsari & Win_7",
|
|
3
3
|
"version": 1.2,
|
|
4
|
-
"hfs_version": "0.
|
|
4
|
+
"hfs_version": "0.54.0a1",
|
|
5
5
|
"translate": {
|
|
6
6
|
"Select": "Valitse",
|
|
7
7
|
"n_files": "{n,plural,one{# tiedosto} other{# tiedostoa}}",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"clipboard_list": "Kohteet leikepöydällä:",
|
|
144
144
|
"Close": "Sulje",
|
|
145
145
|
"Folder": "Kansio",
|
|
146
|
-
"Web page": "
|
|
146
|
+
"Web page": "Verkkosivu",
|
|
147
147
|
"Link": "Linkki",
|
|
148
148
|
"Auto-play": "Auto-toisto",
|
|
149
149
|
"autoplay_seconds": "Sekunteja odottaa kuvissa",
|
|
@@ -163,6 +163,8 @@
|
|
|
163
163
|
"to_clipboard_source_tooltip": "Mene kansioon jossa leikepöydän kohteet sijaitsevat",
|
|
164
164
|
"more_items": "{n} lisää kohdetta",
|
|
165
165
|
"Show details": "Näytä yksityiskohdat",
|
|
166
|
-
"upload_conflict": "on jo olemassa"
|
|
166
|
+
"upload_conflict": "on jo olemassa",
|
|
167
|
+
"Logged in": "Kirjauduttu sisään",
|
|
168
|
+
"Logged out": "Kirjauduttu ulos"
|
|
167
169
|
}
|
|
168
170
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"author": "
|
|
3
|
-
"version": 0.
|
|
4
|
-
"hfs_version": "0.
|
|
2
|
+
"author": "killersolyom",
|
|
3
|
+
"version": 0.4,
|
|
4
|
+
"hfs_version": "0.53.0",
|
|
5
5
|
"translate": {
|
|
6
6
|
"Select": "Kiválasztás",
|
|
7
7
|
"n_files": "{n,többes szám,egy{# file} egyéb{# file}}",
|
|
8
8
|
"n_folders": "{n,többes szám,egy{# folder} egyéb{# folder}}",
|
|
9
9
|
"filter_count": "{n,többes szám, egy{# filtered} egyéb{# filtered}}",
|
|
10
10
|
"select_count": "{n,többes szám, egy{# selected} egyéb{# selected}}",
|
|
11
|
-
"filter_placeholder": "Kulcsszó
|
|
11
|
+
"filter_placeholder": "Kulcsszó fájlok és mappák szűréséhez",
|
|
12
12
|
"Select some files": "Válasszon ki néhány fájlt",
|
|
13
13
|
"zip_checkboxes": "Használja a jelölőnégyzeteket a fájlok kiválasztásához, majd újra használhatja a Zip-et",
|
|
14
14
|
"zip_tooltip_selected": "A kiválasztott elemek letöltése egyetlen zip-fájlként",
|
|
@@ -29,11 +29,12 @@
|
|
|
29
29
|
"Searching": "Keresés",
|
|
30
30
|
"Searched": "Keresett",
|
|
31
31
|
"Clear search": "Keresés törlése",
|
|
32
|
-
"Interrupted": "
|
|
33
|
-
"stopped_before": "
|
|
32
|
+
"Interrupted": "Megszakadt",
|
|
33
|
+
"stopped_before": "A keresés megszakadt mielőtt bármit is találat volna.",
|
|
34
34
|
"empty_list": "Üres mappa",
|
|
35
35
|
"filter_negy": "Nincs szűrő",
|
|
36
|
-
"
|
|
36
|
+
"filter_none": "Nincs találat ezzel a szűrő beállítással",
|
|
37
|
+
"Admin-panel": "Admin panel",
|
|
37
38
|
"Login": "Bejelentkezés",
|
|
38
39
|
"Username": "Felhasználónév",
|
|
39
40
|
"Password": "Jelszó",
|
|
@@ -42,18 +43,18 @@
|
|
|
42
43
|
"login_bad_cookies": "A cookie-k nem működnek - a bejelentkezés sikertelen",
|
|
43
44
|
"User panel": "Felhasználói panel",
|
|
44
45
|
"Change password": "Jelszó módosítása",
|
|
45
|
-
"enter_pass": "
|
|
46
|
-
"enter_pass2": "
|
|
47
|
-
"pass2_mismatch": "A megadott
|
|
48
|
-
"password_changed": "Jelszó
|
|
46
|
+
"enter_pass": "Írja be az új jelszavát",
|
|
47
|
+
"enter_pass2": "Írja be az új jelszavát még egyszer",
|
|
48
|
+
"pass2_mismatch": "A megadott jelszavak nem egyeznek meg! A művelet megszakadt.",
|
|
49
|
+
"password_changed": "Jelszó megváltoztatva",
|
|
49
50
|
"Logout": "Kijelentkezés",
|
|
50
51
|
"connection error": "csatlakozási hiba",
|
|
51
52
|
"Full timestamp:": "Teljes időbélyeg:",
|
|
52
53
|
"Search was interrupted": "A keresés megszakadt",
|
|
53
|
-
"Stop list": "
|
|
54
|
-
"download_starting": "A
|
|
54
|
+
"Stop list": "Betöltés megszakítása",
|
|
55
|
+
"download_starting": "A letöltés hamarosan elkezdődik",
|
|
55
56
|
"wrong_account": "A(z) {u} fióknak nincs hozzáférése, próbálkozzon egy másikkal",
|
|
56
|
-
"no_upload_here": "Nincs feltöltési
|
|
57
|
+
"no_upload_here": "Nincs feltöltési engedélye az aktuális mappához",
|
|
57
58
|
"Create folder": "Mappa létrehozása",
|
|
58
59
|
"Pick files": "Fájlok kiválasztása",
|
|
59
60
|
"Pick folder": "Mappa kiválasztása",
|
|
@@ -118,11 +119,11 @@
|
|
|
118
119
|
"Switch zoom mode": "Nagyítás módra váltás",
|
|
119
120
|
"Full screen": "Teljes képernyő",
|
|
120
121
|
"File Show help": "File Nézet segítség",
|
|
121
|
-
"showHelpMain": "
|
|
122
|
+
"showHelpMain": "A következő műveletekhez használhatod a billentyűzetet:",
|
|
122
123
|
"showHelp_space": "szóköz",
|
|
123
|
-
"showHelp_←/→_body": "
|
|
124
|
-
"showHelp_↑/↓_body": "
|
|
125
|
-
"showHelp_space_body": "
|
|
124
|
+
"showHelp_←/→_body": "Ugrás az előző/következő fájlra",
|
|
125
|
+
"showHelp_↑/↓_body": "Hosszú képek görgetése",
|
|
126
|
+
"showHelp_space_body": "Kiválasztás",
|
|
126
127
|
"showHelp_D_body": "letöltés",
|
|
127
128
|
"showHelp_Z_body": "nagyitó módra váltás",
|
|
128
129
|
"showHelp_F_body": "teljes képernyő",
|
|
@@ -157,6 +158,13 @@
|
|
|
157
158
|
"Overwrite policy": "Felülírási irányelv",
|
|
158
159
|
"Rename to avoid overwriting": "Átnevez, hogy elkerülje a felülírást",
|
|
159
160
|
"Overwrite existing files": "Létező fájlok felülírása",
|
|
160
|
-
"Menu": "Menü"
|
|
161
|
+
"Menu": "Menü",
|
|
162
|
+
"clipboard": "Vágólap ({content})",
|
|
163
|
+
"to_clipboard_source_tooltip": "Ugrorás a mappába ahova a vágólap tartalma mutat",
|
|
164
|
+
"more_items": "{n} több elem(ek)",
|
|
165
|
+
"Show details": "Részletek mutatása",
|
|
166
|
+
"upload_conflict": "Már létezik",
|
|
167
|
+
"Logged in": "Bejelentkezett",
|
|
168
|
+
"Logged out": "Kijelentkezett"
|
|
161
169
|
}
|
|
162
170
|
}
|