hfs 0.48.2 → 0.49.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.
- package/README.md +3 -1
- package/admin/assets/{index-4e55b514.js → index-b34ac7b4.js} +86 -86
- package/{frontend/assets/sha512-70e9a0d9.js → admin/assets/sha512-ae917116.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/{index-e24651da.js → index-4cbc2ffc.js} +12 -12
- package/frontend/assets/index-cbe043ee.css +1 -0
- package/{admin/assets/sha512-2627feb7.js → frontend/assets/sha512-82ed8772.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +3 -3
- package/plugins/download-counter/plugin.js +14 -15
- package/src/adminApis.js +1 -0
- package/src/api.file_list.js +26 -2
- package/src/api.net.js +47 -23
- package/src/api.plugins.js +1 -1
- package/src/commands.js +1 -1
- package/src/config.js +1 -5
- package/src/const.js +1 -1
- package/src/cross.js +15 -2
- package/src/customHtml.js +1 -1
- package/src/debounceAsync.js +1 -0
- package/src/listen.js +23 -14
- package/src/misc.js +2 -1
- package/src/plugins.js +33 -30
- package/src/util-files.js +38 -2
- package/frontend/assets/index-cadcb0e9.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@charset "UTF-8";:root{height:100dvh;--bg: #fff;--text: #555;--ghost-contrast: #8882;--ghost-contrast-alt: #eee;--faint-contrast: #8884;--mild-contrast: #8886;--good-contrast: #000a;--button-bg: #6080aa;--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;--ghost-contrast-alt: #181818;--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}: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>div{max-width:54em;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],[type=range]),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=range]{width:calc(100% - 1px)}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}[class^=fa-]:before,[class*=" fa-"]:before{margin:0}.icon{font-size:1.2em;height:1.2em;width:1.4em;display:inline-block;text-align:center}img.file-icon{height:1em}.file-icon{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:var(--button-bg)}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 .1em;z-index:3}kbd{background-color:#eee;border-radius:3px;border:1px solid #b4b4b4;box-shadow:0 1px 1px #0003,0 2px #ffffffb3 inset;color:#333;display:inline-block;font-size:.85em;font-weight:700;line-height:1;padding:2px 4px;white-space:nowrap;margin-right:.5em}.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}.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 3px;height:1.8em}#filter-bar input[type=checkbox]{margin-top:.3em}#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-right:1em}@media (hover: none){ul.dir li .link-wrapper .popup-menu-button{display:none}}@media (hover: hover){ul.dir li .link-wrapper:not(:hover) .popup-menu-button{display:none}ul.dir li .link-wrapper:hover{padding:1em;margin:-1em}}ul.dir li .link-wrapper a:last-of-type{word-break:break-word;padding-right:.3em}ul.dir li .link-wrapper a .icon{margin-right:.5em;vertical-align:text-bottom;display:inline-block;text-align:center}ul.dir li .link-wrapper a:hover{text-decoration:underline}ul.dir li .entry-comment{display:inline}ul.dir li .entry-comment:before,ul.dir li .entry-comment:after{font-size:1.5em;font-family:serif;line-height:1px;position:relative}ul.dir li .entry-comment:before{content:open-quote;margin-left:.5em;margin-right:.1em;top:.2em}ul.dir li .entry-comment:after{content:"„";top:-.1em;margin-left:.1em}ul.dir li .entry-panel{float:right;padding-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>*:first-child{margin-left:0}#menu-bar>*:last-child{margin-right:0}#menu-bar button{padding:min(1vh,.5em) 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:.4em}.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)}.dialog-icon .icon{margin-left:-1px;font-size:95%;margin-top:.4em;border-radius:.6em 0}#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;white-space:nowrap}.popup-menu-button:hover{opacity:1}.file-dialog .dialog{min-width:13em}.file-dialog-properties{word-break:break-word;line-height:1.5em;margin:0}.file-dialog-properties dt{font-weight:700}.file-dialog-properties dd{margin-left:1.5em}.file-menu{margin-top:1em;padding-top:1em;border-top:1px solid var(--faint-contrast);display:flex;flex-direction:column;gap:1em}.file-menu a{display:flex;align-items:flex-start}.file-menu a label{margin-top:.1em}.file-menu a label small{display:block}.file-menu a .icon{margin-right:.5em}#root>.tiles-mode{max-width:none;margin:0 1em;--tiles-size: 5;--name-lines: 3;--name-height: calc(4.7em + var(--tiles-size) * .4em + 1.2em * var(--name-lines));--tile-width: calc(5em + 1em * var(--tiles-size))}#root>.tiles-mode ul.dir{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--tile-width),1fr));grid-auto-rows:calc(var(--name-height) + 1.3em);gap:0 20px}#root>.tiles-mode ul.dir li{text-align:center;position:relative;display:flex;flex-direction:column;border-bottom:none;overflow-x:clip;padding:.5em 0 0}#root>.tiles-mode ul.dir li .link-wrapper:not(:hover){max-height:var(--name-height);display:block;display:-webkit-box;overflow:hidden;-webkit-line-clamp:calc(var(--name-lines) + 1);-webkit-box-orient:vertical}#root>.tiles-mode ul.dir li .link-wrapper a:last-of-type{padding:0}#root>.tiles-mode ul.dir li .link-wrapper a span{display:block}#root>.tiles-mode ul.dir li .link-wrapper a .icon.icon{font-size:calc(1.2rem + .6rem * var(--tiles-size))}#root>.tiles-mode ul.dir li .link-wrapper a img.icon{width:auto;height:1em;padding:.1em 0}#root>.tiles-mode ul.dir li .link-wrapper a .icon{font-size:4rem;display:block;margin:auto}#root>.tiles-mode ul.dir li .link-wrapper:hover{overflow:visible;display:block;z-index:1}#root>.tiles-mode ul.dir li .link-wrapper:hover .icon:before{text-decoration:none}#root>.tiles-mode ul.dir li .link-wrapper:hover{padding:0;margin:0}#root>.tiles-mode ul.dir li:nth-of-type(odd){background-color:var(--bg)}#root>.tiles-mode ul.dir li .entry-panel{justify-content:center;font-size:10pt}#root>.tiles-mode ul.dir li .entry-details{font-size:80%}#root>.tiles-mode ul.dir li.page-separator:before{content:""}#root>.tiles-mode ul.dir li input[type=checkbox]{margin:0;position:absolute;top:.3em;right:1em}#root>.tiles-mode ul.dir li .link-wrapper a{display:inline}#root>.tiles-mode ul.dir li:hover{--bg: var(--ghost-contrast-alt);background:var(--bg)}#root>.tiles-mode ul.dir li:hover .link-wrapper,#root>.tiles-mode ul.dir li:hover .entry-panel{z-index:1;background:var(--bg)}#root>.tiles-mode ul.dir li:hover input[type=checkbox]{z-index:2}#root>.tiles-mode ul.dir li:hover .entry-panel{padding-bottom:.3em}#root>.tiles-mode .entry-size:after{content:none}#root>.tiles-mode .entry-ts{display:none}#root>.tiles-mode .popup-menu-button{position:absolute;top:0;left:0}#root>.tiles-mode #filter-bar{margin-bottom:1em}#root>.tiles-mode #paging{z-index:1}#root .file-show{--nav-size: min(25vh, 25vw)}#root .file-show>div{height:100%;width:100%}#root .file-show .showing-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center}#root .file-show .showing{max-width:calc(100% - var(--nav-size) * 2);max-height:100%}#root .file-show img.showing{max-width:100%}#root .file-show .main{flex:1;position:relative;max-height:100%;overflow:hidden}#root .file-show .freeY .main,#root .file-show .fullWidth .main{overflow-y:auto}#root .file-show .freeY .showing,#root .file-show .fullWidth .showing{max-height:initial;margin:auto}#root .file-show .freeY .showing-container,#root .file-show .fullWidth .showing-container{overflow:auto;align-items:flex-start}#root .file-show .fullWidth .showing-container,#root .file-show .fullWidth .showing{width:100%}#root .file-show .nav{position:absolute;margin:-.4em;font-size:var(--nav-size);cursor:pointer;opacity:.3;-webkit-text-stroke:2px black;user-select:none;transition:opacity .3s}#root .file-show .nav:hover{opacity:.7}#root .file-show .nav.nav-hidden{opacity:0}#root .file-show .bar{padding:.5em 1em;background:var(--bg);opacity:.8;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:.5em 1.5em}#root .file-show .bar .entry-details{font-size:smaller}#root .file-show .bar .entry-ts{display:inherit}#root .file-show .bar .entry-size:after{display:none}.file-show-help kbd{margin:.5em}.file-show-help kbd:first-of-type{margin-top:1em}@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}#root>.tiles-mode{margin:0}}@media (max-height: 600px){.file-dialog .dialog-content{display:flex;gap:3em;margin:1em}.file-dialog .dialog-content .file-menu{margin-top:0;padding-top:0;border-top:none;margin-left:2em;padding-left:2em;border-left:1px solid var(--faint-contrast)}}@media (pointer: coarse){#root>.tiles-mode .file-menu-button{font-size:1em;margin-top:.3em}}.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,2vh);padding-top:0;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-icon-text{display:flex;align-items:center;justify-content:center}.dialog-title{font-size:120%;margin:.3em 0;padding:0 .5em;min-height:1.2em}.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:1em}.dialog-type{left:0;top:0;overflow:hidden;opacity:.7}.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:1.5em}}.dialog-prompt label{display:block;margin:.5em .1em}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{g as OF,c as UF}from"./index-
|
|
1
|
+
import{g as OF,c as UF}from"./index-4cbc2ffc.js";function gF(sF,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 sF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(sF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(sF,Symbol.toStringTag,{value:"Module"}))}var dF={exports:{}};/*
|
|
2
2
|
* [js-sha512]{@link https://github.com/emn178/js-sha512}
|
|
3
3
|
*
|
|
4
4
|
* @version 0.8.0
|
package/frontend/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
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 type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-4cbc2ffc.js"></script>
|
|
9
|
+
<link rel="stylesheet" href="/assets/index-cbe043ee.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<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.49.0-alpha1",
|
|
4
4
|
"description": "HTTP File Server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file server",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"start-frontend": "npm run start --workspace=frontend",
|
|
17
17
|
"start-admin": "npm run start --workspace=admin",
|
|
18
18
|
"build-all": "npm audit --omit=dev && rm -rf dist && npm i && npm run build-server && npm run build-frontend && npm run build-admin && echo COMPLETED",
|
|
19
|
-
"build-server": "rm -rf dist/src dist/plugins && tsc --target es2018 && touch package.json && cp -v -r package.json README* LICENSE* plugins dist && find dist -name .DS_Store -delete && node afterbuild.js",
|
|
19
|
+
"build-server": "rm -rf dist/src dist/plugins && tsc --target es2018 && touch package.json && cp -v -r package.json central.json README* LICENSE* plugins dist && find dist -name .DS_Store -delete && node afterbuild.js",
|
|
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",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"limiter": "^2.1.0",
|
|
79
79
|
"lodash": "^4.17.21",
|
|
80
80
|
"minimist": "^1.2.6",
|
|
81
|
-
"nat-upnp": "
|
|
81
|
+
"nat-upnp-ts": "^2.0.1",
|
|
82
82
|
"node-html-parser": "^6.1.5",
|
|
83
83
|
"open": "^8.4.0",
|
|
84
84
|
"tssrp6a": "^3.0.0",
|
|
@@ -44,21 +44,20 @@ exports.init = async api => {
|
|
|
44
44
|
frontend_js: 'main.js',
|
|
45
45
|
frontend_css: 'style.css',
|
|
46
46
|
unload: () => save.flush(), // we may have pending savings
|
|
47
|
-
middleware: (
|
|
48
|
-
(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
ctx.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
onDirEntry: ({ entry, listUri }) => {
|
|
47
|
+
middleware: ctx => () => { // callback = execute after other middlewares are done
|
|
48
|
+
if (ctx.status >= 300 || ctx.state.download_counter_ignore || ctx.state.includesLastByte === false) return
|
|
49
|
+
if (!(ctx.vfsNode || api.getConfig('archives') && ctx.state.archive)) return
|
|
50
|
+
ctx.state.completed.then(() => {
|
|
51
|
+
const key = uri2key(ctx.path)
|
|
52
|
+
const entries = ctx.vfsNode ? [key]
|
|
53
|
+
: ctx.state.originalStream?.getArchiveEntries?.().filter(x => x.at(-1) !== '/').map(x => key + uri2key(x))
|
|
54
|
+
if (!entries) return
|
|
55
|
+
for (const k of entries)
|
|
56
|
+
counters[k] = counters[k] + 1 || 1
|
|
57
|
+
save()
|
|
58
|
+
})
|
|
59
|
+
},
|
|
60
|
+
onDirEntry({ entry, listUri }) {
|
|
62
61
|
const k = uri2key(listUri + entry.n)
|
|
63
62
|
const n = counters[k]
|
|
64
63
|
if (n)
|
package/src/adminApis.js
CHANGED
|
@@ -107,6 +107,7 @@ exports.adminApis = {
|
|
|
107
107
|
compatibleApiVersion: const_1.COMPATIBLE_API_VERSION,
|
|
108
108
|
...await (0, listen_1.getServerStatus)(),
|
|
109
109
|
urls: await (0, listen_1.getUrls)(),
|
|
110
|
+
ips: await (0, listen_1.getIps)(false),
|
|
110
111
|
baseUrl: middlewares_1.baseUrl.get(),
|
|
111
112
|
updatePossible: !(0, update_1.updateSupported)() ? false : await (0, update_1.localUpdateAvailable)() ? 'local' : true,
|
|
112
113
|
proxyDetected: (0, middlewares_1.getProxyDetected)(),
|
package/src/api.file_list.js
CHANGED
|
@@ -12,6 +12,10 @@ 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 config_1 = require("./config");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const DESCRIPT_ION = 'descript.ion';
|
|
18
|
+
const descriptIon = (0, config_1.defineConfig)('descript_ion', true);
|
|
15
19
|
const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
16
20
|
var _a;
|
|
17
21
|
const node = await (0, vfs_1.urlToNode)(uri || '/', ctx);
|
|
@@ -57,14 +61,19 @@ const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
|
57
61
|
for await (const sub of walker) {
|
|
58
62
|
if (ctx.aborted)
|
|
59
63
|
break;
|
|
60
|
-
|
|
64
|
+
const name = (0, vfs_1.getNodeName)(sub);
|
|
65
|
+
if (descriptIon.get() && name === DESCRIPT_ION)
|
|
66
|
+
continue;
|
|
67
|
+
if (!filter(name))
|
|
61
68
|
continue;
|
|
62
69
|
const entry = await nodeToDirEntry(ctx, sub);
|
|
63
70
|
if (!entry)
|
|
64
71
|
continue;
|
|
72
|
+
entry.comment = await getCommentFor(sub.source);
|
|
65
73
|
const cbParams = { entry, ctx, listUri: uri, node: sub };
|
|
66
74
|
try {
|
|
67
|
-
|
|
75
|
+
const res = await Promise.all(onDirEntryHandlers.map(cb => cb(cbParams)));
|
|
76
|
+
if (res.some(x => x === false))
|
|
68
77
|
continue;
|
|
69
78
|
}
|
|
70
79
|
catch (e) {
|
|
@@ -123,3 +132,18 @@ const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
|
123
132
|
}
|
|
124
133
|
};
|
|
125
134
|
exports.get_file_list = get_file_list;
|
|
135
|
+
async function getCommentFor(path) {
|
|
136
|
+
return !path || !descriptIon.get() ? undefined
|
|
137
|
+
: readDescription((0, path_1.dirname)(path)).then(x => x.get((0, misc_1.basename)(path)), () => undefined);
|
|
138
|
+
}
|
|
139
|
+
function readDescription(path) {
|
|
140
|
+
return (0, misc_1.parseFile)((0, path_1.join)(path, DESCRIPT_ION), txt => new Map(txt.split('\n').map(line => {
|
|
141
|
+
const quoted = line[0] === '"' ? 1 : 0;
|
|
142
|
+
const i = quoted ? line.indexOf('"', 2) : line.indexOf(' ');
|
|
143
|
+
const fn = line.slice(quoted, i - quoted);
|
|
144
|
+
let comment = line.slice(i + 1);
|
|
145
|
+
if (comment.endsWith('\x04\xc2'))
|
|
146
|
+
comment = comment.slice(0, -2).replaceAll('\\n', '\n');
|
|
147
|
+
return [fn, comment];
|
|
148
|
+
})));
|
|
149
|
+
}
|
package/src/api.net.js
CHANGED
|
@@ -4,9 +4,9 @@ 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.acmeMiddleware = exports.externalIp = void 0;
|
|
7
|
+
exports.makeCert = exports.acme_email = exports.acme_domain = exports.acmeMiddleware = exports.externalIp = void 0;
|
|
8
8
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
9
|
-
const
|
|
9
|
+
const nat_upnp_ts_1 = require("nat-upnp-ts");
|
|
10
10
|
const const_1 = require("./const");
|
|
11
11
|
const axios_1 = __importDefault(require("axios"));
|
|
12
12
|
const node_html_parser_1 = require("node-html-parser");
|
|
@@ -20,22 +20,24 @@ const acme_client_1 = __importDefault(require("acme-client"));
|
|
|
20
20
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
21
21
|
const http_1 = require("http");
|
|
22
22
|
const promises_2 = require("dns/promises");
|
|
23
|
-
const
|
|
24
|
-
const
|
|
23
|
+
const config_1 = require("./config");
|
|
24
|
+
const events_1 = __importDefault(require("./events"));
|
|
25
|
+
const upnpClient = new nat_upnp_ts_1.Client({ timeout: 4000 });
|
|
26
|
+
const originalMethod = upnpClient.getGateway;
|
|
25
27
|
// other client methods call getGateway too, so this will ensure they reuse this same result
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
upnpClient.getGateway = (0, misc_1.debounceAsync)(() => originalMethod.apply(upnpClient), 0, { retain: misc_1.HOUR, retainFailure: 30000 });
|
|
29
|
+
upnpClient.getGateway().catch(() => { });
|
|
28
30
|
exports.externalIp = Promise.resolve(''); // poll external ip
|
|
29
31
|
(0, misc_1.repeat)(10 * misc_1.MINUTE, () => {
|
|
30
32
|
const was = exports.externalIp;
|
|
31
|
-
exports.externalIp =
|
|
33
|
+
exports.externalIp = upnpClient.getPublicIp().catch(() => was); //fallback to previous value
|
|
32
34
|
});
|
|
33
35
|
const getNatInfo = (0, misc_1.debounceAsync)(async () => {
|
|
34
36
|
var _a, _b;
|
|
35
37
|
const gettingIp = getPublicIp(); // don't wait, do it in parallel
|
|
36
|
-
const res = await
|
|
38
|
+
const res = await upnpClient.getGateway().catch(() => null);
|
|
37
39
|
const status = await (0, listen_1.getServerStatus)();
|
|
38
|
-
const mappings = res && await (0, misc_1.haveTimeout)(5000,
|
|
40
|
+
const mappings = res && await (0, misc_1.haveTimeout)(5000, upnpClient.getMappings()).catch(() => null);
|
|
39
41
|
console.debug('mappings found', mappings);
|
|
40
42
|
const gatewayIp = res ? new URL(res.gateway.description).hostname : await findGateway().catch(() => null);
|
|
41
43
|
const localIp = (res === null || res === void 0 ? void 0 : res.address) || (await (0, listen_1.getIps)())[0];
|
|
@@ -129,19 +131,19 @@ async function generateSSLCert(domain, email) {
|
|
|
129
131
|
let check = await checkPort(domain, 80); // some check services may not consider the domain, but we already verified that
|
|
130
132
|
if (check && !check.success && upnp && externalPort !== 80) { // consider a short-lived mapping
|
|
131
133
|
// @ts-ignore
|
|
132
|
-
await
|
|
134
|
+
await upnpClient.createMapping({ private: 80, public: { host: '', port: 80 }, description: 'hfs temporary', ttl: 30 }).catch(() => { });
|
|
133
135
|
check = await checkPort(domain, 80); // repeat test
|
|
134
136
|
}
|
|
135
137
|
if (!check)
|
|
136
138
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "couldn't test port 80");
|
|
137
139
|
if (!check.success)
|
|
138
140
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "port 80 is not working on the specified domain");
|
|
139
|
-
const
|
|
141
|
+
const acmeClient = new acme_client_1.default.Client({
|
|
140
142
|
accountKey: await acme_client_1.default.crypto.createPrivateKey(),
|
|
141
143
|
directoryUrl: acme_client_1.default.directory.letsencrypt.production
|
|
142
144
|
});
|
|
143
145
|
const [key, csr] = await acme_client_1.default.crypto.createCsr({ commonName: domain });
|
|
144
|
-
const cert = await
|
|
146
|
+
const cert = await acmeClient.auto({
|
|
145
147
|
csr,
|
|
146
148
|
email,
|
|
147
149
|
challengePriority: ['http-01'],
|
|
@@ -203,13 +205,13 @@ const apis = {
|
|
|
203
205
|
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'no internal port');
|
|
204
206
|
if (externalPort)
|
|
205
207
|
try {
|
|
206
|
-
await
|
|
208
|
+
await upnpClient.removeMapping({ public: { host: '', port: externalPort } });
|
|
207
209
|
}
|
|
208
210
|
catch (e) {
|
|
209
211
|
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, 'removeMapping failed: ' + String(e));
|
|
210
212
|
}
|
|
211
213
|
if (external) // must use the object form of 'public' to work around a bug of the library
|
|
212
|
-
await
|
|
214
|
+
await upnpClient.createMapping({ private: internal || internalPort, public: { host: '', port: external }, description: 'hfs', ttl: 0 });
|
|
213
215
|
return {};
|
|
214
216
|
},
|
|
215
217
|
async check_server({ port }) {
|
|
@@ -224,19 +226,41 @@ const apis = {
|
|
|
224
226
|
|| new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
|
|
225
227
|
},
|
|
226
228
|
async make_cert({ domain, email }) {
|
|
227
|
-
|
|
228
|
-
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad params');
|
|
229
|
-
const res = await generateSSLCert(domain, email);
|
|
230
|
-
const CERT_FILE = 'acme.cert';
|
|
231
|
-
const KEY_FILE = 'acme.key';
|
|
232
|
-
await promises_1.default.writeFile(CERT_FILE, res.cert);
|
|
233
|
-
await promises_1.default.writeFile(KEY_FILE, res.key);
|
|
234
|
-
listen_1.cert.set(CERT_FILE); // update config
|
|
235
|
-
listen_1.privateKey.set(KEY_FILE);
|
|
229
|
+
await (0, exports.makeCert)(domain, email);
|
|
236
230
|
return {};
|
|
237
231
|
},
|
|
238
232
|
get_cert() {
|
|
239
233
|
return (0, misc_1.objSameKeys)(lodash_1.default.pick((0, listen_1.getCertObject)(), ['subject', 'issuer', 'validFrom', 'validTo']), v => v);
|
|
240
234
|
}
|
|
241
235
|
};
|
|
236
|
+
exports.acme_domain = (0, config_1.defineConfig)('acme_domain', '');
|
|
237
|
+
exports.acme_email = (0, config_1.defineConfig)('acme_email', '');
|
|
238
|
+
exports.makeCert = (0, misc_1.debounceAsync)(async (domain, email) => {
|
|
239
|
+
if (!domain)
|
|
240
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad params');
|
|
241
|
+
const res = await generateSSLCert(domain, email);
|
|
242
|
+
const CERT_FILE = 'acme.cert';
|
|
243
|
+
const KEY_FILE = 'acme.key';
|
|
244
|
+
await promises_1.default.writeFile(CERT_FILE, res.cert);
|
|
245
|
+
await promises_1.default.writeFile(KEY_FILE, res.key);
|
|
246
|
+
listen_1.cert.set(CERT_FILE); // update config
|
|
247
|
+
listen_1.privateKey.set(KEY_FILE);
|
|
248
|
+
}, 0);
|
|
249
|
+
(0, config_1.defineConfig)('acme_renew', false); // handle config changes
|
|
250
|
+
events_1.default.once('https ready', () => (0, misc_1.repeat)(misc_1.HOUR, renewCert));
|
|
251
|
+
// checks if the cert is near expiration date, and if so renews it
|
|
252
|
+
const renewCert = (0, misc_1.debounceAsync)(async () => {
|
|
253
|
+
const acmeLog = (...args) => console.log('[acme-renew]:', ...args);
|
|
254
|
+
const now = new Date();
|
|
255
|
+
const cert = (0, listen_1.getCertObject)();
|
|
256
|
+
if (!cert)
|
|
257
|
+
return;
|
|
258
|
+
const validTo = new Date(cert.validTo);
|
|
259
|
+
const isValid = now > new Date(cert.validFrom) && now < validTo &&
|
|
260
|
+
validTo.getTime() - now.getTime() >= 30 * misc_1.DAY; // it's not expiring in a month
|
|
261
|
+
if (isValid)
|
|
262
|
+
return acmeLog("certificate is good");
|
|
263
|
+
await (0, exports.makeCert)(exports.acme_domain.get(), exports.acme_email.get())
|
|
264
|
+
.catch(e => acmeLog("error: ", e.toString()));
|
|
265
|
+
}, 0, { retain: misc_1.DAY, retainFailure: misc_1.HOUR });
|
|
242
266
|
exports.default = apis;
|
package/src/api.plugins.js
CHANGED
|
@@ -15,7 +15,7 @@ const github_1 = require("./github");
|
|
|
15
15
|
const const_1 = require("./const");
|
|
16
16
|
const apis = {
|
|
17
17
|
get_plugins({}, ctx) {
|
|
18
|
-
const list = new apiMiddleware_1.SendListReadable({ addAtStart: [...(0, plugins_1.mapPlugins)(serialize), ...(0, plugins_1.getAvailablePlugins)().map(serialize)] });
|
|
18
|
+
const list = new apiMiddleware_1.SendListReadable({ addAtStart: [...(0, plugins_1.mapPlugins)(serialize, false), ...(0, plugins_1.getAvailablePlugins)().map(serialize)] });
|
|
19
19
|
return list.events(ctx, {
|
|
20
20
|
pluginInstalled: p => list.add(serialize(p)),
|
|
21
21
|
'pluginStarted pluginStopped pluginUpdated': p => {
|
package/src/commands.js
CHANGED
package/src/config.js
CHANGED
|
@@ -89,11 +89,7 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
89
89
|
else
|
|
90
90
|
setConfig1(k, v);
|
|
91
91
|
},
|
|
92
|
-
compiled: () =>
|
|
93
|
-
if (!compiler)
|
|
94
|
-
throw "missing compiler";
|
|
95
|
-
return compiled;
|
|
96
|
-
}
|
|
92
|
+
compiled: () => compiled !== null && compiled !== void 0 ? compiled : (0, misc_1.throw_)("missing compiler"),
|
|
97
93
|
};
|
|
98
94
|
if (compiler)
|
|
99
95
|
ret.sub((...args) => compiled = compiler(...args));
|
package/src/const.js
CHANGED
|
@@ -38,7 +38,7 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
|
|
|
38
38
|
exports.ORIGINAL_CWD = process.cwd();
|
|
39
39
|
exports.HFS_STARTED = new Date();
|
|
40
40
|
const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
|
|
41
|
-
exports.BUILD_TIMESTAMP = "2023-09-
|
|
41
|
+
exports.BUILD_TIMESTAMP = "2023-09-24T12:10:10.944Z";
|
|
42
42
|
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
43
43
|
exports.VERSION = pkg.version;
|
|
44
44
|
exports.RUNNING_BETA = exports.VERSION.includes('-');
|
package/src/cross.js
CHANGED
|
@@ -3,7 +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
|
-
exports.xlate = exports.isEqualLax = exports.isWindowsDrive = exports.isIP = exports.isPrimitive = exports.formatTimestamp = exports.repeat = exports.asyncGeneratorToArray = exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = exports.objRenameKey = exports.randomId = exports.getOrSet = exports.waitFor = exports.newObj = exports.removeStarting = exports.findDefined = exports.isOrderedEqual = exports.swap = exports.tryJson = exports.basename = exports.pendingPromise = exports._log = exports.wantArray = exports.formatPerc = exports.with_ = exports.try_ = exports.setHidden = exports.onlyTruthy = exports.truthy = exports.enforceFinal = exports.objSameKeys = exports.haveTimeout = exports.wait = exports.prefix = exports.formatBytes = exports.MAX_TILES_SIZE = exports.DAY = exports.HOUR = exports.MINUTE = exports.WIKI_URL = exports.REPO_URL = void 0;
|
|
6
|
+
exports.ipForUrl = exports.ipLocalHost = exports.xlate = exports.isEqualLax = exports.isWindowsDrive = exports.isIP = exports.isPrimitive = exports.formatTimestamp = exports.repeat = exports.asyncGeneratorToArray = exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = exports.objRenameKey = exports.randomId = exports.getOrSet = exports.waitFor = exports.newObj = exports.removeStarting = exports.findDefined = exports.isOrderedEqual = exports.swap = exports.tryJson = exports.basename = exports.pendingPromise = exports._dbg = exports._log = exports.wantArray = exports.formatPerc = exports.with_ = exports.try_ = exports.setHidden = exports.onlyTruthy = exports.truthy = exports.enforceFinal = exports.objSameKeys = exports.haveTimeout = exports.wait = exports.prefix = exports.formatBytes = exports.MAX_TILES_SIZE = exports.DAY = exports.HOUR = exports.MINUTE = exports.WIKI_URL = exports.REPO_URL = void 0;
|
|
7
7
|
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
8
8
|
// all content here is shared between client and server
|
|
9
9
|
const lodash_1 = __importDefault(require("lodash"));
|
|
@@ -86,6 +86,11 @@ function _log(...args) {
|
|
|
86
86
|
return args[args.length - 1];
|
|
87
87
|
}
|
|
88
88
|
exports._log = _log;
|
|
89
|
+
function _dbg(x) {
|
|
90
|
+
debugger;
|
|
91
|
+
return x;
|
|
92
|
+
}
|
|
93
|
+
exports._dbg = _dbg;
|
|
89
94
|
function pendingPromise() {
|
|
90
95
|
let takeOut;
|
|
91
96
|
const ret = new Promise((resolve, reject) => takeOut = { resolve, reject });
|
|
@@ -225,7 +230,7 @@ async function asyncGeneratorToArray(generator) {
|
|
|
225
230
|
}
|
|
226
231
|
exports.asyncGeneratorToArray = asyncGeneratorToArray;
|
|
227
232
|
function repeat(every, cb) {
|
|
228
|
-
Promise.allSettled([cb()]).then(() => setTimeout(() => repeat(every, cb), every));
|
|
233
|
+
return Promise.allSettled([cb()]).then(() => setTimeout(() => repeat(every, cb), every));
|
|
229
234
|
}
|
|
230
235
|
exports.repeat = repeat;
|
|
231
236
|
function formatTimestamp(x) {
|
|
@@ -255,3 +260,11 @@ function xlate(input, table) {
|
|
|
255
260
|
return (_a = table[input]) !== null && _a !== void 0 ? _a : input;
|
|
256
261
|
}
|
|
257
262
|
exports.xlate = xlate;
|
|
263
|
+
function ipLocalHost(ip) {
|
|
264
|
+
return ip === '::1' || ip.endsWith('127.0.0.1');
|
|
265
|
+
}
|
|
266
|
+
exports.ipLocalHost = ipLocalHost;
|
|
267
|
+
function ipForUrl(ip) {
|
|
268
|
+
return ip.includes(':') ? '[' + ip + ']' : ip;
|
|
269
|
+
}
|
|
270
|
+
exports.ipForUrl = ipForUrl;
|
package/src/customHtml.js
CHANGED
|
@@ -35,7 +35,7 @@ function watchLoadCustomHtml(folder = '') {
|
|
|
35
35
|
exports.watchLoadCustomHtml = watchLoadCustomHtml;
|
|
36
36
|
function getSection(name) {
|
|
37
37
|
return (exports.customHtmlState.sections.get(name) || '')
|
|
38
|
-
+ (0, plugins_1.mapPlugins)(pl => pl.getData().customHtml.get(name)).join('\n');
|
|
38
|
+
+ (0, plugins_1.mapPlugins)(pl => { var _a; return (_a = pl.getData().customHtml) === null || _a === void 0 ? void 0 : _a.get(name); }).join('\n');
|
|
39
39
|
}
|
|
40
40
|
exports.getSection = getSection;
|
|
41
41
|
async function saveCustomHtml(sections) {
|
package/src/debounceAsync.js
CHANGED
|
@@ -14,6 +14,7 @@ function debounceAsync(callback, wait = 100, options = {}) {
|
|
|
14
14
|
let lastSince = 0;
|
|
15
15
|
const interceptingWrapper = (...args) => runningDebouncer = debouncer(...args);
|
|
16
16
|
return Object.assign(interceptingWrapper, {
|
|
17
|
+
clearRetain: () => last = undefined,
|
|
17
18
|
flush: () => runningCallback !== null && runningCallback !== void 0 ? runningCallback : exec(),
|
|
18
19
|
...cancelable && {
|
|
19
20
|
cancel() {
|
package/src/listen.js
CHANGED
|
@@ -43,6 +43,7 @@ const adminApis_1 = require("./adminApis");
|
|
|
43
43
|
const lodash_1 = __importDefault(require("lodash"));
|
|
44
44
|
const crypto_1 = require("crypto");
|
|
45
45
|
const api_net_1 = require("./api.net");
|
|
46
|
+
const events_1 = __importDefault(require("./events"));
|
|
46
47
|
let httpSrv;
|
|
47
48
|
let httpsSrv;
|
|
48
49
|
const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', !const_1.DEV);
|
|
@@ -52,13 +53,11 @@ function getHttpsWorkingPort() {
|
|
|
52
53
|
}
|
|
53
54
|
exports.getHttpsWorkingPort = getHttpsWorkingPort;
|
|
54
55
|
const commonOptions = { requestTimeout: 0 };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
while (!index_1.app)
|
|
58
|
-
await (0, misc_1.wait)(100);
|
|
56
|
+
const considerHttp = (0, misc_1.debounceAsync)(async () => {
|
|
57
|
+
await (0, misc_1.waitFor)(() => index_1.app);
|
|
59
58
|
stopServer(httpSrv).then();
|
|
60
59
|
httpSrv = Object.assign(http.createServer(commonOptions, index_1.app.callback()), { name: 'http' });
|
|
61
|
-
port = await startServer(httpSrv, { port });
|
|
60
|
+
const port = await startServer(httpSrv, { port: exports.portCfg.get(), host: listenInterface.get() });
|
|
62
61
|
if (!port)
|
|
63
62
|
return;
|
|
64
63
|
httpSrv.on('connection', connections_1.newConnection);
|
|
@@ -66,6 +65,10 @@ exports.portCfg.sub(async (port) => {
|
|
|
66
65
|
if (openBrowserAtStart.get() && !const_1.argv.updated)
|
|
67
66
|
openAdmin();
|
|
68
67
|
});
|
|
68
|
+
exports.portCfg = (0, config_1.defineConfig)('port', 80);
|
|
69
|
+
const listenInterface = (0, config_1.defineConfig)('listen_interface', '');
|
|
70
|
+
exports.portCfg.sub(considerHttp);
|
|
71
|
+
listenInterface.sub(considerHttp);
|
|
69
72
|
function openAdmin() {
|
|
70
73
|
for (const srv of [httpSrv, httpsSrv]) {
|
|
71
74
|
const a = srv === null || srv === void 0 ? void 0 : srv.address();
|
|
@@ -84,6 +87,8 @@ function openAdmin() {
|
|
|
84
87
|
}
|
|
85
88
|
exports.openAdmin = openAdmin;
|
|
86
89
|
function getCertObject() {
|
|
90
|
+
if (!httpsOptions.cert)
|
|
91
|
+
return;
|
|
87
92
|
const o = new crypto_1.X509Certificate(httpsOptions.cert);
|
|
88
93
|
const some = lodash_1.default.pick(o, ['subject', 'issuer', 'validFrom', 'validTo']);
|
|
89
94
|
return (0, misc_1.objSameKeys)(some, v => (v === null || v === void 0 ? void 0 : v.includes('=')) ? Object.fromEntries(v.split('\n').map(x => x.split('='))) : v);
|
|
@@ -99,6 +104,8 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
|
99
104
|
httpsSrv = Object.assign(https.createServer(port === PORT_DISABLED ? {} : { ...commonOptions, key: httpsOptions.private_key, cert: httpsOptions.cert }, index_1.app.callback()), { name: 'https' });
|
|
100
105
|
if (port >= 0) {
|
|
101
106
|
const cert = getCertObject();
|
|
107
|
+
if (!cert)
|
|
108
|
+
return;
|
|
102
109
|
const cn = (_a = cert.subject) === null || _a === void 0 ? void 0 : _a.CN;
|
|
103
110
|
if (cn)
|
|
104
111
|
console.log("certificate loaded for", cn);
|
|
@@ -120,11 +127,12 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
|
120
127
|
console.log("failed to create https server: check your private key and certificate", String(e));
|
|
121
128
|
return;
|
|
122
129
|
}
|
|
123
|
-
port = await startServer(httpsSrv, { port });
|
|
130
|
+
port = await startServer(httpsSrv, { port, host: listenInterface.get() });
|
|
124
131
|
if (!port)
|
|
125
132
|
return;
|
|
126
133
|
httpsSrv.on('connection', connections_1.newConnection);
|
|
127
134
|
printUrls(httpsSrv.name);
|
|
135
|
+
events_1.default.emit('https ready');
|
|
128
136
|
});
|
|
129
137
|
exports.cert = (0, config_1.defineConfig)('cert', '');
|
|
130
138
|
exports.privateKey = (0, config_1.defineConfig)('private_key', '');
|
|
@@ -150,6 +158,7 @@ for (const cfg of httpsNeeds) {
|
|
|
150
158
|
const PORT_DISABLED = -1;
|
|
151
159
|
exports.httpsPortCfg = (0, config_1.defineConfig)('https_port', PORT_DISABLED);
|
|
152
160
|
exports.httpsPortCfg.sub(considerHttps);
|
|
161
|
+
listenInterface.sub(considerHttps);
|
|
153
162
|
function startServer(srv, { port, host }) {
|
|
154
163
|
return new Promise(async (resolve) => {
|
|
155
164
|
if (!srv)
|
|
@@ -194,6 +203,8 @@ function startServer(srv, { port, host }) {
|
|
|
194
203
|
srv.error = String(e);
|
|
195
204
|
srv.busy = undefined;
|
|
196
205
|
const { code } = e;
|
|
206
|
+
if (code === 'EACCES' && port < 1024)
|
|
207
|
+
srv.error = `lacking permission on port ${port}, try with permission (${const_1.IS_WINDOWS ? 'administrator' : 'sudo'}) or port > 1024`;
|
|
197
208
|
if (code === 'EADDRINUSE') {
|
|
198
209
|
srv.busy = (0, find_process_1.default)('port', port).then(res => { var _a; return ((_a = res === null || res === void 0 ? void 0 : res[0]) === null || _a === void 0 ? void 0 : _a.name) || ''; }, () => '');
|
|
199
210
|
srv.error = `port ${port} busy: ${await srv.busy || "unknown process"}`;
|
|
@@ -242,11 +253,11 @@ async function getServerStatus() {
|
|
|
242
253
|
}
|
|
243
254
|
exports.getServerStatus = getServerStatus;
|
|
244
255
|
const ignore = /^(lo|.*loopback.*|virtualbox.*|.*\(wsl\).*|llw\d|awdl\d|utun\d|anpi\d)$/i; // avoid giving too much information
|
|
245
|
-
async function getIps() {
|
|
256
|
+
async function getIps(external = true) {
|
|
246
257
|
const ips = (0, misc_1.onlyTruthy)(Object.entries((0, os_1.networkInterfaces)()).map(([name, nets]) => nets && !ignore.test(name)
|
|
247
258
|
&& v4first((0, misc_1.onlyTruthy)(nets.map(net => !net.internal && net.address)))[0] // for each interface we consider only 1 address
|
|
248
259
|
)).flat();
|
|
249
|
-
const e = await api_net_1.externalIp;
|
|
260
|
+
const e = external && await api_net_1.externalIp;
|
|
250
261
|
if (e && !ips.includes(e))
|
|
251
262
|
ips.unshift(e);
|
|
252
263
|
return v4first(ips)
|
|
@@ -257,21 +268,19 @@ async function getIps() {
|
|
|
257
268
|
}
|
|
258
269
|
exports.getIps = getIps;
|
|
259
270
|
async function getUrls() {
|
|
260
|
-
const
|
|
271
|
+
const on = listenInterface.get();
|
|
272
|
+
const ips = on ? [on] : await getIps();
|
|
261
273
|
return Object.fromEntries((0, misc_1.onlyTruthy)([httpSrv, httpsSrv].map(srv => {
|
|
262
274
|
var _a;
|
|
263
275
|
if (!(srv === null || srv === void 0 ? void 0 : srv.listening))
|
|
264
276
|
return false;
|
|
265
277
|
const port = (_a = srv === null || srv === void 0 ? void 0 : srv.address()) === null || _a === void 0 ? void 0 : _a.port;
|
|
266
278
|
const appendPort = port === (srv.name === 'https' ? 443 : 80) ? '' : ':' + port;
|
|
267
|
-
const urls = ips.map(ip => `${srv.name}://${ip}${appendPort}`);
|
|
279
|
+
const urls = ips.map(ip => `${srv.name}://${(0, misc_1.ipForUrl)(ip)}${appendPort}`);
|
|
268
280
|
return urls.length && [srv.name, urls];
|
|
269
281
|
})));
|
|
270
282
|
}
|
|
271
283
|
exports.getUrls = getUrls;
|
|
272
284
|
function printUrls(srvName) {
|
|
273
|
-
getUrls().then(urls =>
|
|
274
|
-
for (const url of urls[srvName])
|
|
275
|
-
console.log('serving on', url);
|
|
276
|
-
});
|
|
285
|
+
getUrls().then(urls => lodash_1.default.each(urls[srvName], url => console.log('serving on', url)));
|
|
277
286
|
}
|
package/src/misc.js
CHANGED
|
@@ -32,6 +32,7 @@ const debounceAsync_1 = __importDefault(require("./debounceAsync"));
|
|
|
32
32
|
exports.debounceAsync = debounceAsync_1.default;
|
|
33
33
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
34
34
|
const const_1 = require("./const");
|
|
35
|
+
const cross_1 = require("./cross");
|
|
35
36
|
const cbs = new Set();
|
|
36
37
|
function onProcessExit(cb) {
|
|
37
38
|
cbs.add(cb);
|
|
@@ -70,7 +71,7 @@ function onOff(em, events) {
|
|
|
70
71
|
exports.onOff = onOff;
|
|
71
72
|
function isLocalHost(c) {
|
|
72
73
|
const ip = typeof c === 'string' ? c : c.socket.remoteAddress; // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
|
|
73
|
-
return ip && (
|
|
74
|
+
return ip && (0, cross_1.ipLocalHost)(ip);
|
|
74
75
|
}
|
|
75
76
|
exports.isLocalHost = isLocalHost;
|
|
76
77
|
function makeNetMatcher(mask, emptyMaskReturns = false) {
|