hfs 0.26.2 → 0.26.5
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 +111 -7
- package/admin/assets/{index.f056db34.js → index.3129dad1.js} +16 -16
- package/{frontend/assets/sha512.634b743e.js → admin/assets/sha512.e9b1ee42.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/{index.55c710c2.js → index.1151988f.js} +16 -16
- package/frontend/assets/index.93366732.css +1 -0
- package/{admin/assets/sha512.3c0e384c.js → frontend/assets/sha512.bb881250.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +1 -2
- package/plugins/vhosting/plugin.js +1 -3
- package/src/api.plugins.js +3 -1
- package/src/commands.js +22 -3
- package/src/config.js +1 -5
- package/src/const.js +1 -1
- package/src/listen.js +25 -16
- package/src/plugins.js +33 -28
- package/src/util-files.js +17 -10
- package/src/vfs.js +38 -28
- package/frontend/assets/index.ee805a6c.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg: #fff;--text: #555;--faint-contrast: #0002;--mild-contrast: #0005;--good-contrast: #000a;--button-bg: #68a;--button-text: #fff;--focus-color: #468}:root .theme-dark{--bg: #000;--text: #999;--faint-contrast: #fff2;--mild-contrast: #fff5;--good-contrast: #fffa;--button-bg: #345;--button-text: #999}:root .theme-dark body{color-scheme:dark}:root .theme-dark a{color:#8ac}:root .theme-dark .dialog-closer{background:#633}:root .theme-dark .dialog-icon{color:#ccc}:root .theme-dark .dialog-backdrop{background:rgba(51,51,51,.7333333333)}body{background:var(--bg);margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,button,select,input{font-size:12pt}#root{max-width:50em;margin:auto;min-height:100vh;display:flex;flex-direction:column}body,input{color:var(--text)}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}input,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}input[type=checkbox]{margin:0 1.3em 0 .8em;transform:scale(1.7);accent-color:var(--button-bg)}.hidden{display:none!important}.icon{font-size:1.2em}.icon.mirror:before{transform:scaleX(-1)}a{text-decoration:none;color:#57a}button{background-color:var(--button-bg);color:var(--button-text);padding:.5em 1em;border:transparent;text-decoration:none;border-radius:.3em;vertical-align:middle;cursor:pointer}button.toggled{opacity:.6}button:focus-visible,.breadcrumb:focus-visible{outline:3px solid var(--focus-color)}input:focus-visible,select:focus-visible,ul a:focus-visible{border-radius:.3em;border-color:transparent;outline:2px solid var(--focus-color)}.error-msg{background-color:#faa;color:#833}header{position:sticky;top:0;background:var(--bg);padding:.2em;z-index:1}.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}.icon.emoji.spinner{display:inline-block}.breadcrumb{padding:.1em .6em .2em;line-height:1.8em;border-radius:.7em;background-color:var(--button-bg);color:var(--button-text);border-top:1px solid #666;margin-right:-.1em}.breadcrumb:nth-child(-n+3) .icon{padding:0 .2em}#folder-stats{font-size:90%;margin:.4em 0 0 .5em;float:right}#folder-stats .icon{margin-right:.3em}header input{width:100%;margin:.2em auto;box-sizing:border-box}#filter-bar{display:flex;gap:.3em;margin:.5em 0}#filter-bar input{flex:1}#filter-bar button{padding:0 .5em}ul.dir{flex:1;padding:0;margin:0;clear:both}ul.dir li{display:block;list-style-type:none;margin-bottom:.3em;padding:.3em;border-top:1px solid var(--button-bg)}ul.dir li a{word-break:break-word;padding-right:.3em}ul.dir li a .icon{margin-right:.3em}ul.dir li a.container-folder:hover{text-decoration:underline}ul.dir li .entry-props{float:right;font-size:90%;margin-left:12px;margin-top:.2em}ul.dir li .entry-props .icon{margin:0 .3em}ul.dir li .entry-props .entry-size{display:inline-block}#menu-panel{margin-bottom:.2em}#menu-bar{display:flex;justify-content:space-evenly;flex-wrap:wrap}#menu-bar>*{flex:auto;margin:.1em}#menu-bar button{padding-left:0;padding-right:0}#menu-bar>a>button{width:100%}#searched{margin:.2em}#user-panel{display:flex;flex-direction:column;gap:1em}#user-panel a>button{width:100%}button label{cursor:inherit;margin-left:.5em}.dialog-backdrop.working{font-size:5em;animation:1s fade-in}.dialog-content{padding:.2em}.dialog-confirm,.dialog-prompt{--color: var(--button-bg)}#paging{display:flex;position:sticky;bottom:0;background:var(--bg);gap:.5em;overflow-x:auto}#paging>button{flex:1;background:var(--button-bg);text-align:center}*{scrollbar-width:thin;scrollbar-color:var(--button-bg) var(--faint-contrast)}*::-webkit-scrollbar{width:12px}*::-webkit-scrollbar-track{background:var(--faint-contrast)}*::-webkit-scrollbar-thumb{background-color:var(--button-bg);border-radius:20px;border:1px solid var(--faint-contrast)}@media (max-width: 42em){body,button,select{font-size:14pt}#menu-bar button label{display:none}#filter-bar{margin:.2em 0}#filter-bar label{display:none}#filter-bar button{width:17.6vw;height:2.3em}.breadcrumb{word-break:break-all}.breadcrumb .icon{font-size:24px}}.dialog-backdrop{position:fixed;inset:0;background:#888a;display:flex;justify-content:center;align-items:center;z-index:1000}.dialog{background:#fff;background:var(--bg);padding:max(.5em,1vw);border-radius:1em;position:relative;margin:0 3vw;overflow:hidden;max-height:calc(100vh - 2em)}.dialog-icon{color:#fff;background-color:var(--color);position:absolute;top:0;width:1.8em;height:1.7em;text-align:center;border-radius:.8em 0}.dialog-closer{border-radius:0 .8em;right:0;padding:0;background-color:#c99}.dialog-icon~.dialog-content{margin-top:1.3em}.dialog-type{left:0;top:0;overflow:hidden;line-height:1.7em}.dialog-content{overflow:auto;max-height:calc(100vh - 4em)}.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: 50em){.dialog-closer{font-size:120%}.dialog-icon~.dialog-content{margin-top:2em}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as SF}from"./index.
|
|
1
|
+
import{c as SF}from"./index.1151988f.js";function OF(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 yF={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
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<link href="fontello.css" rel="stylesheet" />
|
|
7
7
|
<script>SESSION = _HFS_SESSION_</script>
|
|
8
8
|
<title>File Server</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index.
|
|
10
|
-
<link rel="stylesheet" href="/assets/index.
|
|
9
|
+
<script type="module" crossorigin src="/assets/index.1151988f.js"></script>
|
|
10
|
+
<link rel="stylesheet" href="/assets/index.93366732.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div hidden>_HFS_PLUGINS_</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hfs",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.5",
|
|
4
4
|
"description": "HTTP File Server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file server",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"dist-bin": "npm run dist-modules && cd dist && pkg . -C gzip && mv -f hfs-win-x64.exe hfs.exe && zip hfs-windows.zip hfs.exe -r plugins && cp -f hfs-linux-x64 hfs && zip hfs-linux.zip hfs -r plugins && cp -f hfs-macos-x64 hfs && zip hfs-mac.zip hfs -r plugins && cp -f hfs-macos-arm64 hfs && zip hfs-mac-arm.zip hfs -r plugins && rm hfs",
|
|
31
31
|
"dist-modules": "cp package*.json dist && cd dist && npm ci --omit=dev && npm i -f --no-save --omit=dev @node-rs/crc32-win32-x64-msvc && rm package-lock.json && cd .. && node prune_modules",
|
|
32
32
|
"dist-win": "npm run dist-modules && cd dist && pkg . -C gzip -t node16-win-x64 && zip hfs-windows.zip hfs.exe -r plugins",
|
|
33
|
-
"dist-mac": "cd dist && pkg . -C gzip -t node16-mac-arm64",
|
|
34
33
|
"dist-node": "npm run dist-modules && cd dist && zip hfs-node.zip -r * -x *.zip *.exe hfs-* *.log logs"
|
|
35
34
|
},
|
|
36
35
|
"engines": {
|
package/src/api.plugins.js
CHANGED
|
@@ -23,7 +23,9 @@ const apis = {
|
|
|
23
23
|
pluginUninstalled: id => list.remove({ id }),
|
|
24
24
|
});
|
|
25
25
|
function serialize(p) {
|
|
26
|
-
|
|
26
|
+
const o = 'getData' in p ? Object.assign(lodash_1.default.pick(p, ['id', 'started']), p.getData())
|
|
27
|
+
: { ...p }; // _.defaults mutates object, and we don't want that
|
|
28
|
+
return lodash_1.default.defaults(o, { started: null, badApi: null }); // nulls should be used to be sure to overwrite previous values,
|
|
27
29
|
}
|
|
28
30
|
},
|
|
29
31
|
async get_plugin_updates() {
|
package/src/commands.js
CHANGED
|
@@ -7,10 +7,23 @@ const perm_1 = require("./perm");
|
|
|
7
7
|
const config_1 = require("./config");
|
|
8
8
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
9
|
const update_1 = require("./update");
|
|
10
|
+
const listen_1 = require("./listen");
|
|
10
11
|
const yaml_1 = __importDefault(require("yaml"));
|
|
11
12
|
const const_1 = require("./const");
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const readline_1 = require("readline");
|
|
14
|
+
try {
|
|
15
|
+
/*
|
|
16
|
+
is this try-block useful in case the stdin is unavailable?
|
|
17
|
+
Not sure, but someone reported a problem using nohup https://github.com/rejetto/hfs/issues/74
|
|
18
|
+
and I've found this example try-catching https://github.com/DefinitelyTyped/DefinitelyTyped/blob/dda83a906914489e09ca28afea12948529015d4a/types/node/readline.d.ts#L489
|
|
19
|
+
*/
|
|
20
|
+
(0, readline_1.createInterface)({ input: process.stdin }).on('line', parseCommandLine);
|
|
21
|
+
console.log(`HINT: type "help" for help`);
|
|
22
|
+
}
|
|
23
|
+
catch (_a) {
|
|
24
|
+
console.log("console commands not available");
|
|
25
|
+
}
|
|
26
|
+
function parseCommandLine(line) {
|
|
14
27
|
if (!line)
|
|
15
28
|
return;
|
|
16
29
|
const [name, ...params] = line.trim().split(/ +/);
|
|
@@ -25,7 +38,7 @@ require('readline').createInterface({ input: process.stdin }).on('line', (line)
|
|
|
25
38
|
else
|
|
26
39
|
throw err;
|
|
27
40
|
});
|
|
28
|
-
}
|
|
41
|
+
}
|
|
29
42
|
const commands = {
|
|
30
43
|
help: {
|
|
31
44
|
params: '',
|
|
@@ -33,6 +46,12 @@ const commands = {
|
|
|
33
46
|
console.log("supported commands:", ...lodash_1.default.map(commands, ({ params }, name) => '\n - ' + name + ' ' + params));
|
|
34
47
|
}
|
|
35
48
|
},
|
|
49
|
+
'show-admin': {
|
|
50
|
+
params: '',
|
|
51
|
+
cb() {
|
|
52
|
+
(0, listen_1.openAdmin)();
|
|
53
|
+
}
|
|
54
|
+
},
|
|
36
55
|
'create-admin': {
|
|
37
56
|
params: '<password> [<username>=admin]',
|
|
38
57
|
async cb(password, username = 'admin') {
|
package/src/config.js
CHANGED
|
@@ -12,7 +12,6 @@ const yaml_1 = __importDefault(require("yaml"));
|
|
|
12
12
|
const lodash_1 = __importDefault(require("lodash"));
|
|
13
13
|
const misc_1 = require("./misc");
|
|
14
14
|
const fs_1 = require("fs");
|
|
15
|
-
const util_1 = require("util");
|
|
16
15
|
const path_1 = require("path");
|
|
17
16
|
const events_2 = __importDefault(require("./events"));
|
|
18
17
|
const FILE = 'config.yaml';
|
|
@@ -163,10 +162,7 @@ exports.saveConfigAsap = (0, misc_1.debounceAsync)(async () => {
|
|
|
163
162
|
await (0, misc_1.wait)(100);
|
|
164
163
|
let txt = yaml_1.default.stringify(state, { lineWidth: 1000 });
|
|
165
164
|
if (txt.trim() === '{}') // most users wouldn't understand
|
|
166
|
-
|
|
167
|
-
txt = '';
|
|
168
|
-
else
|
|
169
|
-
return;
|
|
165
|
+
txt = '';
|
|
170
166
|
save(path, txt)
|
|
171
167
|
.catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
|
|
172
168
|
});
|
package/src/const.js
CHANGED
|
@@ -43,7 +43,7 @@ const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
|
43
43
|
exports.VERSION = pkg.version;
|
|
44
44
|
exports.DAY = 86400000;
|
|
45
45
|
exports.SESSION_DURATION = exports.DAY;
|
|
46
|
-
exports.API_VERSION = 4; //
|
|
46
|
+
exports.API_VERSION = 4.1; // array.$width
|
|
47
47
|
exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise is made equal to API_VERSION
|
|
48
48
|
exports.SPECIAL_URI = '/~/';
|
|
49
49
|
exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
|
package/src/listen.js
CHANGED
|
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
28
|
};
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.getUrls = exports.getStatus = exports.httpsPortCfg = exports.portCfg = void 0;
|
|
30
|
+
exports.getUrls = exports.getStatus = exports.httpsPortCfg = exports.openAdmin = exports.portCfg = void 0;
|
|
31
31
|
const http = __importStar(require("http"));
|
|
32
32
|
const config_1 = require("./config");
|
|
33
33
|
const index_1 = require("./index");
|
|
@@ -40,6 +40,7 @@ const misc_1 = require("./misc");
|
|
|
40
40
|
const const_1 = require("./const");
|
|
41
41
|
const find_process_1 = __importDefault(require("find-process"));
|
|
42
42
|
const perm_1 = require("./perm");
|
|
43
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
43
44
|
let httpSrv;
|
|
44
45
|
let httpsSrv;
|
|
45
46
|
const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', !const_1.DEV);
|
|
@@ -55,13 +56,25 @@ exports.portCfg.sub(async (port) => {
|
|
|
55
56
|
httpSrv.on('connection', connections_1.newConnection);
|
|
56
57
|
printUrls(port, 'http');
|
|
57
58
|
if (openBrowserAtStart.get())
|
|
58
|
-
(
|
|
59
|
+
openAdmin();
|
|
60
|
+
});
|
|
61
|
+
function openAdmin() {
|
|
62
|
+
for (const srv of [httpSrv, httpsSrv]) {
|
|
63
|
+
const a = srv.address();
|
|
64
|
+
if (!a || typeof a === 'string')
|
|
65
|
+
continue;
|
|
66
|
+
const baseUrl = srv.name + '://localhost:' + a.port;
|
|
67
|
+
(0, open_1.default)(baseUrl + const_1.ADMIN_URI, { wait: true }).catch(e => {
|
|
59
68
|
console.debug(String(e));
|
|
60
69
|
console.warn("cannot launch browser on this machine >PLEASE< open your browser and reach one of these (you may need a different address)", ...Object.values(getUrls()).flat().map(x => '\n - ' + x + const_1.ADMIN_URI));
|
|
61
70
|
if (!(0, perm_1.anyAccountCanLoginAdmin)())
|
|
62
71
|
console.log(`HINT: you can enter command: create-admin YOUR_PASSWORD`);
|
|
63
72
|
});
|
|
64
|
-
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
console.log("openAdmin failed");
|
|
76
|
+
}
|
|
77
|
+
exports.openAdmin = openAdmin;
|
|
65
78
|
const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
66
79
|
var _a, _b;
|
|
67
80
|
stopServer(httpsSrv).then();
|
|
@@ -207,20 +220,16 @@ function printUrls(port, proto) {
|
|
|
207
220
|
if (!port)
|
|
208
221
|
return;
|
|
209
222
|
for (const [name, nets] of Object.entries((0, os_1.networkInterfaces)())) {
|
|
210
|
-
if (!nets)
|
|
223
|
+
if (!nets || ignore.test(name))
|
|
211
224
|
continue;
|
|
212
|
-
|
|
213
|
-
if (!
|
|
225
|
+
lodash_1.default.remove(nets, 'internal');
|
|
226
|
+
if (!nets.length)
|
|
214
227
|
continue;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (address.includes(':'))
|
|
222
|
-
address = '[' + address + ']';
|
|
223
|
-
console.log('-', proto + '://' + address + appendPort);
|
|
224
|
-
}
|
|
228
|
+
const best = lodash_1.default.find(nets, { family: 'IPv4' }) || nets[0];
|
|
229
|
+
const appendPort = port === (proto === 'https' ? 443 : 80) ? '' : ':' + port;
|
|
230
|
+
let { address } = best;
|
|
231
|
+
if (address.includes(':'))
|
|
232
|
+
address = '[' + address + ']';
|
|
233
|
+
console.log('network', name, proto + '://' + address + appendPort);
|
|
225
234
|
}
|
|
226
235
|
}
|
package/src/plugins.js
CHANGED
|
@@ -31,7 +31,7 @@ exports.parsePluginSource = exports.rescan = exports.pluginsConfig = exports.ena
|
|
|
31
31
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
32
32
|
const watchLoad_1 = require("./watchLoad");
|
|
33
33
|
const lodash_1 = __importDefault(require("lodash"));
|
|
34
|
-
const path_1 =
|
|
34
|
+
const path_1 = __importDefault(require("path"));
|
|
35
35
|
const const_1 = require("./const");
|
|
36
36
|
const Const = __importStar(require("./const"));
|
|
37
37
|
const misc_1 = require("./misc");
|
|
@@ -66,8 +66,9 @@ function setPluginConfig(id, changes) {
|
|
|
66
66
|
}
|
|
67
67
|
exports.setPluginConfig = setPluginConfig;
|
|
68
68
|
function getPluginInfo(id) {
|
|
69
|
-
var _a
|
|
70
|
-
|
|
69
|
+
var _a;
|
|
70
|
+
const running = (_a = plugins[id]) === null || _a === void 0 ? void 0 : _a.getData();
|
|
71
|
+
return running && Object.assign(running, { id }) || availablePlugins[id];
|
|
71
72
|
}
|
|
72
73
|
exports.getPluginInfo = getPluginInfo;
|
|
73
74
|
function mapPlugins(cb) {
|
|
@@ -123,25 +124,14 @@ function pluginsMiddleware() {
|
|
|
123
124
|
exports.pluginsMiddleware = pluginsMiddleware;
|
|
124
125
|
class Plugin {
|
|
125
126
|
constructor(id, data, unwatch) {
|
|
126
|
-
var _a, _b;
|
|
127
127
|
this.id = id;
|
|
128
128
|
this.data = data;
|
|
129
129
|
this.unwatch = unwatch;
|
|
130
130
|
this.started = new Date();
|
|
131
131
|
if (!data)
|
|
132
132
|
throw 'invalid data';
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
(_b = (_a = old === null || old === void 0 ? void 0 : old.data) === null || _a === void 0 ? void 0 : _a.unload) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
137
|
-
} // we don't want all the effects of the Plugin.unload
|
|
138
|
-
catch (e) {
|
|
139
|
-
console.debug('error unloading plugin', id, String(e));
|
|
140
|
-
}
|
|
141
|
-
// track this
|
|
142
|
-
const wasStopped = availablePlugins[id];
|
|
143
|
-
if (wasStopped)
|
|
144
|
-
delete availablePlugins[id];
|
|
133
|
+
if (plugins[id])
|
|
134
|
+
throw "unload first: " + id;
|
|
145
135
|
plugins[id] = this;
|
|
146
136
|
this.data = data = { ...data }; // clone to make object modifiable. Objects coming from import are not.
|
|
147
137
|
// some validation
|
|
@@ -154,7 +144,6 @@ class Plugin {
|
|
|
154
144
|
console.warn('invalid', k);
|
|
155
145
|
}
|
|
156
146
|
}
|
|
157
|
-
events_1.default.emit(old || wasStopped ? 'pluginStarted' : 'pluginInstalled', this);
|
|
158
147
|
}
|
|
159
148
|
get middleware() {
|
|
160
149
|
var _a;
|
|
@@ -175,17 +164,19 @@ class Plugin {
|
|
|
175
164
|
getData() {
|
|
176
165
|
return { ...this.data };
|
|
177
166
|
}
|
|
178
|
-
async unload() {
|
|
167
|
+
async unload(reloading = false) {
|
|
179
168
|
var _a, _b;
|
|
180
169
|
const { id } = this;
|
|
181
|
-
console.log('unloading plugin', id);
|
|
182
170
|
try {
|
|
183
171
|
await ((_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.unload) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
172
|
+
console.log('unloaded plugin', id);
|
|
184
173
|
}
|
|
185
174
|
catch (e) {
|
|
186
|
-
console.
|
|
175
|
+
console.log('error unloading plugin', id, String(e));
|
|
187
176
|
}
|
|
188
177
|
delete plugins[id];
|
|
178
|
+
if (reloading)
|
|
179
|
+
return;
|
|
189
180
|
this.unwatch();
|
|
190
181
|
if (availablePlugins[id])
|
|
191
182
|
events_1.default.emit('pluginStopped', availablePlugins[id]);
|
|
@@ -213,8 +204,8 @@ async function rescan() {
|
|
|
213
204
|
console.debug('scanning plugins');
|
|
214
205
|
const found = [];
|
|
215
206
|
const foundDisabled = {};
|
|
216
|
-
const MASK =
|
|
217
|
-
for (const f of await (0, fast_glob_1.default)([(0,
|
|
207
|
+
const MASK = exports.PATH + '/*/plugin.js'; // be sure to not use path.join as fast-glob doesn't work with \
|
|
208
|
+
for (const f of await (0, fast_glob_1.default)([(0, misc_1.adjustStaticPathForGlob)(const_1.APP_PATH) + '/' + MASK, MASK])) {
|
|
218
209
|
const id = f.split('/').slice(-2)[0];
|
|
219
210
|
if (id.endsWith(exports.DISABLING_POSTFIX))
|
|
220
211
|
continue;
|
|
@@ -232,14 +223,16 @@ async function rescan() {
|
|
|
232
223
|
const module = path_1.default.resolve(f);
|
|
233
224
|
const { unwatch } = (0, watchLoad_1.watchLoad)(f, async () => {
|
|
234
225
|
try {
|
|
235
|
-
const
|
|
236
|
-
console.log(
|
|
226
|
+
const alreadyRunning = plugins[id];
|
|
227
|
+
console.log(alreadyRunning ? "reloading plugin" : "loading plugin", id);
|
|
237
228
|
const { init, ...data } = await Promise.resolve().then(() => __importStar(require(module)));
|
|
238
229
|
delete data.default;
|
|
239
230
|
deleteModule(require.resolve(module)); // avoid caching at next import
|
|
240
231
|
calculateBadApi(data);
|
|
241
232
|
if (data.badApi)
|
|
242
233
|
console.log("plugin", id, data.badApi);
|
|
234
|
+
await (alreadyRunning === null || alreadyRunning === void 0 ? void 0 : alreadyRunning.unload(true));
|
|
235
|
+
console.debug("starting plugin", id);
|
|
243
236
|
const res = await (init === null || init === void 0 ? void 0 : init.call(null, {
|
|
244
237
|
srcDir: __dirname,
|
|
245
238
|
const: Const,
|
|
@@ -256,16 +249,28 @@ async function rescan() {
|
|
|
256
249
|
cb(last);
|
|
257
250
|
return exports.pluginsConfig.sub(() => {
|
|
258
251
|
const now = this.getConfig(cfgKey);
|
|
259
|
-
if (
|
|
252
|
+
if ((0, misc_1.same)(now, last))
|
|
253
|
+
return;
|
|
254
|
+
try {
|
|
260
255
|
cb(last = now);
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
console.log('plugin', id, String(e));
|
|
259
|
+
}
|
|
261
260
|
});
|
|
262
261
|
},
|
|
263
262
|
getHfsConfig: config_1.getConfig,
|
|
264
263
|
}));
|
|
265
264
|
Object.assign(data, res);
|
|
266
|
-
new Plugin(id, data, unwatch);
|
|
267
|
-
if (
|
|
268
|
-
events_1.default.emit('pluginUpdated', getPluginInfo(id));
|
|
265
|
+
const plugin = new Plugin(id, data, unwatch);
|
|
266
|
+
if (alreadyRunning)
|
|
267
|
+
events_1.default.emit('pluginUpdated', Object.assign(lodash_1.default.pick(plugin, 'started'), getPluginInfo(id)));
|
|
268
|
+
else {
|
|
269
|
+
const wasInstalled = availablePlugins[id];
|
|
270
|
+
if (wasInstalled)
|
|
271
|
+
delete availablePlugins[id];
|
|
272
|
+
events_1.default.emit(wasInstalled ? 'pluginStarted' : 'pluginInstalled', plugin);
|
|
273
|
+
}
|
|
269
274
|
}
|
|
270
275
|
catch (e) {
|
|
271
276
|
console.log("plugin error:", e);
|
package/src/util-files.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.unzip = exports.run = exports.dirStream = exports.isWindowsDrive = exports.dirTraversal = exports.watchDir = exports.readFileBusy = exports.isFile = exports.isDirectory = void 0;
|
|
6
|
+
exports.unzip = exports.run = exports.dirStream = exports.adjustStaticPathForGlob = exports.isWindowsDrive = exports.dirTraversal = exports.watchDir = exports.readFileBusy = exports.isFile = exports.isDirectory = void 0;
|
|
7
7
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
8
|
const misc_1 = require("./misc");
|
|
9
9
|
const fs_1 = require("fs");
|
|
@@ -86,23 +86,30 @@ function isWindowsDrive(s) {
|
|
|
86
86
|
return s && /^[a-zA-Z]:$/.test(s);
|
|
87
87
|
}
|
|
88
88
|
exports.isWindowsDrive = isWindowsDrive;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
// apply this to paths that may contain \ as separator (not supported by fast-glob) and other special chars to be escaped (parenthesis)
|
|
90
|
+
function adjustStaticPathForGlob(path) {
|
|
91
|
+
return fast_glob_1.default.escapePath(path.replace(/\\/g, '/'));
|
|
92
|
+
}
|
|
93
|
+
exports.adjustStaticPathForGlob = adjustStaticPathForGlob;
|
|
94
|
+
async function* dirStream(path, deep) {
|
|
95
|
+
if (!await isDirectory(path))
|
|
92
96
|
throw Error('ENOTDIR');
|
|
93
|
-
const dirStream = fast_glob_1.default.stream('*', {
|
|
97
|
+
const dirStream = fast_glob_1.default.stream(deep ? '**/*' : '*', {
|
|
94
98
|
cwd: path,
|
|
95
99
|
dot: true,
|
|
100
|
+
deep,
|
|
96
101
|
onlyFiles: false,
|
|
97
102
|
suppressErrors: true,
|
|
103
|
+
objectMode: true,
|
|
98
104
|
});
|
|
99
105
|
const skip = await getItemsToSkip(path);
|
|
100
|
-
for await (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (skip === null || skip === void 0 ? void 0 : skip.includes(path))
|
|
106
|
+
for await (const entry of dirStream) {
|
|
107
|
+
let { path, dirent } = entry;
|
|
108
|
+
if (!dirent.isDirectory() && !dirent.isFile())
|
|
104
109
|
continue;
|
|
105
|
-
|
|
110
|
+
path = String(path);
|
|
111
|
+
if (!(skip === null || skip === void 0 ? void 0 : skip.includes(path)))
|
|
112
|
+
yield path;
|
|
106
113
|
}
|
|
107
114
|
async function getItemsToSkip(path) {
|
|
108
115
|
if (!const_1.IS_WINDOWS)
|
package/src/vfs.js
CHANGED
|
@@ -114,7 +114,19 @@ function hasPermission(node, perm, ctx) {
|
|
|
114
114
|
&& (perm !== 'can_see' || hasPermission(node, 'can_read', ctx)); // for can_see you must also can_read
|
|
115
115
|
}
|
|
116
116
|
exports.hasPermission = hasPermission;
|
|
117
|
+
/* redev: 1322k716 8.6s
|
|
118
|
+
-temp 6.43s
|
|
119
|
+
*/
|
|
120
|
+
setTimeout(async () => {
|
|
121
|
+
let n = 0;
|
|
122
|
+
console.time('asd');
|
|
123
|
+
for await (const x of walkNode({ source: '/Users/rejetto/redev' }, undefined, Infinity))
|
|
124
|
+
++n;
|
|
125
|
+
console.timeEnd('asd');
|
|
126
|
+
console.log({ n });
|
|
127
|
+
}, 3000);
|
|
117
128
|
async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
|
|
129
|
+
var _a;
|
|
118
130
|
const { children, source } = parent;
|
|
119
131
|
if (children)
|
|
120
132
|
for (let idx = 0; idx < children.length; idx++) {
|
|
@@ -122,60 +134,58 @@ async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
|
|
|
122
134
|
yield* workItem({
|
|
123
135
|
...child,
|
|
124
136
|
name: prefixPath ? (prefixPath + getNodeName(child)) : child.name
|
|
125
|
-
});
|
|
137
|
+
}, depth > 0 && await nodeIsDirectory(child).catch(() => false));
|
|
126
138
|
}
|
|
127
139
|
if (!source)
|
|
128
140
|
return;
|
|
129
141
|
try {
|
|
130
|
-
for await (const path of (0, misc_1.dirStream)(source)) {
|
|
131
|
-
if (ctx.req.aborted)
|
|
142
|
+
for await (const path of (0, misc_1.dirStream)(source, depth)) {
|
|
143
|
+
if (ctx === null || ctx === void 0 ? void 0 : ctx.req.aborted)
|
|
132
144
|
return;
|
|
133
|
-
|
|
134
|
-
const renamed = rename === null || rename === void 0 ? void 0 : rename[path];
|
|
145
|
+
const renamed = (_a = parent.rename) === null || _a === void 0 ? void 0 : _a[path];
|
|
135
146
|
yield* workItem({
|
|
136
|
-
name:
|
|
147
|
+
name: prefixPath + (renamed || path),
|
|
137
148
|
source: (0, path_1.join)(source, path),
|
|
138
|
-
rename: renameUnderPath(rename, path),
|
|
149
|
+
rename: renameUnderPath(parent.rename, path),
|
|
139
150
|
});
|
|
140
151
|
}
|
|
141
152
|
}
|
|
142
153
|
catch (e) {
|
|
143
154
|
console.debug('glob', source, e); // ENOTDIR, or lacking permissions
|
|
144
155
|
}
|
|
145
|
-
|
|
156
|
+
// item will be changed, so be sure to pass a temp node
|
|
157
|
+
async function* workItem(item, recur = false) {
|
|
158
|
+
const name = getNodeName(item);
|
|
146
159
|
// we basename for depth>0 where we already have the rest of the path in the parent's url, and would be duplicated
|
|
147
|
-
const
|
|
148
|
-
const url = (0, misc_1.enforceFinal)('/', parent.url || '') +
|
|
149
|
-
|
|
150
|
-
...item,
|
|
160
|
+
const virtualBasename = (0, path_1.basename)(name);
|
|
161
|
+
const url = (0, misc_1.enforceFinal)('/', parent.url || '') + virtualBasename;
|
|
162
|
+
Object.assign(item, {
|
|
151
163
|
isTemp: true,
|
|
152
164
|
url,
|
|
153
165
|
parents: [...parent.parents || [], parent],
|
|
154
166
|
});
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
inheritFromParent(parent, item);
|
|
168
|
+
applyMasks(item, parent, virtualBasename);
|
|
169
|
+
if (ctx && !hasPermission(item, 'can_see', ctx))
|
|
157
170
|
return;
|
|
158
|
-
yield
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
yield* walkNode(temp, ctx, depth - 1, getNodeName(temp) + '/');
|
|
164
|
-
}
|
|
165
|
-
catch (_a) { } // stat failed in nodeIsDirectory, ignore
|
|
171
|
+
yield item;
|
|
172
|
+
if (!recur)
|
|
173
|
+
return;
|
|
174
|
+
inheritMasks(item, parent, virtualBasename);
|
|
175
|
+
yield* walkNode(item, ctx, depth - 1, name + '/');
|
|
166
176
|
}
|
|
167
177
|
}
|
|
168
178
|
exports.walkNode = walkNode;
|
|
169
|
-
function applyMasks(item, parent,
|
|
179
|
+
function applyMasks(item, parent, virtualBasename) {
|
|
170
180
|
const { masks } = parent;
|
|
171
181
|
if (!masks)
|
|
172
182
|
return;
|
|
173
183
|
for (const k in masks)
|
|
174
|
-
if (k.startsWith('**/') && (0, micromatch_1.isMatch)(
|
|
175
|
-
|| !k.includes('/') && (0, micromatch_1.isMatch)(
|
|
184
|
+
if (k.startsWith('**/') && (0, micromatch_1.isMatch)(virtualBasename, k.slice(3))
|
|
185
|
+
|| !k.includes('/') && (0, micromatch_1.isMatch)(virtualBasename, k))
|
|
176
186
|
Object.assign(item, masks[k]);
|
|
177
187
|
}
|
|
178
|
-
function inheritMasks(item, parent,
|
|
188
|
+
function inheritMasks(item, parent, virtualBasename) {
|
|
179
189
|
const { masks } = parent;
|
|
180
190
|
if (!masks)
|
|
181
191
|
return;
|
|
@@ -183,8 +193,8 @@ function inheritMasks(item, parent, name) {
|
|
|
183
193
|
for (const k in masks)
|
|
184
194
|
if (k.startsWith('**/'))
|
|
185
195
|
o[k.slice(3)] = masks[k];
|
|
186
|
-
else if (k.startsWith(
|
|
187
|
-
o[k.slice(
|
|
196
|
+
else if (k.startsWith(virtualBasename + '/'))
|
|
197
|
+
o[k.slice(virtualBasename.length + 1)] = masks[k];
|
|
188
198
|
if (Object.keys(o).length)
|
|
189
199
|
item.masks = o;
|
|
190
200
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg: #fff;--text: #555;--faint-contrast: #0002;--mild-contrast: #0005;--good-contrast: #000a;--button-bg: #68a;--button-text: #fff;--focus-color: #468}:root .theme-dark{--bg: #000;--text: #999;--faint-contrast: #fff2;--mild-contrast: #fff5;--good-contrast: #fffa;--button-bg: #345;--button-text: #999}:root .theme-dark a{color:#8ac}:root .theme-dark .dialog-closer{background:#633}:root .theme-dark .dialog-icon{color:#ccc}:root .theme-dark .dialog-backdrop{background:rgba(51,51,51,.7333333333)}body{background:var(--bg);margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,button,select,input{font-size:12pt}#root{max-width:50em;margin:auto;min-height:100vh;display:flex;flex-direction:column}body,input{color:var(--text)}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}input,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}input[type=checkbox]{margin:0 1.3em 0 .8em;transform:scale(1.7);accent-color:var(--button-bg)}.hidden{display:none!important}.icon{font-size:1.2em}.icon.mirror:before{transform:scaleX(-1)}a{text-decoration:none;color:#57a}button{background-color:var(--button-bg);color:var(--button-text);padding:.5em 1em;border:transparent;text-decoration:none;border-radius:.3em;vertical-align:middle;cursor:pointer}button.toggled{opacity:.6}button:focus-visible,.breadcrumb:focus-visible{outline:3px solid var(--focus-color)}input:focus-visible,select:focus-visible,ul a:focus-visible{border-radius:.3em;border-color:transparent;outline:2px solid var(--focus-color)}.error-msg{background-color:#faa;color:#833}header{position:sticky;top:0;background:var(--bg);padding:.2em;z-index:1}.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}.icon.emoji.spinner{display:inline-block}.breadcrumb{padding:.1em .6em .2em;line-height:1.8em;border-radius:.7em;background-color:var(--button-bg);color:var(--button-text);border-top:1px solid #666;margin-right:-.1em}.breadcrumb:nth-child(-n+3) .icon{padding:0 .2em}#folder-stats{font-size:90%;margin:.4em 0 0 .5em;float:right}#folder-stats .icon{margin-right:.3em}header input{width:100%;margin:.2em auto;box-sizing:border-box}#filter-bar{display:flex;gap:.3em;margin:.5em 0}#filter-bar input{flex:1}#filter-bar button{padding:0 .5em}ul.dir{flex:1;padding:0;margin:0;clear:both}ul.dir li{display:block;list-style-type:none;margin-bottom:.3em;padding:.3em;border-top:1px solid var(--button-bg)}ul.dir li a{word-break:break-word;padding-right:.3em}ul.dir li a .icon{margin-right:.3em}ul.dir li a.container-folder:hover{text-decoration:underline}ul.dir li .entry-props{float:right;font-size:90%;margin-left:12px;margin-top:.2em}ul.dir li .entry-props .icon{margin:0 .3em}ul.dir li .entry-props .entry-size{display:inline-block}#menu-panel{margin-bottom:.2em}#menu-bar{display:flex;justify-content:space-evenly;flex-wrap:wrap}#menu-bar>*{flex:auto;margin:.1em}#menu-bar button{padding-left:0;padding-right:0}#menu-bar>a>button{width:100%}#searched{margin:.2em}#user-panel{display:flex;flex-direction:column;gap:1em}#user-panel a>button{width:100%}button label{cursor:inherit;margin-left:.5em}.dialog-backdrop.working{font-size:5em;animation:1s fade-in}.dialog-content{padding:.2em}.dialog-confirm,.dialog-prompt{--color: var(--button-bg)}#paging{display:flex;position:sticky;bottom:0;background:var(--bg);gap:.5em;overflow-x:auto}#paging>button{flex:1;background:var(--button-bg);text-align:center}*{scrollbar-width:thin;scrollbar-color:var(--button-bg) var(--faint-contrast)}*::-webkit-scrollbar{width:12px}*::-webkit-scrollbar-track{background:var(--faint-contrast)}*::-webkit-scrollbar-thumb{background-color:var(--button-bg);border-radius:20px;border:1px solid var(--faint-contrast)}@media (max-width: 42em){body,button,select{font-size:14pt}#menu-bar button label{display:none}#filter-bar{margin:.2em 0}#filter-bar label{display:none}#filter-bar button{width:17.6vw;height:2.3em}.breadcrumb{word-break:break-all}.breadcrumb .icon{font-size:24px}}.dialog-backdrop{position:fixed;inset:0;background:#888a;display:flex;justify-content:center;align-items:center;z-index:1000}.dialog{background:#fff;background:var(--bg);padding:max(.5em,1vw);border-radius:1em;position:relative;margin:0 3vw;overflow:hidden;max-height:calc(100vh - 2em)}.dialog-icon{color:#fff;background-color:var(--color);position:absolute;top:0;width:1.8em;height:1.7em;text-align:center;border-radius:.8em 0}.dialog-closer{border-radius:0 .8em;right:0;padding:0;background-color:#c99}.dialog-icon~.dialog-content{margin-top:1.3em}.dialog-type{left:0;top:0;overflow:hidden;line-height:1.7em}.dialog-content{overflow:auto;max-height:calc(100vh - 4em)}.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: 50em){.dialog-closer{font-size:120%}.dialog-icon~.dialog-content{margin-top:2em}}
|