hfs 3.3.2 → 3.4.0-alpha1

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 (145) hide show
  1. package/admin/assets/AccountsPage-C6J2hRcY.js +4 -0
  2. package/admin/assets/ConfigFilePage-D5TZZyMR.js +4 -0
  3. package/admin/assets/CustomHtmlPage-DV2-ufWZ.js +2 -0
  4. package/admin/assets/InternetPage-DT3wSQYQ.js +6 -0
  5. package/admin/assets/LangPage-DbXCv7wR.js +2 -0
  6. package/admin/assets/LogsPage-DmAxUZpu.js +3 -0
  7. package/admin/assets/MonitorPage-D7ydOeq_.js +2 -0
  8. package/admin/assets/OptionsPage-DpN3ajRm.js +3 -0
  9. package/admin/assets/PluginsPage-BYzfwuvg.js +1 -0
  10. package/admin/assets/icons-CfBG9vvB.js +1 -0
  11. package/admin/assets/index-CVCnYMoB.js +755 -0
  12. package/admin/assets/index-mI9kZlpb.css +1 -0
  13. package/admin/assets/routing-C4l8rnk_.js +1 -0
  14. package/admin/index.html +2 -2
  15. package/central.json +100 -1
  16. package/frontend/assets/{dist-legacy-zQdkj0Z9.js → dist-legacy-CKczq_fx.js} +1 -1
  17. package/frontend/assets/index-legacy-ZYae30AY.js +1 -0
  18. package/frontend/assets/{lib-legacy-CRrFEEqm.js → lib-legacy-D2tyILlj.js} +1 -1
  19. package/frontend/assets/{sha512-legacy-BB7Mdt0y.js → sha512-legacy-CFcDeiiP.js} +1 -1
  20. package/frontend/index.html +1 -1
  21. package/npm-shrinkwrap.json +60 -60
  22. package/package.json +3 -3
  23. package/src/acme.js +143 -50
  24. package/src/acmeConfig.js +43 -0
  25. package/src/acmeDns.js +285 -0
  26. package/src/admin-langs/embedded.js +33 -0
  27. package/src/admin-langs/hfs-admin-lang-ar.json +1141 -0
  28. package/src/admin-langs/hfs-admin-lang-bg.json +1141 -0
  29. package/src/admin-langs/hfs-admin-lang-de.json +1141 -0
  30. package/src/admin-langs/hfs-admin-lang-el.json +1141 -0
  31. package/src/admin-langs/hfs-admin-lang-en.json +1141 -0
  32. package/src/admin-langs/hfs-admin-lang-es.json +1141 -0
  33. package/src/admin-langs/hfs-admin-lang-fi.json +1141 -0
  34. package/src/admin-langs/hfs-admin-lang-fr.json +1141 -0
  35. package/src/admin-langs/hfs-admin-lang-hu.json +1141 -0
  36. package/src/admin-langs/hfs-admin-lang-it.json +1141 -0
  37. package/src/admin-langs/hfs-admin-lang-ja.json +1141 -0
  38. package/src/admin-langs/hfs-admin-lang-ko.json +1141 -0
  39. package/src/admin-langs/hfs-admin-lang-lt.json +1141 -0
  40. package/src/admin-langs/hfs-admin-lang-ms.json +1141 -0
  41. package/src/admin-langs/hfs-admin-lang-nl.json +1141 -0
  42. package/src/admin-langs/hfs-admin-lang-pl.json +1141 -0
  43. package/src/admin-langs/hfs-admin-lang-pt-br.json +1141 -0
  44. package/src/admin-langs/hfs-admin-lang-ro.json +1141 -0
  45. package/src/admin-langs/hfs-admin-lang-ru.json +1141 -0
  46. package/src/admin-langs/hfs-admin-lang-sr-latn.json +1141 -0
  47. package/src/admin-langs/hfs-admin-lang-sr.json +1141 -0
  48. package/src/admin-langs/hfs-admin-lang-th.json +1141 -0
  49. package/src/admin-langs/hfs-admin-lang-tr.json +1141 -0
  50. package/src/admin-langs/hfs-admin-lang-uk.json +1141 -0
  51. package/src/admin-langs/hfs-admin-lang-vi.json +1141 -0
  52. package/src/admin-langs/hfs-admin-lang-zh-tw.json +1141 -0
  53. package/src/admin-langs/hfs-admin-lang-zh.json +1141 -0
  54. package/src/adminApis.js +2 -2
  55. package/src/adminLang.js +50 -0
  56. package/src/api.lang.js +22 -13
  57. package/src/api.log.js +5 -3
  58. package/src/api.net.js +22 -4
  59. package/src/api.plugins.js +53 -19
  60. package/src/api.vfs.js +2 -1
  61. package/src/argv.js +4 -0
  62. package/src/auth.js +0 -2
  63. package/src/basicWeb.js +6 -2
  64. package/src/block.js +1 -1
  65. package/src/config.js +1 -0
  66. package/src/connections.js +4 -7
  67. package/src/const.js +1 -1
  68. package/src/cross-const.js +7 -5
  69. package/src/cross.js +16 -3
  70. package/src/frontEndApis.js +44 -7
  71. package/src/github.js +6 -37
  72. package/src/i18n.js +16 -4
  73. package/src/index.js +4 -2
  74. package/src/lang.js +15 -8
  75. package/src/langs/embedded.js +2 -1
  76. package/src/langs/hfs-lang-ar.json +5 -1
  77. package/src/langs/hfs-lang-bg.json +5 -1
  78. package/src/langs/hfs-lang-de.json +5 -1
  79. package/src/langs/hfs-lang-el.json +5 -1
  80. package/src/langs/hfs-lang-en.json +188 -0
  81. package/src/langs/hfs-lang-es.json +7 -3
  82. package/src/langs/hfs-lang-fi.json +6 -2
  83. package/src/langs/hfs-lang-fr.json +10 -6
  84. package/src/langs/hfs-lang-hu.json +5 -1
  85. package/src/langs/hfs-lang-it.json +9 -4
  86. package/src/langs/hfs-lang-ja.json +5 -1
  87. package/src/langs/hfs-lang-ko.json +5 -1
  88. package/src/langs/hfs-lang-lt.json +5 -1
  89. package/src/langs/hfs-lang-ms.json +7 -1
  90. package/src/langs/hfs-lang-nl.json +5 -1
  91. package/src/langs/hfs-lang-pl.json +5 -1
  92. package/src/langs/hfs-lang-pt-br.json +7 -3
  93. package/src/langs/hfs-lang-ro.json +5 -1
  94. package/src/langs/hfs-lang-ru.json +7 -3
  95. package/src/langs/hfs-lang-sr-latn.json +5 -1
  96. package/src/langs/hfs-lang-sr.json +5 -1
  97. package/src/langs/hfs-lang-th.json +6 -2
  98. package/src/langs/hfs-lang-tr.json +6 -2
  99. package/src/langs/hfs-lang-uk.json +14 -10
  100. package/src/langs/hfs-lang-vi.json +5 -1
  101. package/src/langs/hfs-lang-zh-tw.json +5 -1
  102. package/src/langs/hfs-lang-zh.json +5 -1
  103. package/src/log.js +7 -1
  104. package/src/middlewares.js +85 -14
  105. package/src/multipartUpload.js +21 -0
  106. package/src/nat.js +10 -7
  107. package/src/outboundProxy.js +1 -1
  108. package/src/perm.js +38 -20
  109. package/src/pluginCatalog.js +66 -0
  110. package/src/pluginUsage.js +64 -0
  111. package/src/plugins.js +12 -0
  112. package/src/roots.js +22 -13
  113. package/src/serveFile.js +26 -5
  114. package/src/serveGuiFiles.js +5 -2
  115. package/src/update.js +51 -18
  116. package/src/upload.js +49 -8
  117. package/src/util-http.js +31 -5
  118. package/src/vfs.js +84 -4
  119. package/src/webdav.js +12 -6
  120. package/admin/assets/AccountsPage-Cvi9DSc1.js +0 -3
  121. package/admin/assets/ArrayField-ChS9pNra.js +0 -2
  122. package/admin/assets/ConfigFilePage-6jCYVkbr.js +0 -4
  123. package/admin/assets/CustomHtmlPage-DaEXsEcQ.js +0 -2
  124. package/admin/assets/DataTable-Cf5S5yWr.js +0 -119
  125. package/admin/assets/DateTimeField--mTsSB1l.js +0 -1
  126. package/admin/assets/EventManager-BJWba-fL.js +0 -1
  127. package/admin/assets/FileField-B0WhrAuk.js +0 -117
  128. package/admin/assets/InternetPage-BICQDG_K.js +0 -8
  129. package/admin/assets/LangPage-D42UPSxC.js +0 -2
  130. package/admin/assets/LogsPage-DQTrf5GE.js +0 -4
  131. package/admin/assets/MenuButton-DDfB3119.js +0 -1
  132. package/admin/assets/MonitorPage-CH06c8aW.js +0 -2
  133. package/admin/assets/OptionsPage-BvXLA_R0.js +0 -3
  134. package/admin/assets/PluginsPage-CyaVJZkM.js +0 -1
  135. package/admin/assets/Tabs-CGfV3BK0.js +0 -1
  136. package/admin/assets/TextEditor-BeS-mR2I.css +0 -1
  137. package/admin/assets/TextEditor-Df9LSzm6.js +0 -34
  138. package/admin/assets/VfsPathField-6Kyy5vuQ.js +0 -1
  139. package/admin/assets/agentIcons-DwT9U-lh.js +0 -1
  140. package/admin/assets/baseUrl-D7S0KsqZ.js +0 -2
  141. package/admin/assets/icons-DWkdo8hc.js +0 -1
  142. package/admin/assets/index-11n5z0Ez.css +0 -1
  143. package/admin/assets/index-BDdPG215.js +0 -477
  144. package/admin/assets/routing-CxxvyWME.js +0 -1
  145. package/frontend/assets/index-legacy-B8EvbIOB.js +0 -1
