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.
Files changed (65) hide show
  1. package/admin/assets/index-CcUio6cm.css +1 -0
  2. package/admin/assets/index-DCsMhd8s.js +807 -0
  3. package/admin/assets/{sha512-rpja4qx0.js → sha512-VvQJXb6I.js} +1 -1
  4. package/admin/index.html +4 -2
  5. package/frontend/assets/index-legacy-vQymU_EM.js +60 -0
  6. package/frontend/assets/{sha512-legacy-pz5im6D1.js → sha512-legacy-BD2cMJIT.js} +1 -1
  7. package/frontend/index.html +2 -2
  8. package/package.json +8 -6
  9. package/plugins/antibrute/plugin.js +1 -1
  10. package/plugins/download-counter/plugin.js +0 -15
  11. package/plugins/list-uploader/public/main.js +3 -2
  12. package/src/acme.js +2 -2
  13. package/src/adminApis.js +23 -3
  14. package/src/api.accounts.js +1 -1
  15. package/src/api.auth.js +6 -4
  16. package/src/api.get_file_list.js +18 -9
  17. package/src/api.monitor.js +1 -1
  18. package/src/api.net.js +4 -1
  19. package/src/api.plugins.js +2 -0
  20. package/src/api.vfs.js +6 -4
  21. package/src/apiMiddleware.js +4 -1
  22. package/src/auth.js +7 -7
  23. package/src/basicWeb.js +1 -1
  24. package/src/block.js +13 -1
  25. package/src/commands.js +8 -2
  26. package/src/const.js +13 -10
  27. package/src/cross-const.js +2 -1
  28. package/src/cross.js +25 -8
  29. package/src/debounceAsync.js +3 -5
  30. package/src/dirStream.js +138 -0
  31. package/src/events.js +18 -8
  32. package/src/frontEndApis.js +32 -15
  33. package/src/github.js +21 -3
  34. package/src/langs/embedded.js +4 -1
  35. package/src/langs/hfs-lang-fi.json +5 -3
  36. package/src/langs/hfs-lang-hu.json +27 -19
  37. package/src/langs/hfs-lang-it.json +2 -1
  38. package/src/langs/hfs-lang-th.json +166 -0
  39. package/src/langs/hfs-lang-tr.json +166 -0
  40. package/src/langs/hfs-lang-uk.json +170 -0
  41. package/src/langs/hfs-lang-vi.json +5 -5
  42. package/src/listen.js +12 -4
  43. package/src/log.js +1 -1
  44. package/src/makeQ.js +31 -0
  45. package/src/middlewares.js +11 -8
  46. package/src/misc.js +2 -1
  47. package/src/nat.js +4 -4
  48. package/src/perm.js +1 -1
  49. package/src/persistence.js +2 -1
  50. package/src/plugins.js +22 -3
  51. package/src/serveFile.js +16 -14
  52. package/src/serveGuiAndSharedFiles.js +9 -4
  53. package/src/serveGuiFiles.js +8 -6
  54. package/src/srp.js +2 -2
  55. package/src/update.js +48 -7
  56. package/src/upload.js +33 -18
  57. package/src/util-files.js +7 -29
  58. package/src/vfs.js +23 -11
  59. package/src/watchLoad.js +1 -1
  60. package/src/zip.js +4 -4
  61. package/README.md +0 -232
  62. package/admin/assets/index-Ly9xr1F6.js +0 -811
  63. package/admin/assets/index-gNE0rRfC.css +0 -1
  64. package/frontend/assets/index-legacy-5MEl9EM8.js +0 -60
  65. /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
- return a == b //eslint-disable-line
352
- || (a && b && typeof a === 'object' && typeof b === 'object'
353
- && Object.entries(a).every(([k, v]) => isEqualLax(v, b[k]))
354
- && Object.entries(b).every(([k, v]) => Object.hasOwn(a, k) || isEqualLax(v, a[k])));
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
+ });
@@ -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
- // time to wait after invocation of the debounced function. If you call again while waiting, the timer starts again.
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, { maxWait });
77
+ }, { wait: 100, maxWait });
80
78
  return (...args) => {
81
79
  const idx = batch.push(args) - 1;
82
80
  return debounced().then((x) => x[idx]);
@@ -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.stop = Symbol();
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 !== undefined)
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: () => ret.some(r => r === this.stop),
70
+ isDefaultPrevented: () => prevented,
65
71
  });
66
72
  }
67
- emitAsync(event, ...args) {
68
- const ret = Promise.all(this.emit(event, ...args) || []);
69
- return Object.assign(ret, {
70
- isDefaultPrevented: async () => (await ret).some((r) => r === this.stop)
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
  }
@@ -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
- const upload = node.source && await (0, upload_1.getUploadMeta)(node.source).catch(() => undefined);
68
- return upload && { upload };
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 code = !destNode ? const_1.HTTP_NOT_FOUND : (0, vfs_1.statusCodeForMissingPerm)(destNode, 'can_upload', ctx);
139
- if (code)
140
- return new apiMiddleware_1.ApiError(code);
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 (src) => {
143
- if (typeof src !== 'string')
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)(src, ctx);
146
- if (!srcNode)
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 s = srcNode.source;
149
- const d = (0, path_1.join)(destNode.source, (0, path_1.basename)(srcNode.source));
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)(s, d).catch(async (e) => {
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)(s, d);
155
- await (0, promises_1.unlink)(s);
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(x => Object.assign({ ...builtIn }, const_1.DEV ? null : x)), // fall back to built-in
227
- 0, { retain: misc_1.DAY, retainFailure: 60000 });
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 });
@@ -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
- 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 };
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.53.0b8",
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": "Verkko sivu",
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": "unknown",
3
- "version": 0.3,
4
- "hfs_version": "0.52.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ó a fájlok és mappák szűréséhez",
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": "Megszakított",
33
- "stopped_before": "megállt_előtte",
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
- "Admin-panel": "Adminisztrációs panel",
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": "Új jelszó megadása",
46
- "enter_pass2": "Adja meg újra ugyanazt az új jelszót",
47
- "pass2_mismatch": "A megadott második jelszó nem egyezett meg az elsővel. A művelet megszakadt.",
48
- "password_changed": "Jelszó megváltoztatása",
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": "Stop lista",
54
- "download_starting": "A letöltésnek hamarosan elkezdődik",
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 engedély az aktuális mappához",
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": "Néhány művelethez használhatod a billentyűzetet:",
122
+ "showHelpMain": "A következő műveletekhez használhatod a billentyűzetet:",
122
123
  "showHelp_space": "szóköz",
123
- "showHelp_←/→_body": "ugrás az előző/következő fájlra",
124
- "showHelp_↑/↓_body": "hosszú képek görgetése",
125
- "showHelp_space_body": "kiválasztás",
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
  }
@@ -153,6 +153,7 @@
153
153
  "Show details": "Mostra dettagli",
154
154
  "upload_conflict": "esiste già",
155
155
  "Logged in": "Benvenuto",
156
- "Logged out": "Arrivederci"
156
+ "Logged out": "Arrivederci",
157
+ "Cancel": "Annulla"
157
158
  }
158
159
  }