hfs 0.42.3 → 0.44.0

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 (56) hide show
  1. package/README.md +56 -17
  2. package/admin/assets/index-35f6e3dc.css +1 -0
  3. package/admin/assets/index-ef68a7ab.js +510 -0
  4. package/{frontend/assets/sha512-f6798733.js → admin/assets/sha512-d091720e.js} +1 -1
  5. package/admin/index.html +2 -2
  6. package/frontend/assets/index-a3b0d6ac.js +94 -0
  7. package/frontend/assets/index-fe0f3d77.css +1 -0
  8. package/{admin/assets/sha512-69b26793.js → frontend/assets/sha512-96fd938f.js} +1 -1
  9. package/frontend/fontello.css +9 -3
  10. package/frontend/fontello.woff2 +0 -0
  11. package/frontend/index.html +3 -2
  12. package/package.json +2 -2
  13. package/plugins/antibrute/plugin.js +1 -1
  14. package/plugins/download-counter/plugin.js +14 -5
  15. package/plugins/download-counter/public/main.js +12 -2
  16. package/plugins/vhosting/plugin.js +17 -11
  17. package/src/adminApis.js +4 -4
  18. package/src/api.auth.js +4 -1
  19. package/src/api.file_list.js +22 -10
  20. package/src/api.lang.js +9 -11
  21. package/src/api.vfs.js +43 -29
  22. package/src/apiMiddleware.js +3 -2
  23. package/src/block.js +6 -20
  24. package/src/config.js +6 -2
  25. package/src/const.js +5 -4
  26. package/src/customHtml.js +2 -2
  27. package/src/debounceAsync.js +3 -3
  28. package/src/frontEndApis.js +7 -32
  29. package/src/github.js +26 -8
  30. package/src/index.js +2 -1
  31. package/src/lang.js +67 -0
  32. package/src/langs/embedded.js +13 -0
  33. package/src/langs/hfs-lang-it.json +100 -0
  34. package/src/langs/hfs-lang-ko.json +103 -0
  35. package/src/langs/hfs-lang-ms.json +70 -0
  36. package/src/langs/hfs-lang-ru.json +106 -0
  37. package/src/langs/hfs-lang-sr.json +108 -0
  38. package/src/langs/hfs-lang-zh-tw.json +106 -0
  39. package/src/langs/hfs-lang-zh.json +98 -0
  40. package/src/listen.js +8 -3
  41. package/src/log.js +6 -2
  42. package/src/middlewares.js +24 -18
  43. package/src/misc.js +17 -9
  44. package/src/perm.js +1 -1
  45. package/src/plugins.js +6 -6
  46. package/src/serveFile.js +2 -2
  47. package/src/serveGuiFiles.js +23 -10
  48. package/src/update.js +1 -2
  49. package/src/upload.js +6 -6
  50. package/src/util-http.js +5 -3
  51. package/src/vfs.js +114 -74
  52. package/src/zip.js +4 -4
  53. package/admin/assets/index-08017e15.js +0 -511
  54. package/admin/assets/index-94bbe0be.css +0 -1
  55. package/frontend/assets/index-5f125477.js +0 -94
  56. package/frontend/assets/index-a09cacfd.css +0 -1