@@ -19,6 +19,7 @@ const fs_1 = __importDefault(require("fs"));
19
19
  const promises_1 = require("fs/promises");
20
20
  const path_1 = require("path");
21
21
  const upload_1 = require("./upload");
22
+ const serveFile_1 = require("./serveFile");
22
23
  const misc_1 = require("./misc");
23
24
  const uploadOwners_1 = require("./uploadOwners");
24
25
  const config_1 = require("./config");
@@ -59,10 +60,21 @@ exports.frontEndApis = {
59
60
  return;
60
61
  if (!isAdmin)
61
62
  upload = lodash_1.default.omit(upload, 'ip');
62
- return { upload };
63
+ return { upload, activeContent: isAdmin && (0, serveFile_1.isActiveContentMime)((0, serveFile_1.getNodeMime)(node)) };
63
64
  }))
64
65
  };
65
66
  },
67
+ async set_upload_approved({ uri, approved }, ctx) {
68
+ (0, misc_1.apiAssertTypes)({ string: { uri }, boolean: { approved } });
69
+ if (!(0, adminApis_1.ctxAdminAccess)(ctx))
70
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
71
+ const node = await (0, vfs_1.urlToNode)(uri, ctx);
72
+ if (!node?.source)
73
+ return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
74
+ if (!(0, serveFile_1.isActiveContentMime)((0, serveFile_1.getNodeMime)(node)))
75
+ return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
76
+ return await (0, upload_1.setUploadApproved)(node.source, approved) ? {} : new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY);
77
+ },
66
78
  async create_folder({ uri, name }, ctx) {
67
79
  (0, misc_1.apiAssertTypes)({ string: { uri, name } });
68
80
  try {
@@ -115,10 +127,24 @@ exports.frontEndApis = {
115
127
  return moveFiles(uri_from, uri_to, ctx);
116
128
  },
117
129
  async copy_files({ uri_from, uri_to }, ctx) {
118
- return moveFiles(uri_from, uri_to, ctx, (srcNode, dest) => // override behavior
119
- (0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_read', ctx)
120
- || (0, promises_1.copyFile)(srcNode.source, dest, fs_1.default.constants.COPYFILE_EXCL | fs_1.default.constants.COPYFILE_FICLONE) // .source is checked by moveFiles
121
- .catch(e => e.code || String(e)));
130
+ return moveFiles(uri_from, uri_to, ctx, async (srcNode, dest) => {
131
+ const err = (0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_read', ctx);
132
+ if (err)
133
+ return err;
134
+ const upload = await (0, upload_1.getUploadMeta)(srcNode.source);
135
+ try {
136
+ return await (0, upload_1.whileUploadMetaPending)(dest, async () => {
137
+ await (0, promises_1.copyFile)(srcNode.source, dest, fs_1.default.constants.COPYFILE_EXCL | fs_1.default.constants.COPYFILE_FICLONE);
138
+ if (upload && !await (0, upload_1.saveUploadMeta)(dest, upload)) {
139
+ await (0, promises_1.unlink)(dest);
140
+ return const_1.HTTP_SERVER_ERROR;
141
+ }
142
+ });
143
+ }
144
+ catch (e) {
145
+ return e.code || String(e);
146
+ }
147
+ });
122
148
  },
123
149
  async comment({ uri, comment }, ctx) {
124
150
  (0, misc_1.apiAssertTypes)({ string: { uri, comment } });
@@ -197,6 +223,8 @@ async function moveFiles(uri_from, uri_to, ctx, override) {
197
223
  const src = srcNode?.source;
198
224
  if (!src)
199
225
  return const_1.HTTP_NOT_FOUND;
226
+ if ((0, upload_1.isUploading)(src))
227
+ return const_1.HTTP_CONFLICT;
200
228
  if (!override && (0, webdav_1.isWebdavLocked)(srcNode.vfsPath, ctx))
201
229
  return ctx.status;
202
230
  const destName = (0, path_1.basename)(src);
@@ -212,12 +240,19 @@ async function moveFiles(uri_from, uri_to, ctx, override) {
212
240
  return ctx.status;
213
241
  if (lodash_1.default.isFunction(override))
214
242
  return override?.(srcNode, dest);
243
+ const upload = await (0, upload_1.getUploadMeta)(src);
215
244
  return (0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_delete', ctx)
216
245
  || (0, promises_1.rename)(src, dest).catch(async (e) => {
217
246
  if (e.code !== 'EXDEV')
218
247
  throw e; // exdev = different drive
219
- await (0, promises_1.copyFile)(src, dest);
220
- await (0, promises_1.unlink)(src);
248
+ await (0, upload_1.whileUploadMetaPending)(dest, async () => {
249
+ await (0, promises_1.copyFile)(src, dest);
250
+ if (upload && !await (0, upload_1.saveUploadMeta)(dest, upload)) {
251
+ await (0, promises_1.unlink)(dest);
252
+ throw Error("couldn't move upload metadata");
253
+ }
254
+ await (0, promises_1.unlink)(src);
255
+ });
221
256
  }).then(() => (0, misc_1.moveStoredFileAttrs)(src, dest))
222
257
  .then(() => (0, uploadOwners_1.moveUploadOwner)(srcNode.vfsPath, destUri, dest))
223
258
  .catch(e => e.code || String(e));
@@ -227,6 +262,8 @@ async function moveFiles(uri_from, uri_to, ctx, override) {
227
262
  async function requestedRename(node, newName, ctx) {
228
263
  if (!node)
229
264
  throw new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
265
+ if (node.source && (0, upload_1.isUploading)(node.source))
266
+ throw new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT);
230
267
  // requestedRename is exported, so keep disk rename confinement here even when callers pre-validate
231
268
  if (!(0, util_files_1.isValidFileName)(newName))
232
269
  throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
package/src/github.js CHANGED
@@ -12,7 +12,6 @@ exports.readOnlinePlugin = readOnlinePlugin;
12
12
  exports.readOnlineCompatiblePlugin = readOnlineCompatiblePlugin;
13
13
  exports.getFolder2repo = getFolder2repo;
14
14
  exports.apiGithubPaginated = apiGithubPaginated;
15
- exports.searchPlugins = searchPlugins;
16
15
  const events_1 = __importDefault(require("./events"));
17
16
  const misc_1 = require("./misc");
18
17
  const plugins_1 = require("./plugins");
@@ -213,7 +212,7 @@ async function apiGithub(uri) {
213
212
  : e;
214
213
  });
215
214
  }
216
- async function* apiGithubPaginated(uri) {
215
+ async function* apiGithubPaginated(uri, { requireComplete = false } = {}) {
217
216
  uri += uri.includes('?') ? '&' : '?';
218
217
  const PAGE_SIZE = 100;
219
218
  let page = 1;
@@ -221,6 +220,8 @@ async function* apiGithubPaginated(uri) {
221
220
  try {
222
221
  while (1) {
223
222
  const res = await apiGithub(uri + `page=${page++}&per_page=${PAGE_SIZE}`);
223
+ if (requireComplete && res.incomplete_results)
224
+ throw Error("Incomplete GitHub search results");
224
225
  const a = res.items || res; // "search/repositories" returns an object, while "releases" returns simply an array
225
226
  for (const x of a)
226
227
  yield x;
@@ -231,47 +232,13 @@ async function* apiGithubPaginated(uri) {
231
232
  }
232
233
  }
233
234
  catch (e) {
234
- if (e.message !== '422') // for some strange reason github api is returning this error if we search repos for a missing user, instead of empty set
235
+ if (requireComplete || e.message !== '422') // for some strange reason github api is returning this error if we search repos for a missing user, instead of empty set
235
236
  throw e;
236
237
  }
237
238
  }
238
239
  async function isPluginBlacklisted(repo) {
239
240
  return (0, exports.getProjectInfo)().then(x => x?.repo_blacklist?.[repo]?.message || '', () => undefined);
240
241
  }
241
- async function searchPlugins(text = '', { skipRepos = [''] } = {}) {
242
- const seen = new Set();
243
- return new misc_1.AsapStream(pluginPromises());
244
- async function* pluginPromises() {
245
- // github doesn't allow complex search, so we have to do it multiple times and merge the results
246
- const searches = [
247
- ...text.split(' ').filter(Boolean).slice(0, 2).map(x => 'user:' + encodeURI(x)), // first 2 words can be the author of the plugin
248
- encodeURI(text), // search elsewhere, and results after the author search
249
- ];
250
- for (const term of searches) {
251
- for await (const it of apiGithubPaginated(`search/repositories?q=topic:hfs-plugin+${term}`)) {
252
- const repo = it.full_name;
253
- if (!repo || seen.has(repo)) // avoid duplicates, as we search multiple times
254
- continue;
255
- seen.add(repo);
256
- if (skipRepos.includes(repo))
257
- continue;
258
- yield (async () => {
259
- if (await isPluginBlacklisted(repo))
260
- return;
261
- const pl = await readOnlineCompatiblePlugin(repo, it.default_branch).catch(() => undefined);
262
- if (!pl)
263
- return;
264
- Object.assign(pl, {
265
- repo,
266
- downloading: exports.downloading[repo],
267
- license: it.license?.spdx_id,
268
- }, lodash_1.default.pick(it, ['pushed_at', 'stargazers_count', 'default_branch']));
269
- return pl;
270
- })();
271
- }
272
- }
273
- }
274
- }
275
242
  persistence_1.storedMap.ready().then(() => persistence_1.storedMap.del('alerts')); // remove legacy
276
243
  const cachedCentralInfo = persistence_1.storedMap.singleSync('cachedCentralInfo', ''); // persisting it could also be useful for no-internet instances, so that you can provide a fresher copy
277
244
  exports.blacklistedInstalledPlugins = [];
@@ -292,6 +259,8 @@ exports.getProjectInfo = (0, misc_1.debounceAsync)(async () => {
292
259
  }
293
260
  else
294
261
  obj = { ...cachedCentralInfo.get() || JSON.parse(builtInJson) }; // fall back to built-in
262
+ // older catalogs predate DNS providers; an explicit empty list still disables the built-ins
263
+ obj.acmeDns ??= JSON.parse(builtInJson).acmeDns;
295
264
  // merge byVersions info in the main object but collect alerts separately to preserve multiple instances
296
265
  const newAlerts = [obj.alert];
297
266
  for (const [ver, more] of Object.entries((0, misc_1.popKey)(obj, 'byVersion') || {}))
package/src/i18n.js CHANGED
@@ -37,12 +37,21 @@ function i18nFromTranslations(translations, embedded = 'en') {
37
37
  let selectedLang = ''; // keep track of where we find the translation
38
38
  const { embedded } = state;
39
39
  const langs = Object.keys(state.translations);
40
- for (const key of keys) {
40
+ const english = state.translations[embedded]?.translate || {};
41
+ for (let key of keys) {
42
+ const originalKey = key;
43
+ if (!(key in english))
44
+ key = Object.keys(english).find(k => english[k] === key) || key; // support extensions still passing English text
45
+ const legacyKey = english[key] === key ? undefined : english[key];
41
46
  for (const lang of searchLangs)
42
- if (found = state.translations[selectedLang = lang]?.translate?.[key])
47
+ if (found = state.translations[selectedLang = lang]?.translate?.[key]
48
+ || legacyKey && state.translations[selectedLang = lang]?.translate?.[legacyKey])
43
49
  break;
44
50
  if (found)
45
51
  break;
52
+ if (!fallback && english[key])
53
+ fallback = english[key];
54
+ key = originalKey;
46
55
  if (!warns.has(key) && langs.length && langs[0] !== embedded) {
47
56
  warns.add(key);
48
57
  console.debug("i18n miss:", key);
@@ -85,7 +94,7 @@ function i18nFromTranslations(translations, embedded = 'en') {
85
94
  }
86
95
  return pick(other);
87
96
  function pick(s) {
88
- return s.replace('#', String(v));
97
+ return s.replaceAll('#', String(v)).replace(/\{(\w+)\}/g, (_, key) => params[key] ?? '');
89
98
  }
90
99
  }
91
100
  }
@@ -118,7 +127,10 @@ function i18nFromTranslations(translations, embedded = 'en') {
118
127
  t,
119
128
  state,
120
129
  getLangs,
121
- i18nWrapperProps: () => ({ lang: state.disabled ? state.embedded : getLangs()[0] || state.embedded }),
130
+ i18nWrapperProps: () => {
131
+ const lang = state.disabled ? state.embedded : getLangs()[0] || state.embedded;
132
+ return { lang, dir: lang === 'ar' ? 'rtl' : 'ltr' };
133
+ },
122
134
  useI18N() {
123
135
  (0, valtio_1.useSnapshot)(state);
124
136
  return { t };
package/src/index.js CHANGED
@@ -34,6 +34,7 @@ const roots_1 = require("./roots");
34
34
  const events_1 = __importDefault(require("./events"));
35
35
  const ips_1 = require("./ips");
36
36
  require("./outboundProxy");
37
+ require("./pluginUsage");
37
38
  (0, assert_1.ok)(lodash_1.default.intersection(Object.keys(frontEndApis_1.frontEndApis), Object.keys(adminApis_1.adminApis)).length === 0); // they share same endpoints, don't clash
38
39
  if (new config_1.Version(process.versions.node).olderThan('18.15.0')) {
39
40
  console.error("Node.js 18.15+ is required, please update");
@@ -51,11 +52,12 @@ exports.app.use(middlewares_1.sessionMiddleware)
51
52
  .use(geo_1.geoFilter)
52
53
  .use(ips_1.trackIpsMw)
53
54
  .use(middlewares_1.gzipper)
55
+ .use(roots_1.rootsHostGuard)
56
+ .use(log_1.logMw)
57
+ .use(middlewares_1.someSecurity)
54
58
  .use(middlewares_1.paramsDecoder) // must be done before plugins, so they can manipulate params
55
59
  .use(middlewares_1.headRequests)
56
60
  .use(roots_1.rootsMiddleware)
57
- .use(log_1.logMw)
58
- .use(middlewares_1.someSecurity)
59
61
  .use(throttler_1.throttler)
60
62
  .use(plugins_1.pluginsMiddleware)
61
63
  .use((0, koa_mount_1.default)(const_1.API_URI, (0, apiMiddleware_1.apiMiddleware)({ ...frontEndApis_1.frontEndApis, ...adminApis_1.adminApis })))
package/src/lang.js CHANGED
@@ -17,17 +17,16 @@ const const_1 = require("./const");
17
17
  const plugins_1 = require("./plugins");
18
18
  const path_1 = require("path");
19
19
  const lodash_1 = __importDefault(require("lodash"));
20
- const PREFIX = 'hfs-lang-';
21
20
  const SUFFIX = '.json';
22
21
  function normalizeLangCode(code) {
23
22
  code = code.toLowerCase().split(';', 1)[0].trim();
24
23
  return /^[a-z]{2,3}(-[a-z0-9]{2,8})*$/.test(code) ? code : '';
25
24
  }
26
- function code2file(code) {
27
- return PREFIX + code.toLowerCase() + SUFFIX;
25
+ function code2file(code, admin = false) {
26
+ return (admin ? 'hfs-admin-lang-' : 'hfs-lang-') + code.toLowerCase() + SUFFIX;
28
27
  }
29
- function file2code(fn) {
30
- return fn.replace(PREFIX, '').replace(SUFFIX, '');
28
+ function file2code(fn, admin = false) {
29
+ return fn.replace(admin ? 'hfs-admin-lang-' : 'hfs-lang-', '').replace(SUFFIX, '');
31
30
  }
32
31
  const cache = (0, expiringCache_1.expiringCache)(3_000); // 3 seconds for both a good dx and acceptable performance
33
32
  async function getLangData(ctxOrLangCsv) {
@@ -41,11 +40,13 @@ async function getLangData(ctxOrLangCsv) {
41
40
  const csv = ctxOrLangCsv.toLowerCase();
42
41
  return cache.try(csv, async () => {
43
42
  const langs = csv.split(',').map(normalizeLangCode).filter(Boolean);
43
+ if (!langs.includes(const_1.EMBEDDED_LANGUAGE))
44
+ langs.push(const_1.EMBEDDED_LANGUAGE);
44
45
  const ret = {};
45
46
  let i = 0;
46
47
  while (i < langs.length) {
47
48
  let k = langs[i] || ''; // shut up ts
48
- if (!k || k === const_1.EMBEDDED_LANGUAGE)
49
+ if (!k)
49
50
  break;
50
51
  const fn = code2file(k);
51
52
  try {
@@ -79,11 +80,17 @@ let undo;
79
80
  if (!v)
80
81
  return forceLangData = undefined;
81
82
  const translation = embedded_1.default[v];
82
- forceLangData = { [v]: translation };
83
+ forceLangData = {
84
+ [v]: translation,
85
+ ...v === const_1.EMBEDDED_LANGUAGE ? {} : { [const_1.EMBEDDED_LANGUAGE]: embedded_1.default.en },
86
+ };
83
87
  if (v === const_1.EMBEDDED_LANGUAGE)
84
88
  return;
85
89
  const res = (0, watchLoad_1.watchLoad)(code2file(v), data => {
86
- forceLangData = { [v]: (0, misc_1.tryJson)(data) || translation };
90
+ forceLangData = {
91
+ [v]: (0, misc_1.tryJson)(data) || translation,
92
+ [const_1.EMBEDDED_LANGUAGE]: embedded_1.default.en,
93
+ };
87
94
  });
88
95
  undo = res.unwatch;
89
96
  });
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const hfs_lang_en_json_1 = __importDefault(require("./hfs-lang-en.json"));
6
7
  const hfs_lang_it_json_1 = __importDefault(require("./hfs-lang-it.json"));
7
8
  const hfs_lang_zh_json_1 = __importDefault(require("./hfs-lang-zh.json"));
8
9
  const hfs_lang_ru_json_1 = __importDefault(require("./hfs-lang-ru.json"));
@@ -29,4 +30,4 @@ const hfs_lang_uk_json_1 = __importDefault(require("./hfs-lang-uk.json"));
29
30
  const hfs_lang_ar_json_1 = __importDefault(require("./hfs-lang-ar.json"));
30
31
  const hfs_lang_lt_json_1 = __importDefault(require("./hfs-lang-lt.json"));
31
32
  const hfs_lang_bg_json_1 = __importDefault(require("./hfs-lang-bg.json"));
32
- 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, 'sr-latn': hfs_lang_sr_latn_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, pl: hfs_lang_pl_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, ro: hfs_lang_ro_json_1.default, tr: hfs_lang_tr_json_1.default, th: hfs_lang_th_json_1.default, uk: hfs_lang_uk_json_1.default, ar: hfs_lang_ar_json_1.default, lt: hfs_lang_lt_json_1.default, bg: hfs_lang_bg_json_1.default };
33
+ exports.default = { en: hfs_lang_en_json_1.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, 'sr-latn': hfs_lang_sr_latn_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, pl: hfs_lang_pl_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, ro: hfs_lang_ro_json_1.default, tr: hfs_lang_tr_json_1.default, th: hfs_lang_th_json_1.default, uk: hfs_lang_uk_json_1.default, ar: hfs_lang_ar_json_1.default, lt: hfs_lang_lt_json_1.default, bg: hfs_lang_bg_json_1.default };
@@ -39,6 +39,7 @@
39
39
  "Password": "كلمة المرور",
40
40
  "login_untrusted": "تم إيقاف تسجيل الدخول: لا يمكن الوثوق بهوية الخادم",
41
41
  "login_bad_credentials": "بيانات اعتماد غير صالحة",
42
+ "confirm_url_login": "يحاول هذا الرابط تسجيل دخولك باسم «{username}»",
42
43
  "login_bad_cookies": "الكوكيز لا تعمل - فشل تسجيل الدخول",
43
44
  "User panel": "لوحة المستخدم",
44
45
  "Change password": "تغيير كلمة المرور",
@@ -178,6 +179,9 @@
178
179
  "No": "لا",
179
180
  "esc_again": "انقر على ESC ثانية لاغلاق",
180
181
  "insufficient storage": "مساحة تخزين غير كافية",
181
- "Account disabled": "تم تعطيل الحساب"
182
+ "Account disabled": "تم تعطيل الحساب",
183
+ "upload_name": "تحميل بالاسم الجديد",
184
+ "Uploading": "جارٍ التحميل",
185
+ "proxy_413": "تحقق من هذا الحد على الخادم الوكيل"
182
186
  }
183
187
  }
@@ -39,6 +39,7 @@
39
39
  "Password": "Парола",
40
40
  "login_untrusted": "Вход отказан: самоличността на сървъра не е надеждна",
41
41
  "login_bad_credentials": "Невалидни данни за вход",
42
+ "confirm_url_login": "Тази връзка се опитва да ви впише като „{username}“",
42
43
  "login_bad_cookies": "Бисквитките не работят - вход неуспешен",
43
44
  "User panel": "Панел на потребителя",
44
45
  "Change password": "Смяна на парола",
@@ -176,6 +177,9 @@
176
177
  "No": "Не",
177
178
  "esc_again": "Натиснете ESC два пъти за затваряне",
178
179
  "insufficient storage": "недостатъчно пространство",
179
- "Account disabled": "Акаунтът е деактивиран"
180
+ "Account disabled": "Акаунтът е деактивиран",
181
+ "upload_name": "Качване с ново име",
182
+ "Uploading": "Качване",
183
+ "proxy_413": "Проверете за това ограничение на прокси сървъра"
180
184
  }
181
185
  }
@@ -39,6 +39,7 @@
39
39
  "Password": "Passwort",
40
40
  "login_untrusted": "Anmeldung abgebrochen: Der Serveridentität kann nicht vertraut werden.",
41
41
  "login_bad_credentials": "Ungültige Anmeldedaten",
42
+ "confirm_url_login": "Dieser Link versucht, Sie als „{username}“ anzumelden",
42
43
  "login_bad_cookies": "Cookies funktionieren nicht - Anmeldung fehlgeschlagen",
43
44
  "User panel": "Benutzer-Panel",
44
45
  "Change password": "Das Passwort ändern.",
@@ -178,6 +179,9 @@
178
179
  "No": "Nein",
179
180
  "esc_again": "Drucken Sie ESC zwei Mal, um das Fenster zu schließen.",
180
181
  "insufficient storage": "nicht genügend Speicherplatz",
181
- "Account disabled": "Konto deaktiviert"
182
+ "Account disabled": "Konto deaktiviert",
183
+ "upload_name": "Mit neuem Namen hochladen",
184
+ "Uploading": "Hochladen",
185
+ "proxy_413": "Überprüfen Sie den Proxyserver auf dieses Limit"
182
186
  }
183
187
  }
@@ -39,6 +39,7 @@
39
39
  "Password": "Κωδικός πρόσβασης",
40
40
  "login_untrusted": "Η σύνδεση ακυρώθηκε: δεν μπορεί να εμπιστευτείται την ταυτότητα του διακομιστή",
41
41
  "login_bad_credentials": "Μη έγκυρα διαπιστευτήρια",
42
+ "confirm_url_login": "Αυτός ο σύνδεσμος προσπαθεί να σας συνδέσει ως «{username}»",
42
43
  "login_bad_cookies": "Τα cookies δεν λειτουργούν - η σύνδεση απέτυχε",
43
44
  "User panel": "Πίνακας χρήστη",
44
45
  "Change password": "Αλλαγή κωδικού πρόσβασης",
@@ -178,6 +179,9 @@
178
179
  "No": "Οχι",
179
180
  "esc_again": "Πατήστε ESC δυο φορές για να κλειστειτε",
180
181
  "insufficient storage": "ανεπαρκής χώρος αποθήκευσης",
181
- "Account disabled": "Ο λογαριασμός έχει απενεργοποιηθεί"
182
+ "Account disabled": "Ο λογαριασμός έχει απενεργοποιηθεί",
183
+ "upload_name": "Μεταφόρτωση με νέο όνομα",
184
+ "Uploading": "Μεταφόρτωση",
185
+ "proxy_413": "Ελέγξτε για αυτό το όριο στον διακομιστή μεσολάβησης"
182
186
  }
183
187
  }
@@ -0,0 +1,188 @@
1
+ {
2
+ "author": "NAME OF THE AUTHOR(S) OF THIS TRANSLATION",
3
+ "version": 1.0,
4
+ "hfs_version": "0.57.5",
5
+ "translate": {
6
+ "Select": "Select",
7
+ "n_files": "{n,plural,one{# file} other{# files}}",
8
+ "n_folders": "{n,plural,one{# folder} other{# folders}}",
9
+ "filter_count": "{n,plural, one{# filtered} other{# filtered}}",
10
+ "select_count": "{n,plural, one{# selected} other{# selected}}",
11
+ "filter_placeholder": "Type here to filter the list below",
12
+ "Select some files": "Select some files",
13
+ "zip_checkboxes": "Use checkboxes to select the files, then you can use Zip again",
14
+ "zip_tooltip_selected": "Download selected elements as a single zip file",
15
+ "zip_tooltip_whole": "Download whole list (unfiltered) as a single zip file. If you select some elements, only those will be downloaded.",
16
+ "zip_confirm_search": "Download ALL results of this search as ZIP archive?",
17
+ "zip_confirm_folder": "Download WHOLE folder as ZIP archive?",
18
+ "select_tooltip": "Selection applies to \"Zip\" and \"Delete\" (when available), but you can also filter the list",
19
+ "delete_hint": "To delete, first click Select",
20
+ "delete_confirm": "Delete {n,plural, one{# item} other{# items}}?",
21
+ "delete_completed": "Deletion: {n} completed",
22
+ "delete_failed": ", {n,plural, one{# failed} other{# failed}}",
23
+ "delete_select": "Select something to delete",
24
+ "Delete": "Delete",
25
+ "Options": "Options",
26
+ "Search": "Search",
27
+ "Zip": "Zip",
28
+ "search_msg": "Search this folder and sub-folders",
29
+ "Searching": "Searching",
30
+ "Searched": "Searched",
31
+ "Clear search": "Clear search",
32
+ "Interrupted": "Interrupted",
33
+ "stopped_before": "Stopped before finding anything",
34
+ "empty_list": "Nothing here",
35
+ "filter_none": "No match for this filter",
36
+ "alpha_idx": "Alphabetical index",
37
+ "alpha_idx_button": "AZ",
38
+ "Admin-panel": "Admin-panel",
39
+ "Login": "Login",
40
+ "Username": "Username",
41
+ "Password": "Password",
42
+ "login_untrusted": "Login aborted: server identity cannot be trusted",
43
+ "login_bad_credentials": "Invalid credentials",
44
+ "confirm_url_login": "This link is trying to log you in as \"{username}\"",
45
+ "login_bad_cookies": "Cookies are not working - login failed",
46
+ "User panel": "User panel",
47
+ "Change password": "Change password",
48
+ "enter_pass": "Enter new password",
49
+ "enter_pass2": "Re-enter the same new password",
50
+ "pass2_mismatch": "The second password you entered did not match the first. Procedure aborted.",
51
+ "password_changed": "Password changed",
52
+ "Logout": "Logout",
53
+ "connection error": "connection error",
54
+ "Full timestamp:": "Full timestamp:",
55
+ "Search was interrupted": "Search was interrupted",
56
+ "Stop list": "Stop list",
57
+ "download_starting": "Your download should now start",
58
+ "wrong_account": "Account {u} has no access, try another",
59
+ "no_upload_here": "No upload permission for the current folder",
60
+ "Create folder": "Create folder",
61
+ "Pick files": "Pick files",
62
+ "Pick folder": "Pick folder",
63
+ "ready_to_upload": "{n,plural,one{# file} other{# files}}, {size}, ready to upload",
64
+ "Send": "Send",
65
+ "Clear": "Clear",
66
+ "failed_upload": "Couldn't upload {name}",
67
+ "file too large": "file too large",
68
+ "Enter folder name": "Enter folder name",
69
+ "Successfully created": "Successfully created",
70
+ "enter_folder": "Enter the folder",
71
+ "folder_exists": "Folder with same name already exists",
72
+ "Sort by:": "Sort by: {by}",
73
+ "name": "name",
74
+ "extension": "extension",
75
+ "size": "size",
76
+ "time": "time",
77
+ "Invert order": "Invert order",
78
+ "Folders first": "Folders first",
79
+ "Numeric names": "Numeric names",
80
+ "theme:": "theme:",
81
+ "auto": "auto",
82
+ "light": "light",
83
+ "dark": "dark",
84
+ "parent folder": "parent folder",
85
+ "home": "home",
86
+ "Continue": "Continue",
87
+ "Confirm": "Confirm",
88
+ "Don't": "Don't",
89
+ "Warning": "Warning",
90
+ "Error": "Error",
91
+ "Info": "Info",
92
+ "Unauthorized": "Unauthorized",
93
+ "Forbidden": "Forbidden",
94
+ "Not found": "Not found",
95
+ "Server error": "Server error",
96
+ "Upload": "Upload",
97
+ "upload_concluded": "Upload concluded:",
98
+ "upload_finished": "{n} finished ({size})",
99
+ "upload_errors": "{n} failed",
100
+ "upload_file_rejected": "Some files were not accepted",
101
+ "delete_unfinished_upload": "Remove the unfinished upload?",
102
+ "File menu": "File menu",
103
+ "Folder menu": "Folder menu",
104
+ "Name": "Name",
105
+ "file_open": "Open",
106
+ "Download": "Download",
107
+ "Missing permission": "Missing permission",
108
+ "Reload": "Reload",
109
+ "Get list": "Get list",
110
+ "Skip existing files": "Skip existing files",
111
+ "Size": "Size",
112
+ "Timestamp": "Timestamp",
113
+ "Show": "Show",
114
+ "Loading failed": "Loading failed",
115
+ "Rename": "Rename",
116
+ "Tiles mode:": "Tiles mode:",
117
+ "off": "off",
118
+ "Operation successful": "Operation successful",
119
+ "Uploader": "Uploader",
120
+ "Download counter": "Download counter",
121
+ "Switch zoom mode": "Switch zoom mode",
122
+ "Full screen": "Full screen",
123
+ "File Show help": "File Show help",
124
+ "showHelpMain": "You can use the keyboard for some actions:",
125
+ "showHelp_←/→": "←/→",
126
+ "showHelp_↑/↓": "↑/↓",
127
+ "showHelp_space": "space",
128
+ "showHelp_←/→_body": "Go to previous/next file",
129
+ "showHelp_↑/↓_body": "Scroll tall images",
130
+ "Destination": "Destination",
131
+ "in_queue": "{n} in queue",
132
+ "enter_comment": "Comment for {name}",
133
+ "Comment": "Comment",
134
+ "upload_dd_hint": "You can upload files by dragging and dropping them onto the file list",
135
+ "Upload not available": "Upload not available",
136
+ "Cut": "Cut",
137
+ "n_items": "{n,plural, one{# item} other{# items}}",
138
+ "move_results": "{good} moved{bad,plural, =0{} other{, # failed}}",
139
+ "after_cut": "Your selection is now in the clipboard.\nGo to destination folder to paste.",
140
+ "Cancel clipboard": "Cancel clipboard",
141
+ "to_clipboard_source": "Back to source folder",
142
+ "Paste": "Paste",
143
+ "clipboard_list": "Items in clipboard:",
144
+ "Close": "Close",
145
+ "Folder": "Folder",
146
+ "Web page": "Web page",
147
+ "Link": "Link",
148
+ "Auto-play": "Auto-play",
149
+ "autoplay_seconds": "Seconds to wait on images",
150
+ "Select all": "Select all",
151
+ "go_first": "Go to first item",
152
+ "go_last": "Go to last item",
153
+ "Shuffle": "Shuffle",
154
+ "Repeat": "Repeat",
155
+ "showHelpListShortcut": "From the file list, click while holding {key} to Show",
156
+ "Invalid value": "Invalid value",
157
+ "upload_interrupted": "{n} interrupted",
158
+ "Overwrite policy": "Overwrite policy",
159
+ "Rename to avoid overwriting": "Rename to avoid overwriting",
160
+ "Overwrite existing files": "Overwrite existing files",
161
+ "Menu": "Menu",
162
+ "clipboard": "Clipboard ({content})",
163
+ "to_clipboard_source_tooltip": "Go to the folder where the clipboard contents are located",
164
+ "more_items": "{n} more item(s)",
165
+ "Show details": "Show details",
166
+ "upload_conflict": "already exists",
167
+ "Logged in": "Logged in",
168
+ "Logged out": "Logged out",
169
+ "Cancel": "Cancel",
170
+ "allow_session_ip_change": "Allow IP change during this session",
171
+ "focus_hint": "By typing on your keyboard, you search and focus the items in the list.",
172
+ "copy_links": "Copy links",
173
+ "Calculate": "Calculate",
174
+ "Creation": "Creation",
175
+ "creation": "creation",
176
+ "required_change_password": "You are required to change your password",
177
+ "Wildcards": "Wildcards",
178
+ "Considering resume": "Considering resume",
179
+ "Yes": "Yes",
180
+ "No": "No",
181
+ "esc_again": "Press ESC twice to close",
182
+ "insufficient storage": "insufficient storage",
183
+ "Account disabled": "Account disabled",
184
+ "upload_name": "Upload with new name",
185
+ "Uploading": "Uploading",
186
+ "proxy_413": "Check for this limit on the proxy server"
187
+ }
188
+ }
@@ -39,6 +39,7 @@
39
39
  "Password": "Contraseña",
40
40
  "login_untrusted": "Acceso cancelado: la identidad del servidor no es confiable",
41
41
  "login_bad_credentials": "Credenciales no válidas",
42
+ "confirm_url_login": "Este enlace está intentando iniciar sesión como «{username}»",
42
43
  "login_bad_cookies": "Las Cookies no funcionan - acceso fallido",
43
44
  "User panel": "Panel de usuario",
44
45
  "Change password": "Cambiar contraseña",
@@ -133,8 +134,8 @@
133
134
  "in_queue": "{n} en cola",
134
135
  "enter_comment": "Descripción para {name}",
135
136
  "Comment": "Descripción",
136
- "upload_dd_hint": "Puede cargar archivos arrastrando y soltando en la lista de archivos",
137
- "Upload not available": "Carga no disponible",
137
+ "upload_dd_hint": "Puede subir archivos arrastrándolos y soltándolos en la lista de archivos",
138
+ "Upload not available": "Subida no disponible",
138
139
  "Cut": "Cortar",
139
140
  "n_items": "{n,plural, one{# elemento} other{# elementos}}",
140
141
  "move_results": "{good} movido, {bad} fallado",
@@ -182,6 +183,9 @@
182
183
  "No": "No",
183
184
  "esc_again": "Presione ESC dos veces para cerrar",
184
185
  "insufficient storage": "almacenamiento insuficiente",
185
- "Account disabled": "Cuenta deshabilitada"
186
+ "Account disabled": "Cuenta deshabilitada",
187
+ "upload_name": "Subir con nuevo nombre",
188
+ "Uploading": "Subiendo",
189
+ "proxy_413": "Verifique este límite en el servidor proxy"
186
190
  }
187
191
  }