hfs 0.48.3 → 0.49.0-alpha2
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 +4 -2
- package/admin/assets/index-6afd4b06.js +542 -0
- package/{frontend/assets/sha512-70e9a0d9.js → admin/assets/sha512-f5f12dc1.js} +1 -1
- package/admin/index.html +1 -1
- package/central.json +30 -8
- package/frontend/assets/index-3a379840.js +94 -0
- package/frontend/assets/index-db5f0e4b.css +1 -0
- package/{admin/assets/sha512-2627feb7.js → frontend/assets/sha512-3392c31f.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +1 -2
- package/plugins/download-counter/plugin.js +14 -15
- package/src/adminApis.js +1 -0
- package/src/api.auth.js +6 -6
- package/src/api.file_list.js +15 -8
- package/src/api.net.js +97 -72
- package/src/api.plugins.js +15 -26
- package/src/api.vfs.js +9 -12
- package/src/block.js +14 -1
- package/src/commands.js +1 -1
- package/src/comments.js +49 -0
- package/src/config.js +1 -5
- package/src/const.js +3 -2
- package/src/cross.js +43 -2
- package/src/customHtml.js +1 -1
- package/src/debounceAsync.js +18 -1
- package/src/frontEndApis.js +28 -6
- package/src/github.js +24 -30
- package/src/langs/hfs-lang-it.json +5 -3
- package/src/langs/hfs-lang-ru.json +6 -4
- package/src/listen.js +25 -15
- package/src/middlewares.js +3 -1
- package/src/misc.js +6 -5
- package/src/perm.js +19 -12
- package/src/plugins.js +62 -38
- package/src/serveFile.js +2 -3
- package/src/upload.js +5 -1
- package/src/util-files.js +38 -2
- package/src/util-http.js +8 -6
- package/src/vfs.js +24 -32
- package/src/watchLoad.js +5 -12
- package/src/zip.js +4 -4
- package/admin/assets/index-4e55b514.js +0 -536
- package/frontend/assets/index-cadcb0e9.css +0 -1
- package/frontend/assets/index-e24651da.js +0 -94
|
@@ -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,textarea{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)}textarea{font-size:14pt}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;transition:background-color .5s}button:hover{outline:1px solid var(--mild-contrast)}button[disabled]{background-color:var(--faint-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-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}.entry-comment{display:inline;white-space:pre-wrap;word-break:break-word}.entry-comment:before,.entry-comment:after{font-size:1.5em;font-family:serif;line-height:1px;position:relative}.entry-comment:before{content:"“";margin-left:.5em;margin-right:.1em;top:.2em}.entry-comment:after{content:"„";top:-.1em;margin-left:.1em}#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{margin-top:1em}.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}.nowrap{white-space:nowrap}.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-3a379840.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-3a379840.js"></script>
|
|
9
|
+
<link rel="stylesheet" href="/assets/index-db5f0e4b.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-alpha2",
|
|
4
4
|
"description": "HTTP File Server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file server",
|
|
@@ -79,7 +79,6 @@
|
|
|
79
79
|
"lodash": "^4.17.21",
|
|
80
80
|
"minimist": "^1.2.6",
|
|
81
81
|
"nat-upnp-ts": "^2.0.1",
|
|
82
|
-
"node-html-parser": "^6.1.5",
|
|
83
82
|
"open": "^8.4.0",
|
|
84
83
|
"tssrp6a": "^3.0.0",
|
|
85
84
|
"unzip-stream": "^0.3.1",
|
|
@@ -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.auth.js
CHANGED
|
@@ -33,17 +33,17 @@ function makeExp() {
|
|
|
33
33
|
const login = async ({ username, password }, ctx) => {
|
|
34
34
|
if (!username || !password) // some validation
|
|
35
35
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
36
|
+
const account = (0, perm_1.getAccount)(username);
|
|
37
|
+
if (!account || !(0, perm_1.accountCanLogin)(account))
|
|
38
38
|
return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
|
|
39
|
-
if (!
|
|
39
|
+
if (!account.hashed_password)
|
|
40
40
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE);
|
|
41
|
-
if (!await (0, crypt_1.verifyPassword)(
|
|
41
|
+
if (!await (0, crypt_1.verifyPassword)(account.hashed_password, password))
|
|
42
42
|
return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
|
|
43
43
|
if (!ctx.session)
|
|
44
44
|
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
|
45
45
|
await loggedIn(ctx, username);
|
|
46
|
-
return { ...makeExp(), redirect:
|
|
46
|
+
return { ...makeExp(), redirect: account.redirect };
|
|
47
47
|
};
|
|
48
48
|
exports.login = login;
|
|
49
49
|
const loginSrp1 = async ({ username }, ctx) => {
|
|
@@ -52,7 +52,7 @@ const loginSrp1 = async ({ username }, ctx) => {
|
|
|
52
52
|
const account = (0, perm_1.getAccount)(username);
|
|
53
53
|
if (!ctx.session)
|
|
54
54
|
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
|
55
|
-
if (!account) // TODO simulate fake account to prevent knowing valid usernames
|
|
55
|
+
if (!account || !(0, perm_1.accountCanLogin)(account)) // TODO simulate fake account to prevent knowing valid usernames
|
|
56
56
|
return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
|
|
57
57
|
try {
|
|
58
58
|
const { step1, ...rest } = await srpStep1(account);
|
package/src/api.file_list.js
CHANGED
|
@@ -12,6 +12,8 @@ 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 comments_1 = require("./comments");
|
|
16
|
+
const path_1 = require("path");
|
|
15
17
|
const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
16
18
|
var _a;
|
|
17
19
|
const node = await (0, vfs_1.urlToNode)(uri || '/', ctx);
|
|
@@ -36,12 +38,12 @@ const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
|
36
38
|
const can_upload = (0, vfs_1.hasPermission)(node, 'can_upload', ctx);
|
|
37
39
|
const fakeChild = (0, vfs_1.applyParentToChild)({}, node); // we want to know if we want to delete children
|
|
38
40
|
const can_delete = (0, vfs_1.hasPermission)(fakeChild, 'can_delete', ctx);
|
|
39
|
-
const
|
|
41
|
+
const can_archive = (0, vfs_1.hasPermission)(fakeChild, 'can_archive', ctx);
|
|
42
|
+
const props = { can_archive, can_upload, can_delete, accept: node.accept };
|
|
40
43
|
if (!list)
|
|
41
44
|
return { ...props, list: await (0, misc_1.asyncGeneratorToArray)(produceEntries()) };
|
|
42
45
|
setTimeout(async () => {
|
|
43
|
-
|
|
44
|
-
list.props(props);
|
|
46
|
+
list.props(props);
|
|
45
47
|
for await (const entry of produceEntries())
|
|
46
48
|
list.add(entry);
|
|
47
49
|
list.close();
|
|
@@ -57,14 +59,18 @@ const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
|
57
59
|
for await (const sub of walker) {
|
|
58
60
|
if (ctx.aborted)
|
|
59
61
|
break;
|
|
60
|
-
|
|
62
|
+
const name = (0, path_1.basename)((0, vfs_1.getNodeName)(sub));
|
|
63
|
+
if (comments_1.descriptIon.get() && name === comments_1.DESCRIPT_ION)
|
|
64
|
+
continue;
|
|
65
|
+
if (!filter(name))
|
|
61
66
|
continue;
|
|
62
67
|
const entry = await nodeToDirEntry(ctx, sub);
|
|
63
68
|
if (!entry)
|
|
64
69
|
continue;
|
|
65
70
|
const cbParams = { entry, ctx, listUri: uri, node: sub };
|
|
66
71
|
try {
|
|
67
|
-
|
|
72
|
+
const res = await Promise.all(onDirEntryHandlers.map(cb => cb(cbParams)));
|
|
73
|
+
if (res.some(x => x === false))
|
|
68
74
|
continue;
|
|
69
75
|
}
|
|
70
76
|
catch (e) {
|
|
@@ -96,11 +102,11 @@ const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
|
96
102
|
const st = await (0, promises_1.stat)(source);
|
|
97
103
|
const folder = st.isDirectory();
|
|
98
104
|
const { ctime, mtime } = st;
|
|
99
|
-
const pl = node.can_list ===
|
|
105
|
+
const pl = node.can_list === misc_1.WHO_NO_ONE ? 'l'
|
|
100
106
|
: !(0, vfs_1.hasPermission)(node, 'can_list', ctx) ? 'L'
|
|
101
107
|
: '';
|
|
102
108
|
// no download here, but maybe inside?
|
|
103
|
-
const pr = node.can_read ===
|
|
109
|
+
const pr = node.can_read === misc_1.WHO_NO_ONE && !(folder && filesInsideCould()) ? 'r'
|
|
104
110
|
: !(0, vfs_1.hasPermission)(node, 'can_read', ctx) ? 'R'
|
|
105
111
|
: '';
|
|
106
112
|
const pd = !can_delete && (0, vfs_1.hasPermission)(node, 'can_delete', ctx) ? 'd' : '';
|
|
@@ -109,7 +115,8 @@ const get_file_list = async ({ uri, offset, limit, search, c }, ctx) => {
|
|
|
109
115
|
c: ctime,
|
|
110
116
|
m: Math.abs(+mtime - +ctime) < 1000 ? undefined : mtime,
|
|
111
117
|
s: folder ? undefined : st.size,
|
|
112
|
-
p: (pr + pl + pd) || undefined
|
|
118
|
+
p: (pr + pl + pd) || undefined,
|
|
119
|
+
comment: await (0, comments_1.getCommentFor)(source),
|
|
113
120
|
};
|
|
114
121
|
}
|
|
115
122
|
catch (_a) {
|
package/src/api.net.js
CHANGED
|
@@ -4,12 +4,10 @@ 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
9
|
const nat_upnp_ts_1 = require("nat-upnp-ts");
|
|
10
10
|
const const_1 = require("./const");
|
|
11
|
-
const axios_1 = __importDefault(require("axios"));
|
|
12
|
-
const node_html_parser_1 = require("node-html-parser");
|
|
13
11
|
const lodash_1 = __importDefault(require("lodash"));
|
|
14
12
|
const listen_1 = require("./listen");
|
|
15
13
|
const github_1 = require("./github");
|
|
@@ -20,53 +18,59 @@ const acme_client_1 = __importDefault(require("acme-client"));
|
|
|
20
18
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
21
19
|
const http_1 = require("http");
|
|
22
20
|
const promises_2 = require("dns/promises");
|
|
23
|
-
const
|
|
24
|
-
const
|
|
21
|
+
const config_1 = require("./config");
|
|
22
|
+
const events_1 = __importDefault(require("./events"));
|
|
23
|
+
const net_1 = require("net");
|
|
24
|
+
const upnpClient = new nat_upnp_ts_1.Client({ timeout: 4000 });
|
|
25
|
+
const originalMethod = upnpClient.getGateway;
|
|
25
26
|
// other client methods call getGateway too, so this will ensure they reuse this same result
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
exports.externalIp =
|
|
29
|
-
(0, misc_1.repeat)(10 * misc_1.MINUTE, () =>
|
|
30
|
-
const was = exports.externalIp;
|
|
31
|
-
exports.externalIp = client.getPublicIp().catch(() => was); //fallback to previous value
|
|
32
|
-
});
|
|
27
|
+
upnpClient.getGateway = (0, misc_1.debounceAsync)(() => originalMethod.apply(upnpClient), 0, { retain: misc_1.HOUR, retainFailure: 30000 });
|
|
28
|
+
upnpClient.getGateway().catch(() => { });
|
|
29
|
+
exports.externalIp = ''; // poll external ip
|
|
30
|
+
(0, misc_1.repeat)(10 * misc_1.MINUTE, () => upnpClient.getPublicIp().then(v => exports.externalIp = v));
|
|
33
31
|
const getNatInfo = (0, misc_1.debounceAsync)(async () => {
|
|
34
32
|
var _a, _b;
|
|
35
|
-
const
|
|
36
|
-
const res = await
|
|
33
|
+
const gettingIps = getPublicIps(); // don't wait, do it in parallel
|
|
34
|
+
const res = await upnpClient.getGateway().catch(() => null);
|
|
37
35
|
const status = await (0, listen_1.getServerStatus)();
|
|
38
|
-
const mappings = res && await (0, misc_1.haveTimeout)(5000,
|
|
39
|
-
console.debug('mappings found', mappings);
|
|
40
|
-
const gatewayIp = res ? new URL(res.gateway.description).hostname : await findGateway().catch(() =>
|
|
36
|
+
const mappings = res && await (0, misc_1.haveTimeout)(5000, upnpClient.getMappings()).catch(() => null);
|
|
37
|
+
console.debug('mappings found', mappings === null || mappings === void 0 ? void 0 : mappings.map(x => x.description));
|
|
38
|
+
const gatewayIp = res ? new URL(res.gateway.description).hostname : await findGateway().catch(() => undefined);
|
|
41
39
|
const localIp = (res === null || res === void 0 ? void 0 : res.address) || (await (0, listen_1.getIps)())[0];
|
|
42
|
-
const internalPort = ((_a = status === null || status === void 0 ? void 0 : status.https) === null || _a === void 0 ? void 0 : _a.listening) && status.https.port || ((_b = status === null || status === void 0 ? void 0 : status.http) === null || _b === void 0 ? void 0 : _b.listening) && status.http.port;
|
|
40
|
+
const internalPort = ((_a = status === null || status === void 0 ? void 0 : status.https) === null || _a === void 0 ? void 0 : _a.listening) && status.https.port || ((_b = status === null || status === void 0 ? void 0 : status.http) === null || _b === void 0 ? void 0 : _b.listening) && status.http.port || undefined;
|
|
43
41
|
const mapped = lodash_1.default.find(mappings, x => x.private.host === localIp && x.private.port === internalPort);
|
|
44
42
|
return {
|
|
45
43
|
upnp: Boolean(res),
|
|
46
44
|
localIp,
|
|
47
45
|
gatewayIp,
|
|
48
|
-
|
|
49
|
-
externalIp:
|
|
46
|
+
publicIps: await gettingIps,
|
|
47
|
+
externalIp: exports.externalIp,
|
|
50
48
|
mapped,
|
|
51
49
|
internalPort,
|
|
52
50
|
externalPort: mapped === null || mapped === void 0 ? void 0 : mapped.public.port,
|
|
53
51
|
};
|
|
54
52
|
});
|
|
55
|
-
async function
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
async function getPublicIps() {
|
|
54
|
+
const res = await (0, github_1.getProjectInfo)();
|
|
55
|
+
const groupedByVersion = Object.values(lodash_1.default.groupBy(res.publicIpServices, x => { var _a; return (_a = x.v) !== null && _a !== void 0 ? _a : 4; }));
|
|
56
|
+
const ips = await (0, misc_1.promiseBestEffort)(groupedByVersion.map(singleVersion => Promise.any(singleVersion.map(async (svc) => {
|
|
57
|
+
if (typeof svc === 'string')
|
|
58
|
+
svc = { type: 'http', url: svc };
|
|
59
|
+
console.debug("trying ip service", svc.url || svc.name);
|
|
60
|
+
if (svc.type === 'http')
|
|
61
|
+
return (0, util_http_1.httpString)(svc.url);
|
|
62
|
+
if (svc.type !== 'dns')
|
|
63
|
+
throw "unsupported";
|
|
64
|
+
const resolver = new promises_2.Resolver({ timeout: 2000 });
|
|
65
|
+
resolver.setServers(svc.ips);
|
|
66
|
+
return resolver.resolve(svc.name, svc.dnsRecord);
|
|
67
|
+
}).map(async (ret) => {
|
|
68
|
+
const validIps = (0, misc_1.wantArray)(await ret).map(x => x.trim()).filter(net_1.isIP);
|
|
69
|
+
if (!validIps.length)
|
|
70
|
+
throw "no good";
|
|
71
|
+
return validIps;
|
|
72
|
+
}))));
|
|
73
|
+
return lodash_1.default.uniq(ips.flat());
|
|
70
74
|
}
|
|
71
75
|
function findGateway() {
|
|
72
76
|
return new Promise((resolve, reject) => (0, child_process_1.exec)(const_1.IS_WINDOWS || const_1.IS_MAC ? 'netstat -rn' : 'route -n', (err, out) => {
|
|
@@ -105,12 +109,16 @@ async function checkDomain(domain) {
|
|
|
105
109
|
(0, promises_2.lookup)(domain).then(x => [x.address]),
|
|
106
110
|
]);
|
|
107
111
|
// merge all results
|
|
108
|
-
const
|
|
109
|
-
if (!
|
|
112
|
+
const domainIps = lodash_1.default.uniq((0, misc_1.onlyTruthy)(settled.map(x => x.status === 'fulfilled' && x.value)).flat());
|
|
113
|
+
if (!domainIps.length)
|
|
110
114
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "domain not working");
|
|
111
|
-
const {
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
const { publicIps } = await getNatInfo(); // do this before stopping the server
|
|
116
|
+
for (const v6 of [false, true]) {
|
|
117
|
+
const domainIpsThisVersion = domainIps.filter(x => (0, net_1.isIPv6)(x) === v6);
|
|
118
|
+
const ipsThisVersion = publicIps.filter(x => (0, net_1.isIPv6)(x) === v6);
|
|
119
|
+
if (domainIpsThisVersion.length && ipsThisVersion.length && !lodash_1.default.intersection(domainIpsThisVersion, ipsThisVersion).length)
|
|
120
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, `configure your domain to point to ${ipsThisVersion} (currently on ${domainIpsThisVersion[0]}) – a change can take hours to be effective`);
|
|
121
|
+
}
|
|
114
122
|
}
|
|
115
123
|
async function generateSSLCert(domain, email) {
|
|
116
124
|
await checkDomain(domain);
|
|
@@ -129,19 +137,19 @@ async function generateSSLCert(domain, email) {
|
|
|
129
137
|
let check = await checkPort(domain, 80); // some check services may not consider the domain, but we already verified that
|
|
130
138
|
if (check && !check.success && upnp && externalPort !== 80) { // consider a short-lived mapping
|
|
131
139
|
// @ts-ignore
|
|
132
|
-
await
|
|
140
|
+
await upnpClient.createMapping({ private: 80, public: { host: '', port: 80 }, description: 'hfs temporary', ttl: 30 }).catch(() => { });
|
|
133
141
|
check = await checkPort(domain, 80); // repeat test
|
|
134
142
|
}
|
|
135
143
|
if (!check)
|
|
136
144
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "couldn't test port 80");
|
|
137
145
|
if (!check.success)
|
|
138
146
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "port 80 is not working on the specified domain");
|
|
139
|
-
const
|
|
147
|
+
const acmeClient = new acme_client_1.default.Client({
|
|
140
148
|
accountKey: await acme_client_1.default.crypto.createPrivateKey(),
|
|
141
149
|
directoryUrl: acme_client_1.default.directory.letsencrypt.production
|
|
142
150
|
});
|
|
143
151
|
const [key, csr] = await acme_client_1.default.crypto.createCsr({ commonName: domain });
|
|
144
|
-
const cert = await
|
|
152
|
+
const cert = await acmeClient.auto({
|
|
145
153
|
csr,
|
|
146
154
|
email,
|
|
147
155
|
challengePriority: ['http-01'],
|
|
@@ -166,28 +174,26 @@ async function generateSSLCert(domain, email) {
|
|
|
166
174
|
}
|
|
167
175
|
async function checkPort(ip, port) {
|
|
168
176
|
const prjInfo = await (0, github_1.getProjectInfo)();
|
|
177
|
+
console.log(`checking server ${ip}:${port}`);
|
|
169
178
|
for (const services of lodash_1.default.chunk(lodash_1.default.shuffle(prjInfo.checkServerServices), 2)) {
|
|
170
179
|
try {
|
|
171
|
-
return Promise.any(services.map(async (
|
|
172
|
-
|
|
173
|
-
const service = new URL(svc.url).hostname;
|
|
180
|
+
return Promise.any(services.map(async ({ url, body, selector, regexpSuccess, regexpFailure, ...rest }) => {
|
|
181
|
+
const service = new URL(url).hostname;
|
|
174
182
|
console.log('trying service', service);
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const parsed = (_b = (0, node_html_parser_1.parse)(res.data).querySelector(svc.selector)) === null || _b === void 0 ? void 0 : _b.innerText;
|
|
179
|
-
if (!parsed)
|
|
180
|
-
throw console.debug('empty:' + service);
|
|
181
|
-
const success = new RegExp(svc.regexpSuccess).test(parsed);
|
|
182
|
-
const failure = new RegExp(svc.regexpFailure).test(parsed);
|
|
183
|
+
const res = await (0, util_http_1.httpString)(applySymbols(url), { family: (0, net_1.isIPv6)(ip) ? 6 : 4, body: applySymbols(body), ...rest });
|
|
184
|
+
const success = new RegExp(regexpSuccess).test(res);
|
|
185
|
+
const failure = new RegExp(regexpFailure).test(res);
|
|
183
186
|
if (success === failure)
|
|
184
187
|
throw console.debug('inconsistent:' + service); // this result cannot be trusted
|
|
185
188
|
console.debug(service, 'responded', success);
|
|
186
|
-
return { success, service };
|
|
189
|
+
return { success, service, ip, port };
|
|
187
190
|
}));
|
|
188
191
|
}
|
|
189
192
|
catch (_a) { }
|
|
190
193
|
}
|
|
194
|
+
function applySymbols(s) {
|
|
195
|
+
return s === null || s === void 0 ? void 0 : s.replace('$IP', ip).replace('$PORT', String(port));
|
|
196
|
+
}
|
|
191
197
|
}
|
|
192
198
|
const apis = {
|
|
193
199
|
get_nat: getNatInfo,
|
|
@@ -203,40 +209,59 @@ const apis = {
|
|
|
203
209
|
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'no internal port');
|
|
204
210
|
if (externalPort)
|
|
205
211
|
try {
|
|
206
|
-
await
|
|
212
|
+
await upnpClient.removeMapping({ public: { host: '', port: externalPort } });
|
|
207
213
|
}
|
|
208
214
|
catch (e) {
|
|
209
215
|
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, 'removeMapping failed: ' + String(e));
|
|
210
216
|
}
|
|
211
217
|
if (external) // must use the object form of 'public' to work around a bug of the library
|
|
212
|
-
await
|
|
218
|
+
await upnpClient.createMapping({ private: internal || internalPort, public: { host: '', port: external }, description: 'hfs', ttl: 0 });
|
|
213
219
|
return {};
|
|
214
220
|
},
|
|
215
221
|
async check_server({ port }) {
|
|
216
|
-
const {
|
|
217
|
-
if (!
|
|
218
|
-
return new apiMiddleware_1.ApiError(const_1.
|
|
222
|
+
const { publicIps, internalPort, externalPort } = await getNatInfo();
|
|
223
|
+
if (!publicIps.length)
|
|
224
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'cannot detect public ip');
|
|
219
225
|
if (!internalPort)
|
|
220
226
|
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'no internal port');
|
|
221
227
|
port || (port = externalPort || internalPort);
|
|
222
|
-
|
|
223
|
-
return
|
|
224
|
-
|| new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
|
|
228
|
+
const res = await (0, misc_1.promiseBestEffort)(publicIps.map(ip => checkPort(ip, port)));
|
|
229
|
+
return res.length ? res : new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
|
|
225
230
|
},
|
|
226
231
|
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);
|
|
232
|
+
await (0, exports.makeCert)(domain, email);
|
|
236
233
|
return {};
|
|
237
234
|
},
|
|
238
235
|
get_cert() {
|
|
239
236
|
return (0, misc_1.objSameKeys)(lodash_1.default.pick((0, listen_1.getCertObject)(), ['subject', 'issuer', 'validFrom', 'validTo']), v => v);
|
|
240
237
|
}
|
|
241
238
|
};
|
|
239
|
+
exports.acme_domain = (0, config_1.defineConfig)('acme_domain', '');
|
|
240
|
+
exports.acme_email = (0, config_1.defineConfig)('acme_email', '');
|
|
241
|
+
exports.makeCert = (0, misc_1.debounceAsync)(async (domain, email) => {
|
|
242
|
+
if (!domain)
|
|
243
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad params');
|
|
244
|
+
const res = await generateSSLCert(domain, email);
|
|
245
|
+
const CERT_FILE = 'acme.cert';
|
|
246
|
+
const KEY_FILE = 'acme.key';
|
|
247
|
+
await promises_1.default.writeFile(CERT_FILE, res.cert);
|
|
248
|
+
await promises_1.default.writeFile(KEY_FILE, res.key);
|
|
249
|
+
listen_1.cert.set(CERT_FILE); // update config
|
|
250
|
+
listen_1.privateKey.set(KEY_FILE);
|
|
251
|
+
}, 0);
|
|
252
|
+
(0, config_1.defineConfig)('acme_renew', false); // handle config changes
|
|
253
|
+
events_1.default.once('https ready', () => (0, misc_1.repeat)(misc_1.HOUR, renewCert));
|
|
254
|
+
// checks if the cert is near expiration date, and if so renews it
|
|
255
|
+
const renewCert = (0, misc_1.debounceAsync)(async () => {
|
|
256
|
+
const cert = (0, listen_1.getCertObject)();
|
|
257
|
+
if (!cert)
|
|
258
|
+
return;
|
|
259
|
+
const now = new Date();
|
|
260
|
+
const validTo = new Date(cert.validTo);
|
|
261
|
+
// not expiring in a month
|
|
262
|
+
if (now > new Date(cert.validFrom) && now < validTo && validTo.getTime() - now.getTime() >= 30 * misc_1.DAY)
|
|
263
|
+
return console.log("certificate still good");
|
|
264
|
+
await (0, exports.makeCert)(exports.acme_domain.get(), exports.acme_email.get())
|
|
265
|
+
.catch(e => console.log("error renewing certificate: ", String(e)));
|
|
266
|
+
}, 0, { retain: misc_1.DAY, retainFailure: misc_1.HOUR });
|
|
242
267
|
exports.default = apis;
|
package/src/api.plugins.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.checkDependencies = void 0;
|
|
7
8
|
const plugins_1 = require("./plugins");
|
|
8
9
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
10
|
const assert_1 = __importDefault(require("assert"));
|
|
@@ -15,7 +16,7 @@ const github_1 = require("./github");
|
|
|
15
16
|
const const_1 = require("./const");
|
|
16
17
|
const apis = {
|
|
17
18
|
get_plugins({}, ctx) {
|
|
18
|
-
const list = new apiMiddleware_1.SendListReadable({ addAtStart: [...(0, plugins_1.mapPlugins)(serialize), ...(0, plugins_1.getAvailablePlugins)().map(serialize)] });
|
|
19
|
+
const list = new apiMiddleware_1.SendListReadable({ addAtStart: [...(0, plugins_1.mapPlugins)(serialize, false), ...(0, plugins_1.getAvailablePlugins)().map(serialize)] });
|
|
19
20
|
return list.events(ctx, {
|
|
20
21
|
pluginInstalled: p => list.add(serialize(p)),
|
|
21
22
|
'pluginStarted pluginStopped pluginUpdated': p => {
|
|
@@ -28,7 +29,8 @@ const apis = {
|
|
|
28
29
|
async get_plugin_updates() {
|
|
29
30
|
return new apiMiddleware_1.SendListReadable({
|
|
30
31
|
async doAtStart(list) {
|
|
31
|
-
const errs =
|
|
32
|
+
const errs = [];
|
|
33
|
+
await Promise.allSettled(lodash_1.default.map((0, github_1.getFolder2repo)(), async (repo, folder) => {
|
|
32
34
|
try {
|
|
33
35
|
if (!repo)
|
|
34
36
|
return;
|
|
@@ -46,12 +48,11 @@ const apis = {
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
catch (err) {
|
|
49
|
-
if (err.message
|
|
50
|
-
|
|
51
|
-
return err.code || err.message;
|
|
51
|
+
if (err.message !== '404') // the plugin is declaring a wrong repo
|
|
52
|
+
errs.push(err.code || err.message);
|
|
52
53
|
}
|
|
53
54
|
}));
|
|
54
|
-
for (const x of lodash_1.default.uniq(
|
|
55
|
+
for (const x of lodash_1.default.uniq(errs))
|
|
55
56
|
list.error(x);
|
|
56
57
|
list.close();
|
|
57
58
|
}
|
|
@@ -124,18 +125,16 @@ const apis = {
|
|
|
124
125
|
});
|
|
125
126
|
},
|
|
126
127
|
async download_plugin({ id, branch }) {
|
|
127
|
-
await checkDependencies(id, branch);
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
return res;
|
|
131
|
-
return (await (0, misc_1.waitFor)(() => (0, plugins_1.getPluginInfo)(res), { timeout: 5000 }))
|
|
128
|
+
await checkDependencies(await (0, github_1.readOnlinePlugin)(id, branch));
|
|
129
|
+
const folder = await (0, github_1.downloadPlugin)(id, { branch });
|
|
130
|
+
return (await (0, misc_1.waitFor)(() => (0, plugins_1.getPluginInfo)(folder), { timeout: 5000 }))
|
|
132
131
|
|| new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
|
133
132
|
},
|
|
134
133
|
async update_plugin({ id }) {
|
|
135
134
|
const found = (0, plugins_1.getPluginInfo)(id);
|
|
136
135
|
if (!found)
|
|
137
136
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
138
|
-
await checkDependencies(found
|
|
137
|
+
await checkDependencies(found);
|
|
139
138
|
const enabled = (0, plugins_1.isPluginEnabled)(id);
|
|
140
139
|
await (0, plugins_1.stopPlugin)(id);
|
|
141
140
|
await (0, github_1.downloadPlugin)(found.repo, { overwrite: true });
|
|
@@ -157,19 +156,9 @@ function serialize(p) {
|
|
|
157
156
|
o.repo = o.repo.web;
|
|
158
157
|
return lodash_1.default.defaults(o, { started: null, badApi: null }); // nulls should be used to be sure to overwrite previous values,
|
|
159
158
|
}
|
|
160
|
-
async function checkDependencies(
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
return;
|
|
164
|
-
const miss = rec.depend && rec.depend.map((dep) => {
|
|
165
|
-
const res = (0, plugins_1.findPluginByRepo)(dep.repo);
|
|
166
|
-
const error = !res ? 'missing'
|
|
167
|
-
: (res.version || 0) < dep.version ? 'version'
|
|
168
|
-
: !(0, plugins_1.isPluginEnabled)(res.id) ? 'disabled'
|
|
169
|
-
: !(0, plugins_1.isPluginRunning)(res.id) ? 'stopped'
|
|
170
|
-
: '';
|
|
171
|
-
return error && { repo: dep.repo, error, id: res === null || res === void 0 ? void 0 : res.id };
|
|
172
|
-
}).filter(Boolean);
|
|
173
|
-
if (miss === null || miss === void 0 ? void 0 : miss.length)
|
|
159
|
+
async function checkDependencies(plugin) {
|
|
160
|
+
const miss = await (0, plugins_1.getMissingDependencies)(plugin);
|
|
161
|
+
if (miss.length)
|
|
174
162
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, miss);
|
|
175
163
|
}
|
|
164
|
+
exports.checkDependencies = checkDependencies;
|
package/src/api.vfs.js
CHANGED
|
@@ -19,10 +19,7 @@ async function urlToNodeOriginal(uri) {
|
|
|
19
19
|
}
|
|
20
20
|
const apis = {
|
|
21
21
|
async get_vfs() {
|
|
22
|
-
return {
|
|
23
|
-
root: await recur(),
|
|
24
|
-
defaultPerms: vfs_1.defaultPerms,
|
|
25
|
-
};
|
|
22
|
+
return { root: await recur() };
|
|
26
23
|
async function recur(node = vfs_1.vfs) {
|
|
27
24
|
var _a;
|
|
28
25
|
const { source } = node;
|
|
@@ -32,19 +29,19 @@ const apis = {
|
|
|
32
29
|
: { size: source ? -1 : undefined };
|
|
33
30
|
if (copyStats.mtime && Number(copyStats.mtime) === Number(copyStats.ctime))
|
|
34
31
|
delete copyStats.mtime;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
&&
|
|
38
|
-
|
|
39
|
-
byMasks = undefined;
|
|
32
|
+
const inherited = node.parent && (0, vfs_1.permsFromParent)(node.parent, node.original || node);
|
|
33
|
+
const byMasks = node.original && lodash_1.default.pickBy(node, (v, k) => v !== node.original[k] // something is changing me...
|
|
34
|
+
&& !(inherited && k in inherited) // ...and it's not inheritance...
|
|
35
|
+
&& misc_1.PERM_KEYS.includes(k)); // ...must be masks. Please limit this to perms
|
|
40
36
|
return {
|
|
41
37
|
...copyStats,
|
|
42
38
|
...node.original || node,
|
|
43
|
-
|
|
39
|
+
inherited,
|
|
40
|
+
byMasks: lodash_1.default.isEmpty(byMasks) ? undefined : byMasks,
|
|
44
41
|
website: Boolean((_a = node.children) === null || _a === void 0 ? void 0 : _a.find((0, vfs_1.isSameFilenameAs)('index.html')))
|
|
45
42
|
|| isDir && source && await (0, promises_1.stat)((0, path_1.join)(source, 'index.html')).then(() => true, () => undefined)
|
|
46
43
|
|| undefined,
|
|
47
|
-
name: node === vfs_1.vfs ?
|
|
44
|
+
name: node === vfs_1.vfs ? '' : (0, vfs_1.getNodeName)(node),
|
|
48
45
|
type: isDir ? 'folder' : undefined,
|
|
49
46
|
children: node.children && await Promise.all(node.children.map(child => recur((0, vfs_1.applyParentToChild)(child, node))))
|
|
50
47
|
};
|
|
@@ -78,7 +75,7 @@ const apis = {
|
|
|
78
75
|
const n = await urlToNodeOriginal(uri);
|
|
79
76
|
if (!n)
|
|
80
77
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'path not found');
|
|
81
|
-
props = pickProps(props, ['name', 'source', 'masks', 'default', 'accept', ...
|
|
78
|
+
props = pickProps(props, ['name', 'source', 'masks', 'default', 'accept', ...misc_1.PERM_KEYS]); // sanitize
|
|
82
79
|
if (props.name && props.name !== (0, vfs_1.getNodeName)(n)) {
|
|
83
80
|
const parent = await urlToNodeOriginal((0, path_1.dirname)(uri));
|
|
84
81
|
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))
|