@@ -0,0 +1,106 @@
1
+ {
2
+ "author": "Mefistofell, SanokKule",
3
+ "version": 1.92,
4
+ "hfs_version": "0.43.0",
5
+ "translate": {
6
+ "Select": "Выбор",
7
+ "n_files": "{n,plural, one{1 файл} few{# файла} other{# файлов}}",
8
+ "n_folders": "{n,plural, one{1 папка} few{# папки} other{# папок}}",
9
+ "filter_count": "{n,plural, one{1 элемент} few{# элемента} other{# элементов}}",
10
+ "select_count": "{n,plural, one{Выбран #} other{Выбрано #}}",
11
+ "filter_placeholder": "Введите текст для фильтрации списка",
12
+ "Select some files": "Выделите файлы",
13
+ "zip_checkboxes": "Выделите нужные файлы и снова нажмите zip",
14
+ "zip_tooltip_selected": "Скачать выбранные элементы в zip-архиве",
15
+ "zip_tooltip_whole": "Выбрать файлы для скачивания в zip-архиве. Если файлы не выбраны, то будет скачана вся папка",
16
+ "zip_confirm_search": "Скачать все результаты поиска в одном zip-архиве",
17
+ "zip_confirm_folder": "Скачать всю папку как zip-архив?",
18
+ "select_tooltip": "Выбор применяется к \"Zip\" и \"Удалить\" (если доступно), но также список можно отфильтровать",
19
+ "delete_hint": "Для удаления файлов сначала нажмите Выбор",
20
+ "delete_confirm": "Удалить {n,plural, =1{элемент} one{# элемент} few{# элемента} many{# элементов} other{# элементов}}?",
21
+ "delete_completed": "{n,plural, one{Удалён} other{Удалено}} {n,plural, one{# элемент} few{# элемента} many{# элементов} other{# элементов}}",
22
+ "delete_failed": ", {n} не удалось",
23
+ "delete_select": "Выделите элементы для удаления",
24
+ "Delete": "Удалить",
25
+ "Options": "Настройки",
26
+ "Search": "Поиск",
27
+ "Zip": "Zip",
28
+ "search_msg": "Поиск в текущей и вложенных папках",
29
+ "Searching": "Идёт поиск",
30
+ "Searched": "Результаты поиска",
31
+ "Clear search": "Отменить поиск",
32
+ "Interrupted": "Прерван",
33
+ "stopped_before": "Поиск остановлен",
34
+ "empty_list": "Здесь ничего нет",
35
+ "filter_none": "Ни один элемент не соответствует фильтру",
36
+ "Admin-panel": "Панель администратора",
37
+ "Login": "Вход",
38
+ "Username": "Имя пользователя",
39
+ "Password": "Пароль",
40
+ "login_untrusted": "Вход отменён: личности сервера нельзя доверять",
41
+ "login_bad_credentials": "Неверное имя пользователя или пароль",
42
+ "login_bad_cookies": "Не удалось войти - отключены cookies",
43
+ "User panel": "Панель пользователя",
44
+ "Change password": "Изменить пароль",
45
+ "enter_pass": "Введите новый пароль",
46
+ "enter_pass2": "Повторите пароль",
47
+ "pass2_mismatch": "Введённые пароли не совпадают",
48
+ "password_changed": "Пароль успешно изменён",
49
+ "Logout": "Выйти",
50
+ "connection error": "Ошибка соединения",
51
+ "Full timestamp:": "Дата и время: ",
52
+ "Search was interrupted": "Поиск прерван",
53
+ "Stop list": "Остановить",
54
+ "upload_starting": "Загрузка началась",
55
+ "wrong_account": "Пользователь {u} не имеет доступа к этой папке",
56
+ "no_upload_here": "Нет доступа к загрузке файлов в эту папку",
57
+ "Create folder": "Создать папку",
58
+ "Pick files": "Выбрать файлы",
59
+ "Pick folder": "Выбрать папку",
60
+ "send_files": "Загрузить {n,plural, one{# файл} few{# файла} many{# файлов} other{# файла}}, {size}",
61
+ "Clear": "Очистить",
62
+ "failed_upload": "Не удалась загрузить {name}",
63
+ "confirm_resume": "Возобновить загрузку?",
64
+ "file too large": "слишком большой файл",
65
+ "Enter folder name": "Введите имя папки",
66
+ "Successfully created": "Успешно создано",
67
+ "enter_folder": "Перейти в папку",
68
+ "folder_exists": "Папка уже существует",
69
+ "Sort by": "Сортировка по",
70
+ "name": "имени",
71
+ "extension": "расширению",
72
+ "size": "размеру",
73
+ "time": "времени",
74
+ "Invert order": "Инвертировать",
75
+ "Folders first": "Сначала папки",
76
+ "Numeric names": "Сначала цифры",
77
+ "theme:": "Тема:",
78
+ "auto": "авто",
79
+ "light": "светлая",
80
+ "dark": "тёмная",
81
+ "parent folder": "Родительская папка",
82
+ "home": "Домашняя папка",
83
+ "Continue": "Продолжить",
84
+ "Confirm": "Да",
85
+ "Don't": "Нет",
86
+ "Warning": "Внимание",
87
+ "Error": "Ошибка",
88
+ "Info": "Информация",
89
+ "Unauthorized": "Вход не выполнен",
90
+ "Forbidden": "Запрещено",
91
+ "Not found": "Не найдено",
92
+ "Server error": "Ошибка сервера",
93
+ "Upload": "Загрузить",
94
+ "upload_concluded": "Загрузка окончена:",
95
+ "upload_finished": "{n,plural, one{# файл загружен} few{# файла загружено} other{# файлов загружено}} ({size})",
96
+ "upload_errors": "{n,plural, one{# файл} few{# файла} other{# файлов}} не удалось загрузить",
97
+ "upload_file_rejected": "Некоторые файлы не были приняты",
98
+ "download counter": "Количество скачиваний",
99
+ "File menu": "Меню файла",
100
+ "Folder menu": "Меню папки",
101
+ "Name": "Имя",
102
+ "file_open": "Открыть",
103
+ "Download": "Скачать",
104
+ "Missing permission": "Отсутствует доступ"
105
+ }
106
+ }
@@ -0,0 +1,108 @@
1
+ {
2
+ "author": "Горан",
3
+ "version": 1.6,
4
+ "hfs_version": "0.43.0",
5
+ "translate": {
6
+ "Select": "Изабери",
7
+ "n_files": "{n,plural, one {# документ} other{# документа}}",
8
+ "n_folders": "{n,plural, one {# фолдер} other{# фолдера}}",
9
+ "filter_count": "{n,plural, one{# ставка} other{# ставки}}",
10
+ "select_count": "{n,plural, one{# изабран} other{# изабранa}}",
11
+ "filter_placeholder": "Филтер",
12
+ "Select some files": "Изаберите неке документе",
13
+ "zip_checkboxes": "Изаберите жељене документе и поново притисните zip",
14
+ "zip_tooltip_selected": "Преузми изабране ставке у zip архиву",
15
+ "zip_tooltip_whole": "Изаберите датотеке за преузимање у zip архиви. Ако ниједан документ није изабран, цео директоријум ће бити преузет",
16
+ "zip_confirm_search": "Преузми све резултате претраге у једној зип архиви",
17
+ "zip_confirm_folder": "Сачувај цео директоријум у зип архиву?",
18
+ "select_tooltip": "Изаберите потребне датотеке",
19
+ "delete_hint": "Да бисте избрисали датотеке, прво притисните Изабери",
20
+ "delete_confirm": "Избриши {n,plural, one{1 елемент} =2 =3 =4 {# елеменaтa} other{# елементи}}?",
21
+ "delete_completed": "{n,plural,one{# елемент обрисан} other{# елемената обрисано}}",
22
+ "delete_failed": ", {n,plural,one{# неуспешно обрисан} other{# неуспешно обрисаних}}",
23
+ "delete_select": "Изабери ставке за брисање",
24
+ "Delete": "Избриши",
25
+ "Options": "Сортирање",
26
+ "Search": "Претрага",
27
+ "Zip": "Zip",
28
+ "search_msg": "Претражи тренутни фолдер и све подфолдере",
29
+ "Searching": "Претражујем...",
30
+ "Searched": "Резултати претраге",
31
+ "Clear search": "Откажи претрагу",
32
+ "Interrupted": "Прекинуто",
33
+ "stopped_before": "Претрага је заустављена",
34
+ "empty_list": "Празна листа",
35
+ "filter_none": "Ниједна ставка не одговара филтеру",
36
+ "Admin-panel": "Администратор-панел",
37
+ "Login": "Пријава",
38
+ "Username": "Корисничко име",
39
+ "Password": "Лозинка",
40
+ "Continue": "Настави",
41
+ "login_untrusted": "Пријава је прекинута",
42
+ "login_bad_credentials": "Неважеће корисничко име или лозинка",
43
+ "login_bad_cookies": "Пријава је прекинута. Колачићи су онемогућени",
44
+ "User panel": "Кориснички панел",
45
+ "Change password": "Промени лозинку",
46
+ "enter_pass": "Унесите нову лозинку",
47
+ "enter_pass2": "Поновите лозинку",
48
+ "pass2_mismatch": "Унете лозинке се не поклапају",
49
+ "password_changed": "Лозинка је успешно промењена",
50
+ "Logout": "Одјава",
51
+ "connection error": " Грешка везе ",
52
+ "Full timestamp:": "Датум и време: ",
53
+ "Search was interrupted": "Претрага је прекинута",
54
+ "Stop list": "Заустави",
55
+ "Upload": "Отпреми",
56
+ "upload_starting": "Отпремање је почело",
57
+ "wrong_account": "Корисник {u} нема приступ овом директоријуму",
58
+ "no_upload_here": "Немогуће отпремање овде",
59
+ "Create folder": "Креирај фолдер",
60
+ "Pick files": " Изаберите документ(а)",
61
+ "Pick folder": "Изаберите фолдер(е)",
62
+ "send_files": "Отпремите {n} документ(а), {size}",
63
+ "Clear": "Обриши",
64
+ "failed_upload": "Отпремање није успело {name}",
65
+ "confirm_resume": "Настави преузимање?",
66
+ "file too large": "Неважећа величина документа",
67
+ "Upload error": "Грешка при отпремању",
68
+ "Enter folder name": "Унесите назив фолдера",
69
+ "Successfully created": "Успешно креирано",
70
+ "enter_folder": "Иди у фолдер",
71
+ "folder_exists": "Фолдер већ постоји",
72
+ "finished": "учитано",
73
+ "Sort by": "Сортирај по",
74
+ "name": "Име",
75
+ "extension": " Екстензија",
76
+ "size": "Величина",
77
+ "time": "Време",
78
+ "Invert order": "Обрни редослед",
79
+ "Folders first": "Прво фолдери",
80
+ "Numeric names": "Прво бројеви",
81
+ "theme:": "Изглед:",
82
+ "auto": "Ауто",
83
+ "light": "Светло",
84
+ "dark": "Тамно",
85
+ "parent folder": "Надређени фолдер",
86
+ "home": "Почетна страна",
87
+ "Confirm": "Да",
88
+ "Don't": "Не",
89
+ "Warning": "Упозорење",
90
+ "Error": "Грешка",
91
+ "Info": "Информација",
92
+ "Unauthorized": "Пријава није успела",
93
+ "Forbidden": "Забрањено",
94
+ "Not found": "Није пронађено",
95
+ "Server error": " Сервер Грешка",
96
+ "upload_concluded": "Отпремање завршено:",
97
+ "upload_finished": "{n} пребачен(о) ({size})",
98
+ "upload_errors": "{n} неуспешно пребачен(их)",
99
+ "upload_file_rejected": "Нека документа нису прихваћена",
100
+ "download counter": "бројач преузимања",
101
+ "File menu": "Мени документа",
102
+ "Folder menu": "Мени фолдера",
103
+ "Name": "Име",
104
+ "file_open": "Отвори",
105
+ "Download": "Преузми",
106
+ "Missing permission": "Додељене дозволе онемогућавају преузимање докумената, фолдера"
107
+ }
108
+ }
@@ -0,0 +1,106 @@
1
+ {
2
+ "author": "Phoenix",
3
+ "version": 1.0,
4
+ "hfs_version": "0.43.0",
5
+ "translate": {
6
+ "Select": "選擇",
7
+ "n_files": "{n,plural,one{# 個檔案} other{# 個檔案}}",
8
+ "n_folders": "{n,plural,one{# 個資料夾} other{# 個資料夾}}",
9
+ "filter_count": "{n,plural, one{# 已過濾} other{# 已過濾}}",
10
+ "select_count": "{n,plural, one{# 已選擇} other{# 已選擇}}",
11
+ "filter_placeholder": "輸入內容,過濾下方列表",
12
+ "Select some files": "選擇一些檔案",
13
+ "zip_checkboxes": "使用選擇來複選檔案,然後再次使用Zip壓縮",
14
+ "zip_tooltip_selected": "將所選檔案下載為單一Zip壓縮檔",
15
+ "zip_tooltip_whole": "將整個列表(未過濾)作為單一Zip壓縮檔下載.如果選擇了一些檔案,則只會下載那些檔案",
16
+ "zip_confirm_search": "將此搜尋的所有結果下載為Zip壓縮檔?",
17
+ "zip_confirm_folder": "確認將整個資料夾下載為Zip壓縮檔?",
18
+ "select_tooltip": "選擇適用於\"Zip\"和\"刪除\"(如果有的話),也可以過濾列表",
19
+ "delete_hint": "請先點擊“選擇”某些項目,才能執行刪除.",
20
+ "delete_confirm": "確認刪除 {n,plural, one{# 項} other{# 項}}?",
21
+ "delete_completed": "刪除: {n} 完成",
22
+ "delete_failed": ", {n,plural, one{# 失敗} other{# 失敗}}",
23
+ "delete_select": "選擇要刪除的項目",
24
+ "Delete": "刪除",
25
+ "Options": "選項",
26
+ "Search": "搜尋",
27
+ "Zip": "Zip",
28
+ "search_msg": "搜尋目前資料夾和子資料夾",
29
+ "Searching": "搜尋中",
30
+ "Searched": "搜尋完成",
31
+ "Clear search": "清除搜尋條件",
32
+ "Interrupted": "中止",
33
+ "stopped_before": "已停止",
34
+ "empty_list": "空列表",
35
+ "filter_none": "過濾後無找到",
36
+ "Admin-panel": "管理面板",
37
+ "Login": "登入",
38
+ "Username": "使用者名稱",
39
+ "Password": "密碼",
40
+ "login_untrusted": "登入已中止:無法信任伺服器身份",
41
+ "login_bad_credentials": "帳號密碼錯誤",
42
+ "login_bad_cookies": "Cookies無法運作 - 登入失敗",
43
+ "User panel": "使用者面板",
44
+ "Change password": "修改密碼",
45
+ "enter_pass": "輸入新密碼",
46
+ "enter_pass2": "再次輸入相同的新密碼",
47
+ "pass2_mismatch": "密碼輸入不一致。修改失敗!",
48
+ "password_changed": "密碼修改成功",
49
+ "Logout": "登出",
50
+ "connection error": "連線錯誤",
51
+ "Full timestamp:": "完整的時間戳:",
52
+ "Search was interrupted": "搜尋已中止",
53
+ "Stop list": "停止",
54
+ "upload_starting": "開始下載",
55
+ "wrong_account": "使用者 {u} 沒有存取權限,請更換使用者",
56
+ "no_upload_here": "沒有上傳權限",
57
+ "Create folder": "建立資料夾",
58
+ "Pick files": "選擇檔案",
59
+ "Pick folder": "選擇資料夾",
60
+ "send_files": "開始上傳 {n,plural,one{# 檔案} other{# 檔案}}, {size}",
61
+ "Clear": "清空",
62
+ "failed_upload": "無法上傳 {name}",
63
+ "confirm_resume": "繼續上傳?",
64
+ "file too large": "檔案太大",
65
+ "Enter folder name": "輸入資料夾名稱",
66
+ "Successfully created": "建立成功",
67
+ "enter_folder": "進入資料夾",
68
+ "folder_exists": "已有相同名稱資料夾存在",
69
+ "Sort by": "排序依據",
70
+ "name": "檔案名稱",
71
+ "extension": "副檔名",
72
+ "size": "檔案大小",
73
+ "time": "檔案時間",
74
+ "Invert order": "倒序排列",
75
+ "Folders first": "資料夾先排序",
76
+ "Numeric names": "數字編號排序",
77
+ "theme:": "主題:",
78
+ "auto": "自動",
79
+ "light": "淺色",
80
+ "dark": "深色",
81
+ "parent folder": "上層資料夾",
82
+ "home": "主目錄",
83
+ "Continue": "繼續",
84
+ "Confirm": "確認",
85
+ "Don't": "取消",
86
+ "Warning": "警告",
87
+ "Error": "錯誤",
88
+ "Info": "資訊",
89
+ "Unauthorized": "未授權",
90
+ "Forbidden": "禁止",
91
+ "Not found": "找不到",
92
+ "Server error": "伺服器錯誤",
93
+ "Upload": "上傳",
94
+ "upload_concluded": "上傳完成:",
95
+ "upload_finished": "{n} 個檔案完成 ({size})",
96
+ "upload_errors": "{n} 失敗",
97
+ "upload_file_rejected": "部分檔案不被允許",
98
+ "download counter": "下載次數",
99
+ "File menu": "檔案選單",
100
+ "Folder menu": "資料夾選單",
101
+ "Name": "名稱",
102
+ "file_open": "開啟",
103
+ "Download": "下載",
104
+ "Missing permission": "權限不足"
105
+ }
106
+ }
@@ -0,0 +1,98 @@
1
+ {
2
+ "author": "yx3016",
3
+ "version": 1.1,
4
+ "hfs_version": "0.40.1",
5
+ "translate": {
6
+ "Select": "选择",
7
+ "n_files": "{n} 个文件",
8
+ "n_folders": "{n} 个文件夹",
9
+ "filter_count": "{n} 通过筛选",
10
+ "select_count": "{n} 已选择",
11
+ "filter_placeholder": "输入筛选内容,筛选以下列表",
12
+ "Select some files": "去勾选文件!",
13
+ "zip_checkboxes": "点击复选框选择文件,然后再次使用Zip打包!",
14
+ "zip_tooltip_selected": "将所选元素下载为单个zip文件",
15
+ "zip_tooltip_whole": "将整个列表(未经过滤)作为一个zip文件下载.如果选择了一些元素,则只会下载这些元素.",
16
+ "zip_confirm_search": "将此搜索的所有结果下载为ZIP压缩包?",
17
+ "zip_confirm_folder": "确认将整个文件夹下载为ZIP压缩包?",
18
+ "select_tooltip": "选择适用于“Zip”和“删除”,也可以筛选列表.",
19
+ "delete_hint": "请先单击“选择”某个项目,才能执行删除.",
20
+ "delete_confirm": "确认删除 {n} 项?",
21
+ "delete_completed": "删除: {n} 成功!",
22
+ "delete_failed": "删除失败, {n,plural, one{# failed} other{# failed}}",
23
+ "delete_select": "选择要删除的内容",
24
+ "Delete": "删除",
25
+ "Options": "选项",
26
+ "Search": "搜索",
27
+ "Zip": "Zip",
28
+ "search_msg": "搜索当前文件夹和子文件夹",
29
+ "Searching": "搜索中...",
30
+ "Searched": "搜索完成!",
31
+ "Clear search": "清除搜索条件",
32
+ "Interrupted": "暂停",
33
+ "stopped_before": "已停止",
34
+ "empty_list": "空",
35
+ "filter_none": "无过滤",
36
+ "Admin-panel": "管理面板",
37
+ "Login": "登陆",
38
+ "Username": "用户名",
39
+ "Password": "密码",
40
+ "login_untrusted": "登录中止:服务器标识不可信!",
41
+ "login_bad_credentials": "证书参数有问题",
42
+ "login_bad_cookies": "Cookie错误-登录失败",
43
+ "User panel": "用户面板",
44
+ "Change password": "修改密码",
45
+ "enter_pass": "输入新密码",
46
+ "enter_pass2": "再次输入一遍密码",
47
+ "pass2_mismatch": "2次密码输入的不一致。修改失败!",
48
+ "password_changed": "密码修改成功",
49
+ "Logout": "注销",
50
+ "connection error": "链接错误",
51
+ "Full timestamp:": "完整的时间戳:",
52
+ "Search was interrupted": "搜索已中断",
53
+ "Stop list": "停止",
54
+ "upload_starting": "开始下载",
55
+ "wrong_account": "用户 {u} 没有访问权限,请更换用户",
56
+ "no_upload_here": "没有上传权限",
57
+ "Create folder": "创建文件夹",
58
+ "Pick files": "选择文件",
59
+ "Pick folder": "选择文件夹",
60
+ "send_files": "开始上传 {n,plural,one{# file} other{# files}}, {size}",
61
+ "Clear": "清空",
62
+ "failed_upload": "无法上传 {name}",
63
+ "confirm_resume": "是否重命名?",
64
+ "file too large": "文件太大",
65
+ "Enter folder name": "输入文件夹名称",
66
+ "Successfully created": "创建成功",
67
+ "enter_folder": "进入文件夹",
68
+ "folder_exists": "已存在同名文件夹",
69
+ "Sort by": "排序依据",
70
+ "name": "文件名",
71
+ "extension": "扩展名",
72
+ "size": "文件大小",
73
+ "time": "文件时间",
74
+ "Invert order": "降序",
75
+ "Folders first": "文件夹在前",
76
+ "Numeric names": "数字名称",
77
+ "theme:": "主题:",
78
+ "auto": "auto",
79
+ "light": "明亮",
80
+ "dark": "暗黑",
81
+ "parent folder": "上层文件夹",
82
+ "home": "根目录",
83
+ "Continue": "继续",
84
+ "Confirm": "确认",
85
+ "Don't": "取消",
86
+ "Warning": "警告",
87
+ "Error": "错误",
88
+ "Unauthorized": "未授权",
89
+ "Forbidden": "禁止",
90
+ "Not found": "未找到",
91
+ "Server error": "服务器错误",
92
+ "Upload": "上传",
93
+ "Upload terminated": "上传已终止",
94
+ "upload_finished": "上传{n} 结束 ({size})",
95
+ "upload_errors": "上传失败{n} 失败",
96
+ "download counter": "download 计数"
97
+ }
98
+ }
package/src/listen.js CHANGED
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.getUrls = exports.getServerStatus = exports.httpsPortCfg = exports.openAdmin = exports.portCfg = void 0;
30
+ exports.getUrls = exports.getServerStatus = exports.httpsPortCfg = exports.openAdmin = exports.portCfg = exports.getHttpsWorkingPort = void 0;
31
31
  const http = __importStar(require("http"));
