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.
- package/README.md +56 -17
- package/admin/assets/index-35f6e3dc.css +1 -0
- package/admin/assets/index-ef68a7ab.js +510 -0
- package/{frontend/assets/sha512-f6798733.js → admin/assets/sha512-d091720e.js} +1 -1
- package/admin/index.html +2 -2
- package/frontend/assets/index-a3b0d6ac.js +94 -0
- package/frontend/assets/index-fe0f3d77.css +1 -0
- package/{admin/assets/sha512-69b26793.js → frontend/assets/sha512-96fd938f.js} +1 -1
- package/frontend/fontello.css +9 -3
- package/frontend/fontello.woff2 +0 -0
- package/frontend/index.html +3 -2
- package/package.json +2 -2
- package/plugins/antibrute/plugin.js +1 -1
- package/plugins/download-counter/plugin.js +14 -5
- package/plugins/download-counter/public/main.js +12 -2
- package/plugins/vhosting/plugin.js +17 -11
- package/src/adminApis.js +4 -4
- package/src/api.auth.js +4 -1
- package/src/api.file_list.js +22 -10
- package/src/api.lang.js +9 -11
- package/src/api.vfs.js +43 -29
- package/src/apiMiddleware.js +3 -2
- package/src/block.js +6 -20
- package/src/config.js +6 -2
- package/src/const.js +5 -4
- package/src/customHtml.js +2 -2
- package/src/debounceAsync.js +3 -3
- package/src/frontEndApis.js +7 -32
- package/src/github.js +26 -8
- package/src/index.js +2 -1
- package/src/lang.js +67 -0
- package/src/langs/embedded.js +13 -0
- package/src/langs/hfs-lang-it.json +100 -0
- package/src/langs/hfs-lang-ko.json +103 -0
- package/src/langs/hfs-lang-ms.json +70 -0
- package/src/langs/hfs-lang-ru.json +106 -0
- package/src/langs/hfs-lang-sr.json +108 -0
- package/src/langs/hfs-lang-zh-tw.json +106 -0
- package/src/langs/hfs-lang-zh.json +98 -0
- package/src/listen.js +8 -3
- package/src/log.js +6 -2
- package/src/middlewares.js +24 -18
- package/src/misc.js +17 -9
- package/src/perm.js +1 -1
- package/src/plugins.js +6 -6
- package/src/serveFile.js +2 -2
- package/src/serveGuiFiles.js +23 -10
- package/src/update.js +1 -2
- package/src/upload.js +6 -6
- package/src/util-http.js +5 -3
- package/src/vfs.js +114 -74
- package/src/zip.js +4 -4
- package/admin/assets/index-08017e15.js +0 -511
- package/admin/assets/index-94bbe0be.css +0 -1
- package/frontend/assets/index-5f125477.js +0 -94
- package/frontend/assets/index-a09cacfd.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@charset "UTF-8";:root{height:100dvh;--bg: #fff;--text: #555;--ghost-contrast: #8882;--faint-contrast: #8884;--mild-contrast: #8886;--good-contrast: #000a;--button-bg: #68a;--button-text: #eaeaea;--focus-color: #468;--separator: " – "}:root .highlightedText,:root .file-menu a:hover{color:#0006;text-shadow:0 0 3px rgba(0,0,0,.4)}:root .theme-dark{--bg: #000;--text: #999;--good-contrast: #fffa;--button-bg: #345;--button-text: #999;color-scheme:dark}:root .theme-dark .highlightedText,:root .theme-dark .file-menu a:hover,:root .file-menu .theme-dark a:hover{color:#fff;text-shadow:0 0 3px #fff}:root .theme-dark a{color:#8ac}:root .theme-dark .dialog-closer{background:#633}:root .theme-dark .dialog-icon{color:#ccc}:root .theme-dark .dialog-icon .icon{color:#aaa;margin-left:-1px;font-size:95%}:root .theme-dark .dialog-backdrop{background:rgba(51,51,51,.7333333333)}:root .theme-dark .error-msg{color:#b88;background-color:#623}:root .theme-dark button.toggled{color:#eee}body{background-color:var(--bg);margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,button,select,input{font-size:12pt}#root{max-width:50em;margin:auto;min-height:100vh;display:flex;flex-direction:column}body,input{color:var(--text)}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}input:not([type=checkbox]),select{padding:.3em .4em;border-radius:.5em;background:var(--bg);border-color:var(--mild-contrast);color:var(--good-contrast);max-width:100%;box-sizing:border-box;width:100%}input[type=checkbox]{transform:scale(1.7);accent-color:var(--button-bg)}label input[type=checkbox]{margin-right:.8em}select{text-align:center}.hidden{display:none!important}.icon{font-size:1.2em}.emoji-icon,.file-icon{display:inline-block;width:1.4em;text-align:center}.file-icon{height:1em;background-size:contain;background-repeat:no-repeat;background-position:center;vertical-align:text-bottom}.icon.mirror:before{transform:scaleX(-1)}a{text-decoration:none;color:#57a}button{background-color:var(--button-bg);color:var(--button-text);padding:.5em 1em;border:transparent;text-decoration:none;border-radius:.3em;vertical-align:middle;cursor:pointer}button:hover{outline:1px solid var(--mild-contrast)}button.toggled{color:#fff;text-shadow:0 0 3px #fff}button:focus-visible,.breadcrumb:focus-visible{outline:3px solid var(--focus-color)}a>button{width:100%}input:focus-visible,select:focus-visible,ul a:focus-visible{border-radius:.3em;border-color:transparent;outline:2px solid var(--focus-color)}.icon-button,ul.dir li .entry-panel .file-menu-button{font-size:.7em;padding:.2em .4em;margin-left:.4em;vertical-align:bottom}.error-msg{background-color:#faa;color:#833;padding:.5em 1em}.hide-back,.upload-toolbar,header{background-color:var(--bg)}header{position:sticky;top:0;padding:.2em;z-index:1}.before-sliding{width:0!important;flex:0!important;margin:0!important;height:0!important;padding:0!important;overflow:hidden!important;transition:flex .5s}.show-sliding{transition:flex .5s;overflow:clip;flex:1;white-space:nowrap}.ani-working{animation:1s blink infinite}@keyframes blink{0%{opacity:1}50%{opacity:.2}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.spinner,.icon.spinner:before{animation:1.5s spin infinite linear;display:inline-flex;justify-content:center;align-items:center;width:min-content}.icon.emoji.spinner{display:inline-block}.breadcrumb{padding:.1em .6em .2em;line-height:1.8em;border-radius:.7em;background-color:var(--button-bg);color:var(--button-text);border-top:1px solid #666;margin-right:-.1em}.breadcrumb:nth-child(-n+3) .icon{padding:0 .2em}#folder-stats,#filter-bar>span{font-size:90%}#folder-stats{margin:.5em 0 0 .5em;float:right}#folder-stats .icon{margin-right:.3em}#filter{flex:1;box-sizing:border-box}#filter-bar{display:flex;align-items:center;gap:.8em;margin:.2em 0 0;padding:2px 0 1px 11px;height:1.8em}#filter-bar input[type=checkbox]{margin-top:.5em}#filter-bar span:empty{display:none}ul.dir{flex:1;padding:0;margin:0;clear:both}ul.dir>p{text-align:center}ul.dir li{display:block;list-style-type:none;padding:.3em .3em .4em;border-bottom:1px solid var(--faint-contrast)}ul.dir li:nth-of-type(odd){background-color:var(--ghost-contrast)}ul.dir li input[type=checkbox]{margin:0 .8em}ul.dir li a:last-of-type{word-break:break-word;padding-right:.3em}ul.dir li a .icon{margin-right:.3em}ul.dir li a:hover{text-decoration:underline}ul.dir li .entry-panel{float:right;margin-top:.3em;display:flex;align-items:center}ul.dir li .entry-panel .file-menu-button{margin:-3px 0 -3px .4em}ul.dir li .entry-panel .entry-details{font-size:90%;margin-left:4px;font-variant-numeric:tabular-nums}ul.dir li .entry-panel .entry-details .entry-size-unit{margin-left:.3em}ul.dir li>div:last-of-type{clear:both}ul.dir li.page-separator{margin-top:1em;position:relative}ul.dir li.page-separator:before{content:attr(label);position:absolute;top:-1.8em;font-size:smaller;margin-left:calc(50% - 1em);opacity:.9}#menu-bar{display:flex;justify-content:space-evenly;flex-wrap:wrap}#menu-bar>*{flex:1;margin:.1em}#menu-bar button{padding-left:0;padding-right:0}#searched{margin:.2em}#user-panel{display:flex;flex-direction:column;gap:1em}#user-panel a>button{width:100%}button label{cursor:inherit;margin-left:.5em}.dialog-backdrop.working{font-size:5em;animation:1s fade-in}.dialog-content{padding:.2em}.dialog-alert .dialog-content{text-align:center}.dialog-alert .dialog-content p{text-align:left;display:inline-block}.dialog{min-width:11em;--color: var(--button-bg)}#paging{position:sticky;bottom:0;display:flex;gap:.1em;background-color:var(--bg);padding:0 .2em .2em}#paging>button{z-index:1}#paging button{box-shadow:0 0 .3em .3em #0003}#paging #paging-middle{padding:0 .5em;margin:0 -.3em;display:flex;gap:.5em;flex:1;overflow-x:auto}#paging #paging-middle>button{flex:1;padding-top:0;padding-bottom:0}#paging button{background:var(--button-bg);text-align:center;white-space:nowrap;padding:.5em}.upload-toolbar{position:sticky;top:-4px}.upload-progress:before{content:var(--separator)}.entry-size:after{content:var(--separator)}.upload-progress{min-width:4em;display:inline-block;margin-left:.5em}.upload-list td:nth-child(1){width:0}.upload-list td:nth-child(2){text-align:right;width:0;white-space:nowrap;padding-left:.5em}.upload-list td:nth-child(3){padding:.2em .5em;word-break:break-word}.dialog-login form{display:flex;flex-direction:column;gap:1.2em}.dialog-login label{display:block;margin-bottom:.5em;margin-left:.1em}.miss-perm{margin:.3em}.popup-menu-button{font-size:.8em;padding:.2em .3em;position:absolute;opacity:.8}.popup-menu-button:hover{opacity:1}.file-dialog .dialog-content{min-width:calc(100% - 1em)}.file-dialog .dialog{min-width:13em}.file-dialog-properties{word-break:break-word;line-height:1.5em}.file-dialog-properties dt{font-weight:700}.file-dialog-properties dd{margin-left:1.5em}.file-menu{margin-top:1em;display:flex;flex-direction:column}.file-menu a{padding:.5em 0}.file-menu a:first-child{padding-top:1em;border-top:1px solid var(--faint-contrast)}.file-menu a .icon{margin-right:.5em}@media (min-width: 42em){body{scrollbar-width:thin;scrollbar-color:var(--button-bg) var(--ghost-contrast)}body::-webkit-scrollbar{width:12px}body::-webkit-scrollbar-track{background:var(--ghost-contrast)}body::-webkit-scrollbar-thumb{background-color:var(--button-bg);border-radius:20px;border:1px solid var(--ghost-contrast)}}@media (max-width: 42em){:root{--ghost-contrast: #8883}body,button,select{font-size:14pt}#menu-bar button label,#filter-bar button label{display:none}#filter-bar{margin-top:.4em}#filter-bar button{width:17.6vw;height:2.3em}.breadcrumb{word-break:break-all}.breadcrumb .icon{font-size:24px}}.dialog-backdrop{position:fixed;inset:0;background:#8886;backdrop-filter:blur(2px);display:flex;justify-content:center;align-items:center;z-index:1000}.dialog{background:#fff;background:var(--bg);padding:max(.5em,1vw);border-radius:1em;position:relative;margin:0 3vw;overflow:hidden;max-height:calc(100vh - 2em);display:flex;flex-direction:column;justify-content:center}.dialog-icon{color:#fff;background-color:var(--color);position:absolute;top:0;width:2em;height:1.8em;text-align:center;border-radius:.8em 0}.dialog-title{font-size:120%;margin-top:-.4em;padding:0 .5em}.dialog-closer~.dialog-title{margin-right:2em}.dialog-type~.dialog-title{margin-left:2em}.dialog-icon~.dialog-title{text-align:center}.dialog-closer{border-radius:0 .8em;right:0;padding:0;background-color:#c88}.dialog-icon~.dialog-content{margin-top:2em}.dialog-type{left:0;top:0;overflow:hidden;line-height:1.8em;opacity:.8}.dialog-content{overflow:auto;max-height:calc(100vh - 4.5em)}.dialog-content p{white-space:pre-wrap;margin:.5em 0}.dialog-confirm .dialog-content button{margin-top:1em}.dialog-alert-info{--color: #282 }.dialog-alert-warning{--color: #c91 }.dialog-alert-error{--color: #822}@media (max-width: 42em){.dialog-icon{font-size:120%}.dialog-icon~.dialog-content{margin-top:2.5em}.dialog-title{margin-top:-.2em}}.dialog-prompt label{display:block;margin-bottom:.5em;margin-left:.1em}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as SF}from"./index-
|
|
1
|
+
import{c as SF}from"./index-a3b0d6ac.js";function OF(iF,hF){for(var eF=0;eF<hF.length;eF++){const tF=hF[eF];if(typeof tF!="string"&&!Array.isArray(tF)){for(const w in tF)if(w!=="default"&&!(w in iF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(iF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(iF,Symbol.toStringTag,{value:"Module"}))}var EF={},UF={get exports(){return EF},set exports(iF){EF=iF}};/*
|
|
2
2
|
* [js-sha512]{@link https://github.com/emn178/js-sha512}
|
|
3
3
|
*
|
|
4
4
|
* @version 0.8.0
|
package/frontend/fontello.css
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
@font-face {
|
|
2
2
|
font-family: 'fontello';
|
|
3
|
-
src: url('fontello.woff2?
|
|
3
|
+
src: url('fontello.woff2?19258766') format('woff2'); font-weight: normal;
|
|
4
|
+
font-weight: normal;
|
|
4
5
|
font-style: normal;
|
|
5
6
|
}
|
|
6
7
|
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
|
|
@@ -9,7 +10,7 @@
|
|
|
9
10
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
|
10
11
|
@font-face {
|
|
11
12
|
font-family: 'fontello';
|
|
12
|
-
src: url('../font/fontello.svg?
|
|
13
|
+
src: url('../font/fontello.svg?19258766#fontello') format('svg');
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
16
|
*/
|
|
@@ -49,9 +50,11 @@
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
.fa-cog:before { content: '\e800'; } /* '' */
|
|
53
|
+
.fa-audio:before { content: '\e801'; } /* '' */
|
|
52
54
|
.fa-doc:before { content: '\e802'; } /* '' */
|
|
53
55
|
.fa-stop:before { content: '\e803'; } /* '' */
|
|
54
56
|
.fa-play:before { content: '\e804'; } /* '' */
|
|
57
|
+
.fa-music:before { content: '\e805'; } /* '' */
|
|
55
58
|
.fa-cancel:before { content: '\e806'; } /* '' */
|
|
56
59
|
.fa-edit:before { content: '\e807'; } /* '' */
|
|
57
60
|
.fa-check:before { content: '\e808'; } /* '' */
|
|
@@ -63,8 +66,12 @@
|
|
|
63
66
|
.fa-to_start:before { content: '\e80e'; } /* '' */
|
|
64
67
|
.fa-retweet:before { content: '\e80f'; } /* '' */
|
|
65
68
|
.fa-to_end:before { content: '\e810'; } /* '' */
|
|
69
|
+
.fa-picture:before { content: '\e811'; } /* '' */
|
|
70
|
+
.fa-camera:before { content: '\e812'; } /* '' */
|
|
66
71
|
.fa-search:before { content: '\e813'; } /* '' */
|
|
67
72
|
.fa-logout:before { content: '\e814'; } /* '' */
|
|
73
|
+
.fa-video:before { content: '\e815'; } /* '' */
|
|
74
|
+
.fa-left:before { content: '\e816'; } /* '' */
|
|
68
75
|
.fa-spin6:before { content: '\e839'; } /* '' */
|
|
69
76
|
.fa-crown:before { content: '\e844'; } /* '' */
|
|
70
77
|
.fa-download:before { content: '\f02e'; } /* '' */
|
|
@@ -73,5 +80,4 @@
|
|
|
73
80
|
.fa-menu:before { content: '\f0c9'; } /* '' */
|
|
74
81
|
.fa-quote:before { content: '\f10d'; } /* '' */
|
|
75
82
|
.fa-unlink:before { content: '\f127'; } /* '' */
|
|
76
|
-
.fa-level-up:before { content: '\f148'; } /* '' */
|
|
77
83
|
.fa-archive:before { content: '\f1c6'; } /* '' */
|
package/frontend/fontello.woff2
CHANGED
|
Binary file
|
package/frontend/index.html
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0" />
|
|
6
6
|
<link href="/fontello.css" rel="stylesheet" />
|
|
7
7
|
|
|
8
|
-
<script
|
|
9
|
-
<
|
|
8
|
+
<script>HFS={}</script>
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-a3b0d6ac.js"></script>
|
|
10
|
+
<link rel="stylesheet" href="/assets/index-fe0f3d77.css">
|
|
10
11
|
</head>
|
|
11
12
|
<body>
|
|
12
13
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hfs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "HTTP File Server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file server",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"build-frontend": "npm run build --workspace=frontend",
|
|
21
21
|
"build-admin": "npm run build --workspace=admin",
|
|
22
22
|
"server-for-test": "node dist/src --cwd . --config tests && rm custom.html",
|
|
23
|
-
"server-for-test-dev": "cross-env DEV=1 nodemon --ignore tests/ --watch src -e ts,tsx --exec ts-node src -- --cwd . --config tests",
|
|
23
|
+
"server-for-test-dev": "cross-env DEV=1 FRONTEND_PROXY=3005 ADMIN_PROXY=3006 nodemon --ignore tests/ --watch src -e ts,tsx --exec ts-node src -- --cwd . --config tests",
|
|
24
24
|
"test": "mocha -r ts-node/register 'tests/**/*.ts'",
|
|
25
25
|
"pub": "cd dist && npm publish",
|
|
26
26
|
"dist": "npm run build-all && npm run dist-bin",
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
exports.description = "Counts downloads for each file, and displays the total in the list"
|
|
2
|
-
exports.version =
|
|
3
|
-
exports.apiRequired =
|
|
1
|
+
exports.description = "Counts downloads for each file, and displays the total in the list or file menu"
|
|
2
|
+
exports.version = 4 // config.where
|
|
3
|
+
exports.apiRequired = 8
|
|
4
|
+
|
|
5
|
+
exports.config = {
|
|
6
|
+
where: { frontend: true, type: 'select', defaultValue: 'menu',
|
|
7
|
+
options: ['list', { value: 'menu', label: "file menu" }],
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.configDialog = {
|
|
11
|
+
sx: { maxWidth: '20em' },
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
exports.init = async api => {
|
|
6
15
|
const _ = api.require('lodash')
|
|
@@ -38,8 +47,8 @@ exports.init = async api => {
|
|
|
38
47
|
counters[path] = counters[path] + 1 || 1
|
|
39
48
|
save()
|
|
40
49
|
},
|
|
41
|
-
onDirEntry: ({ entry,
|
|
42
|
-
const path =
|
|
50
|
+
onDirEntry: ({ entry, listUri }) => {
|
|
51
|
+
const path = listUri + entry.n
|
|
43
52
|
const n = counters[path]
|
|
44
53
|
if (n)
|
|
45
54
|
entry.hits = n
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
(() => { // this wrapper avoids name clashing of outer variables and functions
|
|
2
|
+
const config = HFS.getPluginConfig()
|
|
3
|
+
|
|
4
|
+
const inMenu = config.where === 'menu'
|
|
5
|
+
HFS.onEvent('additionalEntryDetails', ({ entry: { hits } }) =>
|
|
6
|
+
hits && !inMenu && `<span class="download-counter" title="${HFS.t`download counter`}">${hits}</span>`)
|
|
7
|
+
|
|
8
|
+
HFS.onEvent('fileMenu', ({ entry, props }) => {
|
|
9
|
+
if (inMenu && !entry.isFolder)
|
|
10
|
+
props.push(["Downloads", entry.hits || 0])
|
|
11
|
+
})
|
|
12
|
+
})()
|
|
@@ -21,26 +21,32 @@ exports.init = api => {
|
|
|
21
21
|
const { matches } = api.require('./misc')
|
|
22
22
|
return {
|
|
23
23
|
middleware(ctx) {
|
|
24
|
-
let
|
|
24
|
+
let params // undefined if we are not going to work on api parameters
|
|
25
25
|
if (ctx.path.startsWith(api.const.SPECIAL_URI)) { // special uris should be excluded...
|
|
26
26
|
// ...unless it's a frontend api with a path param
|
|
27
|
-
if (!ctx.path.startsWith(api.const.API_URI)
|
|
27
|
+
if (!ctx.path.startsWith(api.const.API_URI)) return
|
|
28
28
|
let { referer } = ctx.headers
|
|
29
29
|
referer &&= new URL(referer).pathname
|
|
30
|
-
if (referer?.startsWith(ctx.state.revProxyPath + api.const.ADMIN_URI)) return
|
|
31
|
-
|
|
30
|
+
if (referer?.startsWith(ctx.state.revProxyPath + api.const.ADMIN_URI)) return // exclude apis for admin-panel
|
|
31
|
+
params = ctx.params
|
|
32
32
|
}
|
|
33
|
+
|
|
33
34
|
const hosts = api.getConfig('hosts')
|
|
34
35
|
if (!hosts?.length) return
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const row = hosts?.find(x => matches(ctx.host, x.host))
|
|
37
|
+
if (!row) {
|
|
38
|
+
if (api.getConfig('mandatory')) {
|
|
39
|
+
ctx.socket.destroy()
|
|
40
|
+
return true
|
|
39
41
|
}
|
|
40
|
-
|
|
41
|
-
ctx.socket.destroy()
|
|
42
|
-
return true
|
|
42
|
+
return
|
|
43
43
|
}
|
|
44
|
+
if (!params)
|
|
45
|
+
ctx.path = row.root + ctx.path
|
|
46
|
+
else
|
|
47
|
+
for (const [k,v] of Object.entries(params))
|
|
48
|
+
if (k.startsWith('uri'))
|
|
49
|
+
params[k] = row.root + v
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
52
|
}
|
package/src/adminApis.js
CHANGED
|
@@ -167,11 +167,11 @@ for (const [k, was] of Object.entries(exports.adminApis))
|
|
|
167
167
|
: new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, props);
|
|
168
168
|
};
|
|
169
169
|
exports.localhostAdmin = (0, config_1.defineConfig)('localhost_admin', true);
|
|
170
|
-
exports.adminNet = (0, config_1.defineConfig)('admin_net', '');
|
|
171
|
-
exports.favicon = (0, config_1.defineConfig)('favicon');
|
|
170
|
+
exports.adminNet = (0, config_1.defineConfig)('admin_net', '', v => (0, misc_1.makeNetMatcher)(v, true));
|
|
171
|
+
exports.favicon = (0, config_1.defineConfig)('favicon', '');
|
|
172
172
|
exports.title = (0, config_1.defineConfig)('title', "File server");
|
|
173
173
|
function ctxAdminAccess(ctx) {
|
|
174
|
-
return !ctx.
|
|
174
|
+
return !ctx.ips.length // we consider localhost_admin only if no proxy is being usedø
|
|
175
175
|
&& exports.localhostAdmin.get() && (0, misc_1.isLocalHost)(ctx)
|
|
176
176
|
|| (0, perm_1.getFromAccount)(ctx.state.account, a => a.admin);
|
|
177
177
|
}
|
|
@@ -187,6 +187,6 @@ function anyAccountCanLoginAdmin() {
|
|
|
187
187
|
}
|
|
188
188
|
exports.anyAccountCanLoginAdmin = anyAccountCanLoginAdmin;
|
|
189
189
|
function allowAdmin(ctx) {
|
|
190
|
-
return
|
|
190
|
+
return exports.adminNet.compiled()(ctx.ip);
|
|
191
191
|
}
|
|
192
192
|
exports.allowAdmin = allowAdmin;
|
package/src/api.auth.js
CHANGED
|
@@ -11,8 +11,10 @@ const misc_1 = require("./misc");
|
|
|
11
11
|
const api_helpers_1 = require("./api.helpers");
|
|
12
12
|
const adminApis_1 = require("./adminApis");
|
|
13
13
|
const middlewares_1 = require("./middlewares");
|
|
14
|
+
const config_1 = require("./config");
|
|
14
15
|
const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters());
|
|
15
16
|
const ongoingLogins = {}; // store data that doesn't fit session object
|
|
17
|
+
const keepSessionAlive = (0, config_1.defineConfig)('keep_session_alive', true);
|
|
16
18
|
// centralized log-in state
|
|
17
19
|
async function loggedIn(ctx, username) {
|
|
18
20
|
const s = ctx.session;
|
|
@@ -29,7 +31,8 @@ async function loggedIn(ctx, username) {
|
|
|
29
31
|
ctx.cookies.set('csrf', (0, misc_1.randomId)(), { signed: false, httpOnly: false });
|
|
30
32
|
}
|
|
31
33
|
function makeExp() {
|
|
32
|
-
return
|
|
34
|
+
return !keepSessionAlive.get() ? undefined
|
|
35
|
+
: { exp: new Date(Date.now() + const_1.SESSION_DURATION) };
|
|
33
36
|
}
|
|
34
37
|
const login = async ({ username, password }, ctx) => {
|
|
35
38
|
if (!username || !password) // some validation
|
package/src/api.file_list.js
CHANGED
|
@@ -12,19 +12,18 @@ const plugins_1 = require("./plugins");
|
|
|
12
12
|
const misc_1 = require("./misc");
|
|
13
13
|
const lodash_1 = __importDefault(require("lodash"));
|
|
14
14
|
const const_1 = require("./const");
|
|
15
|
-
const file_list = async ({
|
|
16
|
-
|
|
15
|
+
const file_list = async ({ uri, offset, limit, search, omit, sse }, ctx) => {
|
|
16
|
+
const node = await (0, vfs_1.urlToNode)(uri || '/', ctx);
|
|
17
17
|
const list = new apiMiddleware_1.SendListReadable();
|
|
18
18
|
if (!node)
|
|
19
19
|
return fail(const_1.HTTP_NOT_FOUND);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return fail(res);
|
|
20
|
+
if ((0, vfs_1.statusCodeForMissingPerm)(node, 'can_list', ctx))
|
|
21
|
+
return fail();
|
|
23
22
|
if ((0, misc_1.dirTraversal)(search))
|
|
24
23
|
return fail(const_1.HTTP_FOOL);
|
|
25
24
|
if (node.default)
|
|
26
25
|
return (sse ? list.custom : lodash_1.default.identity)({
|
|
27
|
-
redirect:
|
|
26
|
+
redirect: uri // tell the browser to access the folder (instead of using this api), so it will get the default file
|
|
28
27
|
});
|
|
29
28
|
if (!await (0, vfs_1.nodeIsDirectory)(node))
|
|
30
29
|
return fail(const_1.HTTP_METHOD_NOT_ALLOWED);
|
|
@@ -46,7 +45,7 @@ const file_list = async ({ path, offset, limit, search, omit, sse }, ctx) => {
|
|
|
46
45
|
list.close();
|
|
47
46
|
});
|
|
48
47
|
return list;
|
|
49
|
-
function fail(code) {
|
|
48
|
+
function fail(code = ctx.status) {
|
|
50
49
|
if (!sse)
|
|
51
50
|
return new apiMiddleware_1.ApiError(code);
|
|
52
51
|
list.error(code, true);
|
|
@@ -58,10 +57,10 @@ const file_list = async ({ path, offset, limit, search, omit, sse }, ctx) => {
|
|
|
58
57
|
break;
|
|
59
58
|
if (!filter((0, vfs_1.getNodeName)(sub)))
|
|
60
59
|
continue;
|
|
61
|
-
const entry = await nodeToDirEntry(sub);
|
|
60
|
+
const entry = await nodeToDirEntry(ctx, sub);
|
|
62
61
|
if (!entry)
|
|
63
62
|
continue;
|
|
64
|
-
const cbParams = { entry, ctx,
|
|
63
|
+
const cbParams = { entry, ctx, listUri: uri, node: sub };
|
|
65
64
|
try {
|
|
66
65
|
if (onDirEntryHandlers.some(cb => cb(cbParams) === false))
|
|
67
66
|
continue;
|
|
@@ -87,7 +86,7 @@ const file_list = async ({ path, offset, limit, search, omit, sse }, ctx) => {
|
|
|
87
86
|
}
|
|
88
87
|
};
|
|
89
88
|
exports.file_list = file_list;
|
|
90
|
-
async function nodeToDirEntry(node) {
|
|
89
|
+
async function nodeToDirEntry(ctx, node) {
|
|
91
90
|
let { source, default: def } = node;
|
|
92
91
|
const name = (0, vfs_1.getNodeName)(node);
|
|
93
92
|
if (!source)
|
|
@@ -98,14 +97,27 @@ async function nodeToDirEntry(node) {
|
|
|
98
97
|
const st = await (0, promises_1.stat)(source);
|
|
99
98
|
const folder = st.isDirectory();
|
|
100
99
|
const { ctime, mtime } = st;
|
|
100
|
+
const pl = node.can_list === vfs_1.WHO_NO_ONE ? 'l'
|
|
101
|
+
: !(0, vfs_1.hasPermission)(node, 'can_list', ctx) ? 'L'
|
|
102
|
+
: '';
|
|
103
|
+
// no download here, but maybe inside?
|
|
104
|
+
const pr = node.can_read === vfs_1.WHO_NO_ONE && !(folder && filesInsideCould()) ? 'r'
|
|
105
|
+
: !(0, vfs_1.hasPermission)(node, 'can_read', ctx) ? 'R'
|
|
106
|
+
: '';
|
|
101
107
|
return {
|
|
102
108
|
n: name + (folder ? '/' : ''),
|
|
103
109
|
c: ctime,
|
|
104
110
|
m: Math.abs(+mtime - +ctime) < 1000 ? undefined : mtime,
|
|
105
111
|
s: folder ? undefined : st.size,
|
|
112
|
+
p: (pr + pl) || undefined
|
|
106
113
|
};
|
|
107
114
|
}
|
|
108
115
|
catch (_a) {
|
|
109
116
|
return null;
|
|
110
117
|
}
|
|
118
|
+
function filesInsideCould(n = node) {
|
|
119
|
+
var _a;
|
|
120
|
+
return (0, vfs_1.masksCouldGivePermission)(n.masks, 'can_read')
|
|
121
|
+
|| ((_a = n.children) === null || _a === void 0 ? void 0 : _a.some(c => c.can_read || filesInsideCould(c))); // we count on the boolean-compliant nature of the permission type here
|
|
122
|
+
}
|
|
111
123
|
}
|
package/src/api.lang.js
CHANGED
|
@@ -10,21 +10,23 @@ const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
|
10
10
|
const promises_1 = require("fs/promises");
|
|
11
11
|
const const_1 = require("./const");
|
|
12
12
|
const misc_1 = require("./misc");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const lang_1 = require("./lang");
|
|
14
|
+
const embedded_1 = __importDefault(require("./langs/embedded"));
|
|
15
15
|
const apis = {
|
|
16
16
|
list_langs() {
|
|
17
17
|
return new apiMiddleware_1.SendListReadable({
|
|
18
18
|
doAtStart: async (list) => {
|
|
19
|
-
for await (let name of fast_glob_1.default.stream(code2file('*'))) {
|
|
19
|
+
for await (let name of fast_glob_1.default.stream((0, lang_1.code2file)('*'))) {
|
|
20
20
|
name = String(name);
|
|
21
|
-
const code =
|
|
21
|
+
const code = (0, lang_1.file2code)(name);
|
|
22
22
|
try {
|
|
23
23
|
const data = JSON.parse(await (0, promises_1.readFile)(name, 'utf8'));
|
|
24
24
|
list.add({ code, ...lodash_1.default.omit(data, 'translate') });
|
|
25
25
|
}
|
|
26
26
|
catch (_a) { }
|
|
27
27
|
}
|
|
28
|
+
for (const [code, data] of Object.entries(embedded_1.default))
|
|
29
|
+
list.add({ code, embedded: true, ...lodash_1.default.omit(data, 'translate') });
|
|
28
30
|
list.close();
|
|
29
31
|
}
|
|
30
32
|
});
|
|
@@ -32,7 +34,7 @@ const apis = {
|
|
|
32
34
|
async del_lang({ code }) {
|
|
33
35
|
validateCode(code);
|
|
34
36
|
try {
|
|
35
|
-
await (0, promises_1.rm)(code2file(code));
|
|
37
|
+
await (0, promises_1.rm)((0, lang_1.code2file)(code));
|
|
36
38
|
return {};
|
|
37
39
|
}
|
|
38
40
|
catch (e) {
|
|
@@ -41,10 +43,9 @@ const apis = {
|
|
|
41
43
|
},
|
|
42
44
|
async add_langs({ langs }) {
|
|
43
45
|
for (let [code, content] of Object.entries(langs)) {
|
|
44
|
-
|
|
45
|
-
code = code.slice(PREFIX.length, -SUFFIX.length);
|
|
46
|
+
code = (0, lang_1.file2code)(code);
|
|
46
47
|
validateCode(code);
|
|
47
|
-
const fn = code2file(code);
|
|
48
|
+
const fn = (0, lang_1.code2file)(code);
|
|
48
49
|
const s = content = String(content);
|
|
49
50
|
if (!(0, misc_1.tryJson)(s))
|
|
50
51
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, "bad content for file " + fn);
|
|
@@ -54,9 +55,6 @@ const apis = {
|
|
|
54
55
|
}
|
|
55
56
|
};
|
|
56
57
|
exports.default = apis;
|
|
57
|
-
function code2file(code) {
|
|
58
|
-
return PREFIX + code.toLowerCase() + SUFFIX;
|
|
59
|
-
}
|
|
60
58
|
function validateCode(code) {
|
|
61
59
|
if (!/^(\w\w)(-\w\w)*$/.test(code))
|
|
62
60
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad code/filename');
|
package/src/api.vfs.js
CHANGED
|
@@ -20,37 +20,45 @@ async function urlToNodeOriginal(uri) {
|
|
|
20
20
|
const apis = {
|
|
21
21
|
async get_vfs() {
|
|
22
22
|
return {
|
|
23
|
-
root:
|
|
23
|
+
root: await recur(),
|
|
24
24
|
defaultPerms: vfs_1.defaultPerms,
|
|
25
25
|
};
|
|
26
|
-
async function recur(node) {
|
|
26
|
+
async function recur(node = vfs_1.vfs) {
|
|
27
27
|
var _a;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
28
|
+
const { source } = node;
|
|
29
|
+
const stats = Boolean(source) && await (0, promises_1.stat)(source).catch(() => false);
|
|
30
|
+
const isDir = !source || stats && stats.isDirectory();
|
|
30
31
|
const copyStats = stats ? lodash_1.default.pick(stats, ['size', 'ctime', 'mtime'])
|
|
31
|
-
: { size:
|
|
32
|
+
: { size: source ? -1 : undefined };
|
|
32
33
|
if (copyStats.mtime && Number(copyStats.mtime) === Number(copyStats.ctime))
|
|
33
34
|
delete copyStats.mtime;
|
|
34
|
-
|
|
35
|
+
let byMasks = node.original && lodash_1.default.pickBy(node, (v, k) => v !== node.original[k] // something is changing me...
|
|
36
|
+
&& v !== node.parent[k] // ...and it's not inheritance...
|
|
37
|
+
&& vfs_1.PERM_KEYS.includes(k)); // ...must be masks. Please limit this to perms
|
|
38
|
+
if (lodash_1.default.isEmpty(byMasks))
|
|
39
|
+
byMasks = undefined;
|
|
35
40
|
return {
|
|
36
41
|
...copyStats,
|
|
37
|
-
...node,
|
|
42
|
+
...node.original || node,
|
|
43
|
+
byMasks,
|
|
38
44
|
website: Boolean((_a = node.children) === null || _a === void 0 ? void 0 : _a.find((0, vfs_1.isSameFilenameAs)('index.html')))
|
|
39
|
-
|| isDir &&
|
|
45
|
+
|| isDir && source && await (0, promises_1.stat)((0, path_1.join)(source, 'index.html')).then(() => true, () => undefined)
|
|
40
46
|
|| undefined,
|
|
41
|
-
name:
|
|
47
|
+
name: node === vfs_1.vfs ? undefined : (0, vfs_1.getNodeName)(node),
|
|
42
48
|
type: isDir ? 'folder' : undefined,
|
|
43
|
-
children: node.children && await Promise.all(node.children.map(recur))
|
|
49
|
+
children: node.children && await Promise.all(node.children.map(async (original) => recur((0, vfs_1.applyParentToChild)(original, node))))
|
|
44
50
|
};
|
|
45
51
|
}
|
|
46
52
|
},
|
|
47
53
|
async move_vfs({ from, parent }) {
|
|
48
54
|
var _a;
|
|
49
|
-
if (from
|
|
55
|
+
if (!from || !parent)
|
|
50
56
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
|
|
51
57
|
const fromNode = await urlToNodeOriginal(from);
|
|
52
58
|
if (!fromNode)
|
|
53
59
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'from not found');
|
|
60
|
+
if (fromNode === vfs_1.vfs)
|
|
61
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'from is root');
|
|
54
62
|
const parentNode = await urlToNodeOriginal(parent);
|
|
55
63
|
if (!parentNode)
|
|
56
64
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'parent not found');
|
|
@@ -70,39 +78,39 @@ const apis = {
|
|
|
70
78
|
const n = await urlToNodeOriginal(uri);
|
|
71
79
|
if (!n)
|
|
72
80
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'path not found');
|
|
73
|
-
props = pickProps(props, ['name', 'source', 'masks', 'default', 'accept', ...
|
|
81
|
+
props = pickProps(props, ['name', 'source', 'masks', 'default', 'accept', ...vfs_1.PERM_KEYS]); // sanitize
|
|
74
82
|
if (props.name && props.name !== (0, vfs_1.getNodeName)(n)) {
|
|
75
83
|
const parent = await urlToNodeOriginal((0, path_1.dirname)(uri));
|
|
76
84
|
if ((_a = parent === null || parent === void 0 ? void 0 : parent.children) === null || _a === void 0 ? void 0 : _a.find(x => (0, vfs_1.getNodeName)(x) === props.name))
|
|
77
85
|
return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT, 'name already present');
|
|
78
86
|
}
|
|
79
|
-
props = (0, misc_1.newObj)(props, v => v === null ? undefined : v); // null is a way to serialize undefined, that will restore default values
|
|
80
87
|
if (props.masks && typeof props.masks !== 'object')
|
|
81
88
|
delete props.masks;
|
|
82
89
|
Object.assign(n, props);
|
|
83
|
-
|
|
84
|
-
n.name = undefined;
|
|
90
|
+
simplifyName(n);
|
|
85
91
|
await (0, vfs_1.saveVfs)();
|
|
86
92
|
return n;
|
|
87
93
|
},
|
|
88
94
|
async add_vfs({ parent, source, name }) {
|
|
89
95
|
var _a;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
return new apiMiddleware_1.ApiError(const_1.
|
|
96
|
+
if (!source && !name)
|
|
97
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'name or source required');
|
|
98
|
+
parent = parent ? await urlToNodeOriginal(parent) : vfs_1.vfs;
|
|
99
|
+
if (!parent)
|
|
100
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'parent not found');
|
|
101
|
+
if (!await (0, vfs_1.nodeIsDirectory)(parent))
|
|
102
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, 'parent not a folder');
|
|
95
103
|
if ((0, misc_1.isWindowsDrive)(source))
|
|
96
104
|
source += '\\'; // slash must be included, otherwise it will refer to the cwd of that drive
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
105
|
+
const child = { source, name };
|
|
106
|
+
name = (0, vfs_1.getNodeName)(child); // could be not given as input
|
|
107
|
+
const ext = (0, path_1.extname)(name);
|
|
108
|
+
const noExt = ext ? name.slice(0, -ext.length) : name;
|
|
100
109
|
let idx = 2;
|
|
101
|
-
while ((_a =
|
|
102
|
-
|
|
103
|
-
name =
|
|
104
|
-
|
|
105
|
-
n.children.unshift({ source, name });
|
|
110
|
+
while ((_a = parent.children) === null || _a === void 0 ? void 0 : _a.find((0, vfs_1.isSameFilenameAs)(name)))
|
|
111
|
+
name = `${noExt} ${idx++}${ext}`;
|
|
112
|
+
child.name = name;
|
|
113
|
+
(parent.children || (parent.children = [])).unshift({ source, name });
|
|
106
114
|
await (0, vfs_1.saveVfs)();
|
|
107
115
|
return { name };
|
|
108
116
|
},
|
|
@@ -154,13 +162,14 @@ const apis = {
|
|
|
154
162
|
return;
|
|
155
163
|
}
|
|
156
164
|
try {
|
|
165
|
+
const matching = (0, misc_1.makeMatcher)(fileMask);
|
|
157
166
|
path = (0, misc_1.isWindowsDrive)(path) ? path + '\\' : (0, path_1.resolve)(path || '/');
|
|
158
167
|
for await (const [name, isDir] of (0, misc_1.dirStream)(path)) {
|
|
159
168
|
if (ctx.req.aborted)
|
|
160
169
|
return;
|
|
161
170
|
try {
|
|
162
171
|
if (!isDir)
|
|
163
|
-
if (!files || fileMask && !(
|
|
172
|
+
if (!files || fileMask && !matching(name))
|
|
164
173
|
continue;
|
|
165
174
|
const stats = await (0, promises_1.stat)((0, path_1.join)(path, name));
|
|
166
175
|
yield {
|
|
@@ -191,3 +200,8 @@ function pickProps(o, keys) {
|
|
|
191
200
|
ret[k] = o[k] === null || o[k] === '' ? undefined : o[k];
|
|
192
201
|
return ret;
|
|
193
202
|
}
|
|
203
|
+
function simplifyName(node) {
|
|
204
|
+
const { name, ...noName } = node;
|
|
205
|
+
if ((0, vfs_1.getNodeName)(noName) === name)
|
|
206
|
+
delete node.name;
|
|
207
|
+
}
|
package/src/apiMiddleware.js
CHANGED
|
@@ -31,8 +31,9 @@ function apiMiddleware(apis) {
|
|
|
31
31
|
// we don't rely on SameSite cookie option because it's https-only
|
|
32
32
|
let res;
|
|
33
33
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
for (const [k, v] of Object.entries(params))
|
|
35
|
+
if (k.startsWith('uri') && typeof v === 'string')
|
|
36
|
+
params[k] = (0, misc_1.removeStarting)(ctx.state.revProxyPath, v);
|
|
36
37
|
res = csrf && csrf !== params.csrf ? new ApiError(const_1.HTTP_UNAUTHORIZED, 'csrf')
|
|
37
38
|
: await apiFun(params || {}, ctx);
|
|
38
39
|
}
|
package/src/block.js
CHANGED
|
@@ -1,34 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
4
|
exports.applyBlock = void 0;
|
|
8
5
|
const config_1 = require("./config");
|
|
9
6
|
const connections_1 = require("./connections");
|
|
10
7
|
const misc_1 = require("./misc");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
compileBlock(rules);
|
|
8
|
+
const block = (0, config_1.defineConfig)('block', [], rules => {
|
|
9
|
+
const ret = !Array.isArray(rules) ? []
|
|
10
|
+
: (0, misc_1.onlyTruthy)(rules.map(rule => (0, misc_1.makeNetMatcher)(rule.ip, true)));
|
|
11
|
+
// reapply new block to existing connections
|
|
16
12
|
for (const { socket, ip } of (0, connections_1.getConnections)())
|
|
17
13
|
applyBlock(socket, ip);
|
|
14
|
+
return ret;
|
|
18
15
|
});
|
|
19
|
-
function compileBlock(rules) {
|
|
20
|
-
blockFunctions = !Array.isArray(rules) ? []
|
|
21
|
-
: (0, misc_1.onlyTruthy)(rules.map(rule => !rule ? null
|
|
22
|
-
: (0, misc_1.with_)(rule.ip, ip => typeof ip !== 'string' ? null
|
|
23
|
-
: ip.includes('/') ? x => cidr_tools_1.default.contains(ip, x)
|
|
24
|
-
: ip.includes('*') ? (0, misc_1.with_)(ipMask2regExp(ip), re => x => re.test(x))
|
|
25
|
-
: x => x === ip)));
|
|
26
|
-
function ipMask2regExp(ipMask) {
|
|
27
|
-
return new RegExp(lodash_1.default.escapeRegExp(ipMask).replace(/\\\*/g, '.*'));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
16
|
function applyBlock(socket, ip = (0, connections_1.normalizeIp)(socket.remoteAddress || '')) {
|
|
31
|
-
if (ip &&
|
|
17
|
+
if (ip && block.compiled().find(rule => rule(ip)))
|
|
32
18
|
return socket.destroy();
|
|
33
19
|
}
|
|
34
20
|
exports.applyBlock = applyBlock;
|
package/src/config.js
CHANGED
|
@@ -51,8 +51,11 @@ const { save } = (0, watchLoad_1.watchLoad)(path, values => setConfig(values ||
|
|
|
51
51
|
setConfig({}, false);
|
|
52
52
|
}
|
|
53
53
|
});
|
|
54
|
-
function defineConfig(k, defaultValue) {
|
|
54
|
+
function defineConfig(k, defaultValue, compiler = lodash_1.default.identity) {
|
|
55
55
|
configProps[k] = { defaultValue };
|
|
56
|
+
let compiled = compiler(defaultValue);
|
|
57
|
+
if (compiler)
|
|
58
|
+
subscribeConfig(k, (v) => compiled = compiler(v));
|
|
56
59
|
return {
|
|
57
60
|
key() {
|
|
58
61
|
return k;
|
|
@@ -68,7 +71,8 @@ function defineConfig(k, defaultValue) {
|
|
|
68
71
|
this.set(v(this.get()));
|
|
69
72
|
else
|
|
70
73
|
setConfig1(k, v);
|
|
71
|
-
}
|
|
74
|
+
},
|
|
75
|
+
compiled: () => compiled
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
78
|
exports.defineConfig = defineConfig;
|