32
32
  const config_1 = require("./config");
33
33
  const index_1 = require("./index");
@@ -44,6 +44,11 @@ const lodash_1 = __importDefault(require("lodash"));
44
44
  let httpSrv;
45
45
  let httpsSrv;
46
46
  const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', !const_1.DEV);
47
+ function getHttpsWorkingPort() {
48
+ var _a;
49
+ return (httpsSrv === null || httpsSrv === void 0 ? void 0 : httpsSrv.listening) && ((_a = httpsSrv.address()) === null || _a === void 0 ? void 0 : _a.port);
50
+ }
51
+ exports.getHttpsWorkingPort = getHttpsWorkingPort;
47
52
  exports.portCfg = (0, config_1.defineConfig)('port', 80);
48
53
  exports.portCfg.sub(async (port) => {
49
54
  while (!index_1.app)
@@ -104,8 +109,8 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
104
109
  httpsSrv.on('connection', connections_1.newConnection);
105
110
  printUrls(port, 'https');
106
111
  });
107
- const cert = (0, config_1.defineConfig)('cert');
108
- const privateKey = (0, config_1.defineConfig)('private_key');
112
+ const cert = (0, config_1.defineConfig)('cert', '');
113
+ const privateKey = (0, config_1.defineConfig)('private_key', '');
109
114
  const httpsNeeds = [cert, privateKey];
110
115
  const httpsOptions = { cert: '', private_key: '' };
111
116
  for (const cfg of httpsNeeds) {
package/src/log.js CHANGED
@@ -38,6 +38,7 @@ const events_1 = __importDefault(require("./events"));
38
38
  const lodash_1 = __importDefault(require("lodash"));
39
39
  const util_files_1 = require("./util-files");
40
40
  const perm_1 = require("./perm");
41
+ const misc_1 = require("./misc");
41
42
  class Logger {
42
43
  constructor(name) {
43
44
  this.name = name;
@@ -79,6 +80,7 @@ errorLogFile.sub(path => {
79
80
  accessErrorLog.setPath(path);
80
81
  });
81
82
  const logRotation = (0, config_1.defineConfig)('log_rotation', 'weekly');
83
+ const dontLogNet = (0, config_1.defineConfig)('dont_log_net', '127.0.0.1|::1', misc_1.makeNetMatcher);
82
84
  function log() {
83
85
  const debounce = lodash_1.default.debounce(cb => cb(), 1000);
84
86
  return async (ctx, next) => {
@@ -87,6 +89,8 @@ function log() {
87
89
  console.debug(ctx.status, ctx.method, ctx.path);
88
90
  Promise.race([(0, stream_1.once)(ctx.res, 'finish'), (0, stream_1.once)(ctx.res, 'close')]).then(() => {
89
91
  var _a, _b, _c, _d;
92
+ if (dontLogNet.compiled()(ctx.ip))
93
+ return;
90
94
  const isError = ctx.status >= 400;
91
95
  const logger = isError && accessErrorLog || accessLogger;
92
96
  const rotate = (_a = logRotation.get()) === null || _a === void 0 ? void 0 : _a[0];
@@ -135,7 +139,7 @@ debugLogFile.on('open', () => {
135
139
  const was = console.error;
136
140
  console.error = function (...args) {
137
141
  was.apply(this, args);
138
- const params = args.map(x => typeof x === 'string' ? x : JSON.stringify(x)).join(' ');
139
- debugLogFile.write(new Date().toLocaleString() + ': ' + params + '\n');
142
+ args = args.map(x => { var _a; return typeof x === 'string' ? x : ((_a = (0, misc_1.tryJson)(x)) !== null && _a !== void 0 ? _a : String(x)); });
143
+ debugLogFile.write(new Date().toLocaleString() + ': ' + args.join(' ') + '\n');
140
144
  };
141
145
  }).on('error', () => console.log("cannot create debug.log"));
@@ -4,9 +4,8 @@ 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.paramsDecoder = exports.prepareState = exports.getProxyDetected = exports.someSecurity = exports.serveGuiAndSharedFiles = exports.sessions = exports.headRequests = exports.gzipper = void 0;
7
+ exports.paramsDecoder = exports.prepareState = exports.getProxyDetected = exports.someSecurity = exports.serveGuiAndSharedFiles = exports.headRequests = exports.gzipper = void 0;
8
8
  const koa_compress_1 = __importDefault(require("koa-compress"));
9
- const koa_session_1 = __importDefault(require("koa-session"));
10
9
  const const_1 = require("./const");
11
10
  const const_2 = require("./const");
12
11
  const vfs_1 = require("./vfs");
@@ -28,6 +27,10 @@ const formidable_1 = __importDefault(require("formidable"));
28
27
  const upload_1 = require("./upload");
29
28
  const adminApis_1 = require("./adminApis");
30
29
  const zlib_1 = require("zlib");
30
+ const listen_1 = require("./listen");
31
+ const config_1 = require("./config");
32
+ const forceHttps = (0, config_1.defineConfig)('force_https', true);
33
+ const ignoreProxies = (0, config_1.defineConfig)('ignore_proxies', false);
31
34
  exports.gzipper = (0, koa_compress_1.default)({
32
35
  threshold: 2048,
33
36
  gzip: { flush: zlib_1.constants.Z_SYNC_FLUSH },
@@ -52,13 +55,6 @@ const headRequests = async (ctx, next) => {
52
55
  ctx.response.length = length;
53
56
  };
54
57
  exports.headRequests = headRequests;
55
- const sessions = (app) => (0, koa_session_1.default)({
56
- key: 'hfs_$id',
57
- signed: true,
58
- rolling: true,
59
- maxAge: const_1.SESSION_DURATION,
60
- }, app);
61
- exports.sessions = sessions;
62
58
  const serveFrontendFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.FRONTEND_PROXY, const_2.FRONTEND_URI);
63
59
  const serveFrontendPrefixed = (0, koa_mount_1.default)(const_2.FRONTEND_URI.slice(0, -1), serveFrontendFiles);
64
60
  const serveAdminFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.ADMIN_PROXY, const_1.ADMIN_URI);
@@ -74,6 +70,13 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
74
70
  }
75
71
  if (ctx.body)
76
72
  return next();
73
+ if (!ctx.secure && forceHttps.get() && (0, listen_1.getHttpsWorkingPort)() && !(0, misc_1.isLocalHost)(ctx)) {
74
+ const { URL } = ctx;
75
+ URL.protocol = 'https';
76
+ URL.port = (0, listen_1.getHttpsWorkingPort)();
77
+ ctx.status = 307; // this ensures the client doesn't switch to a simpler GET request
78
+ return ctx.redirect(URL.href);
79
+ }
77
80
  if (path.startsWith(const_2.FRONTEND_URI))
78
81
  return serveFrontendPrefixed(ctx, next);
79
82
  if (path.length === const_1.ADMIN_URI.length - 1 && const_1.ADMIN_URI.startsWith(path))
@@ -132,20 +135,20 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
132
135
  : serveFrontendFiles(ctx, next);
133
136
  };
134
137
  exports.serveGuiAndSharedFiles = serveGuiAndSharedFiles;
135
- let proxyDetected = false;
138
+ let proxyDetected;
136
139
  const someSecurity = async (ctx, next) => {
137
140
  ctx.request.ip = (0, connections_1.normalizeIp)(ctx.ip);
138
141
  try {
139
- let proxy = ctx.get('X-Forwarded-For');
140
- // we have some dev-proxies to ignore
141
- if (const_1.DEV && proxy && [process.env.FRONTEND_PROXY, process.env.ADMIN_PROXY].includes(ctx.get('X-Forwarded-port')))
142
- proxy = '';
143
142
  if ((0, misc_1.dirTraversal)(decodeURI(ctx.path)))
144
143
  return ctx.status = const_1.HTTP_FOOL;
145
144
  if ((0, block_1.applyBlock)(ctx.socket, ctx.ip))
146
145
  return;
147
- proxyDetected || (proxyDetected = proxy > '');
148
- ctx.state.proxiedFor = proxy;
146
+ if (!ctx.ips.length && ctx.get('X-Forwarded-For') // empty ctx.ips implies we didn't configure for proxies
147
+ // we have some dev-proxies to ignore
148
+ && !(const_1.DEV && [process.env.FRONTEND_PROXY, process.env.ADMIN_PROXY].includes(ctx.get('X-Forwarded-port')))) {
149
+ proxyDetected = ctx;
150
+ ctx.state.when = new Date();
151
+ }
149
152
  }
150
153
  catch (_a) {
151
154
  return ctx.status = const_1.HTTP_FOOL;
@@ -153,9 +156,12 @@ const someSecurity = async (ctx, next) => {
153
156
  return next();
154
157
  };
155
158
  exports.someSecurity = someSecurity;
156
- // this is only about http proxies
159
+ // limited to http proxies
157
160
  function getProxyDetected() {
158
- return proxyDetected;
161
+ if ((proxyDetected === null || proxyDetected === void 0 ? void 0 : proxyDetected.state.when) < Date.now() - const_1.DAY)
162
+ proxyDetected = undefined;
163
+ return !ignoreProxies.get() && proxyDetected
164
+ && { from: proxyDetected.ip, for: proxyDetected.get('X-Forwarded-For') };
159
165
  }
160
166
  exports.getProxyDetected = getProxyDetected;
161
167
  const prepareState = async (ctx, next) => {
package/src/misc.js CHANGED
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.try_ = exports.stream2string = exports.tryJson = exports.same = exports.matches = exports.matchesNet = exports.isLocalHost = exports.with_ = exports.typedKeys = exports.objRenameKey = exports.onOff = exports.pendingPromise = exports.onlyTruthy = exports.truthy = exports.pattern2filter = exports.onFirstEvent = exports.onProcessExit = exports.randomId = exports.getOrSet = exports.wantArray = exports.waitFor = exports.wait = exports.newObj = exports.setHidden = exports.prefix = exports.removeStarting = exports.enforceFinal = exports.debounceAsync = void 0;
21
+ exports.try_ = exports.stream2string = exports.tryJson = exports.same = exports.matches = exports.makeMatcher = exports.makeNetMatcher = exports.isLocalHost = exports.with_ = exports.hasProp = exports.typedKeys = exports.objRenameKey = exports.onOff = exports.pendingPromise = exports.onlyTruthy = exports.truthy = exports.pattern2filter = exports.onFirstEvent = exports.onProcessExit = exports.randomId = exports.getOrSet = exports.wantArray = exports.waitFor = exports.wait = exports.newObj = exports.setHidden = exports.prefix = exports.removeStarting = exports.enforceFinal = exports.debounceAsync = void 0;
22
22
  const path_1 = require("path");
23
23
  const lodash_1 = __importDefault(require("lodash"));
24
24
  const assert_1 = __importDefault(require("assert"));
@@ -28,6 +28,7 @@ __exportStar(require("./util-files"), exports);
28
28
  const debounceAsync_1 = __importDefault(require("./debounceAsync"));
29
29
  exports.debounceAsync = debounceAsync_1.default;
30
30
  const micromatch_1 = require("micromatch");
31
+ const cidr_tools_1 = __importDefault(require("cidr-tools"));
31
32
  function enforceFinal(sub, s) {
32
33
  return s.endsWith(sub) ? s : s + sub;
33
34
  }
@@ -165,6 +166,10 @@ function typedKeys(o) {
165
166
  return Object.keys(o);
166
167
  }
167
168
  exports.typedKeys = typedKeys;
169
+ function hasProp(obj, key) {
170
+ return key in obj;
171
+ }
172
+ exports.hasProp = hasProp;
168
173
  function with_(par, cb) {
169
174
  return cb(par);
170
175
  }
@@ -174,16 +179,19 @@ function isLocalHost(c) {
174
179
  return ip && (ip === '::1' || ip.endsWith('127.0.0.1'));
175
180
  }
176
181
  exports.isLocalHost = isLocalHost;
177
- function matchesNet(ip, mask, emptyMaskReturns = false) {
178
- if (typeof ip !== 'string')
179
- ip = ip.ip;
180
- return matches(ip, mask, emptyMaskReturns);
182
+ function makeNetMatcher(mask, emptyMaskReturns = false) {
183
+ return !mask ? () => emptyMaskReturns
184
+ : mask.includes('/') ? (ip) => cidr_tools_1.default.contains(mask, ip)
185
+ : makeMatcher(mask);
186
+ }
187
+ exports.makeNetMatcher = makeNetMatcher;
188
+ function makeMatcher(mask, emptyMaskReturns = false) {
189
+ return mask ? (0, micromatch_1.matcher)(mask.replace(/^(!)?/, '$1(') + ')') // adding () will allow us to use the pipe at root level
190
+ : () => emptyMaskReturns;
181
191
  }
182
- exports.matchesNet = matchesNet;
192
+ exports.makeMatcher = makeMatcher;
183
193
  function matches(s, mask, emptyMaskReturns = false) {
184
- if (!mask)
185
- return emptyMaskReturns;
186
- return (0, micromatch_1.isMatch)(s, '(' + mask + ')'); // adding () will allow us to use the pipe at root level
194
+ return makeMatcher(mask, emptyMaskReturns)(s); // adding () will allow us to use the pipe at root level
187
195
  }
188
196
  exports.matches = matches;
189
197
  function same(a, b) {