hfs 0.26.8 → 0.26.9
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/admin/assets/index.bb5198ec.js +281 -0
- package/admin/assets/index.dcc78777.css +1 -0
- package/admin/assets/sha512.9dfe82e1.js +8 -0
- package/admin/index.html +3 -1
- package/admin/{public/logo.svg → logo.svg} +0 -0
- package/frontend/assets/index.27a78796.js +85 -0
- package/frontend/assets/index.93366732.css +1 -0
- package/frontend/assets/sha512.6af42937.js +8 -0
- package/frontend/{public/fontello.css → fontello.css} +0 -0
- package/frontend/{public/fontello.woff2 → fontello.woff2} +0 -0
- package/frontend/index.html +3 -1
- package/package.json +1 -1
- package/src/QuickZipStream.js +285 -0
- package/src/ThrottledStream.js +93 -0
- package/src/adminApis.js +169 -0
- package/src/api.accounts.js +59 -0
- package/src/api.auth.js +128 -0
- package/src/api.file_list.js +103 -0
- package/src/api.helpers.js +32 -0
- package/src/api.monitor.js +102 -0
- package/src/api.plugins.js +127 -0
- package/src/api.vfs.js +164 -0
- package/src/apiMiddleware.js +120 -0
- package/src/block.js +33 -0
- package/src/commands.js +124 -0
- package/src/config.js +168 -0
- package/src/connections.js +57 -0
- package/src/const.js +83 -0
- package/src/crypt.js +21 -0
- package/src/debounceAsync.js +48 -0
- package/src/events.js +9 -0
- package/src/frontEndApis.js +38 -0
- package/src/github.js +102 -0
- package/src/index.js +56 -0
- package/src/listen.js +235 -0
- package/src/log.js +137 -0
- package/src/middlewares.js +175 -0
- package/src/misc.js +160 -0
- package/src/pbkdf2.js +74 -0
- package/src/perm.js +181 -0
- package/src/plugins.js +343 -0
- package/src/serveFile.js +105 -0
- package/src/serveGuiFiles.js +113 -0
- package/src/sse.js +29 -0
- package/src/throttler.js +91 -0
- package/src/update.js +69 -0
- package/src/util-files.js +148 -0
- package/src/util-generators.js +30 -0
- package/src/util-http.js +30 -0
- package/src/vfs.js +230 -0
- package/src/watchLoad.js +73 -0
- package/src/zip.js +72 -0
- package/admin/.DS_Store +0 -0
- package/admin/.eslintrc +0 -8
- package/admin/.gitignore +0 -23
- package/admin/package.json +0 -67
- package/admin/src/AccountForm.ts +0 -92
- package/admin/src/AccountsPage.ts +0 -143
- package/admin/src/App.ts +0 -83
- package/admin/src/ArrayField.ts +0 -84
- package/admin/src/ConfigPage.ts +0 -279
- package/admin/src/FileField.ts +0 -52
- package/admin/src/FileForm.ts +0 -148
- package/admin/src/FilePicker.ts +0 -166
- package/admin/src/HomePage.ts +0 -96
- package/admin/src/InstalledPlugins.ts +0 -158
- package/admin/src/LoginRequired.ts +0 -75
- package/admin/src/LogoutPage.ts +0 -27
- package/admin/src/LogsPage.ts +0 -75
- package/admin/src/MainMenu.ts +0 -74
- package/admin/src/MenuButton.ts +0 -38
- package/admin/src/MonitorPage.ts +0 -200
- package/admin/src/OnlinePlugins.ts +0 -101
- package/admin/src/PermField.ts +0 -80
- package/admin/src/PluginsPage.ts +0 -27
- package/admin/src/VfsMenuBar.ts +0 -58
- package/admin/src/VfsPage.ts +0 -124
- package/admin/src/VfsTree.ts +0 -95
- package/admin/src/addFiles.ts +0 -59
- package/admin/src/api.ts +0 -246
- package/admin/src/dialog.ts +0 -203
- package/admin/src/index.css +0 -21
- package/admin/src/index.ts +0 -10
- package/admin/src/md.ts +0 -31
- package/admin/src/misc.ts +0 -141
- package/admin/src/react-app-env.d.ts +0 -1
- package/admin/src/reportWebVitals.ts +0 -15
- package/admin/src/setupTests.ts +0 -5
- package/admin/src/state.ts +0 -40
- package/admin/src/theme.ts +0 -37
- package/admin/tsconfig.json +0 -26
- package/admin/vite.config.ts +0 -32
- package/frontend/.DS_Store +0 -0
- package/frontend/.eslintrc +0 -8
- package/frontend/.gitignore +0 -23
- package/frontend/package.json +0 -51
- package/frontend/src/App.ts +0 -25
- package/frontend/src/Breadcrumbs.ts +0 -43
- package/frontend/src/BrowseFiles.ts +0 -141
- package/frontend/src/Head.ts +0 -45
- package/frontend/src/UserPanel.ts +0 -52
- package/frontend/src/api.ts +0 -78
- package/frontend/src/components.ts +0 -54
- package/frontend/src/dialog.css +0 -76
- package/frontend/src/dialog.ts +0 -105
- package/frontend/src/icons.ts +0 -46
- package/frontend/src/index.scss +0 -307
- package/frontend/src/index.ts +0 -10
- package/frontend/src/login.ts +0 -50
- package/frontend/src/menu.ts +0 -188
- package/frontend/src/misc.ts +0 -54
- package/frontend/src/options.ts +0 -52
- package/frontend/src/react-app-env.d.ts +0 -1
- package/frontend/src/reportWebVitals.ts +0 -15
- package/frontend/src/setupTests.ts +0 -5
- package/frontend/src/state.ts +0 -82
- package/frontend/src/useAuthorized.ts +0 -17
- package/frontend/src/useFetchList.ts +0 -144
- package/frontend/src/useTheme.ts +0 -23
- package/frontend/tsconfig.json +0 -26
- package/frontend/vite.config.ts +0 -21
- package/src/QuickZipStream.ts +0 -279
- package/src/ThrottledStream.ts +0 -98
- package/src/adminApis.ts +0 -161
- package/src/api.accounts.ts +0 -78
- package/src/api.auth.ts +0 -131
- package/src/api.file_list.ts +0 -102
- package/src/api.helpers.ts +0 -30
- package/src/api.monitor.ts +0 -106
- package/src/api.plugins.ts +0 -139
- package/src/api.vfs.ts +0 -182
- package/src/apiMiddleware.ts +0 -124
- package/src/block.ts +0 -35
- package/src/commands.ts +0 -122
- package/src/config.ts +0 -166
- package/src/connections.ts +0 -60
- package/src/const.ts +0 -57
- package/src/crypt.ts +0 -16
- package/src/debounceAsync.ts +0 -51
- package/src/events.ts +0 -6
- package/src/frontEndApis.ts +0 -17
- package/src/github.ts +0 -102
- package/src/index.ts +0 -53
- package/src/listen.ts +0 -220
- package/src/log.ts +0 -128
- package/src/middlewares.ts +0 -176
- package/src/misc.ts +0 -149
- package/src/pbkdf2.ts +0 -83
- package/src/perm.ts +0 -194
- package/src/plugins.ts +0 -342
- package/src/serveFile.ts +0 -104
- package/src/serveGuiFiles.ts +0 -95
- package/src/sse.ts +0 -29
- package/src/throttler.ts +0 -106
- package/src/update.ts +0 -67
- package/src/util-files.ts +0 -137
- package/src/util-generators.ts +0 -29
- package/src/util-http.ts +0 -29
- package/src/vfs.ts +0 -258
- package/src/watchLoad.ts +0 -75
- package/src/zip.ts +0 -69
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.serveGuiFiles = void 0;
|
|
31
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
32
|
+
const const_1 = require("./const");
|
|
33
|
+
const serveFile_1 = require("./serveFile");
|
|
34
|
+
const plugins_1 = require("./plugins");
|
|
35
|
+
const api_auth_1 = require("./api.auth");
|
|
36
|
+
const apiMiddleware_1 = require("./apiMiddleware");
|
|
37
|
+
const path_1 = require("path");
|
|
38
|
+
const misc_1 = require("./misc");
|
|
39
|
+
// in case of dev env we have our static files within the 'dist' folder'
|
|
40
|
+
const DEV_STATIC = process.env.DEV ? 'dist/' : '';
|
|
41
|
+
function serveStatic(uri) {
|
|
42
|
+
const folder = uri.slice(2, -1); // we know folder is very similar to uri
|
|
43
|
+
const cache = {};
|
|
44
|
+
return async (ctx, next) => {
|
|
45
|
+
if (ctx.method === 'OPTIONS') {
|
|
46
|
+
ctx.status = const_1.NO_CONTENT;
|
|
47
|
+
ctx.set({ Allow: 'OPTIONS, GET' });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (ctx.method !== 'GET')
|
|
51
|
+
return ctx.status = const_1.METHOD_NOT_ALLOWED;
|
|
52
|
+
const serveApp = shouldServeApp(ctx);
|
|
53
|
+
const fullPath = (0, path_1.join)(__dirname, '..', DEV_STATIC, folder, serveApp ? '/index.html' : ctx.path);
|
|
54
|
+
const content = await (0, misc_1.getOrSet)(cache, ctx.path, async () => {
|
|
55
|
+
const data = await promises_1.default.readFile(fullPath).catch(() => null);
|
|
56
|
+
return serveApp || !data ? data
|
|
57
|
+
: adjustBundlerLinks(ctx.path, uri, data);
|
|
58
|
+
});
|
|
59
|
+
if (content === null)
|
|
60
|
+
return ctx.status = 404;
|
|
61
|
+
if (!serveApp)
|
|
62
|
+
return (0, serveFile_1.serveFile)(fullPath, 'auto', content)(ctx, next);
|
|
63
|
+
// we don't cache the index as it's small and may prevent plugins change to apply
|
|
64
|
+
ctx.body = await treatIndex(ctx, String(content), uri);
|
|
65
|
+
ctx.type = 'html';
|
|
66
|
+
ctx.set('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function shouldServeApp(ctx) {
|
|
70
|
+
var _a;
|
|
71
|
+
return (_a = ctx.state).serveApp || (_a.serveApp = ctx.path.endsWith('/'));
|
|
72
|
+
}
|
|
73
|
+
function adjustBundlerLinks(path, uri, data) {
|
|
74
|
+
const ext = (0, path_1.extname)(path);
|
|
75
|
+
return ext && !ext.match(/\.(css|html|js|ts|scss)/) ? data
|
|
76
|
+
: String(data).replace(/((?:import | from )['"])\//g, `$1${uri}`);
|
|
77
|
+
}
|
|
78
|
+
async function treatIndex(ctx, body, filesUri) {
|
|
79
|
+
const session = await (0, api_auth_1.refresh_session)({}, ctx);
|
|
80
|
+
ctx.set('etag', '');
|
|
81
|
+
return body
|
|
82
|
+
.replace(/((?:src|href) *= *['"])\/?(?![a-z]+:\/\/)/g, '$1' + filesUri)
|
|
83
|
+
.replace('_HFS_SESSION_', session instanceof apiMiddleware_1.ApiError ? 'null' : JSON.stringify(session))
|
|
84
|
+
// replacing this text allow us to avoid injecting in frontends that don't support plugins. Don't use a <--comment--> or it will be removed by webpack
|
|
85
|
+
.replace('_HFS_PLUGINS_', pluginsInjection);
|
|
86
|
+
}
|
|
87
|
+
function serveProxied(port, uri) {
|
|
88
|
+
if (!port)
|
|
89
|
+
return;
|
|
90
|
+
console.debug('proxied on port', port);
|
|
91
|
+
let proxy;
|
|
92
|
+
Promise.resolve().then(() => __importStar(require('koa-better-http-proxy'))).then(lib => // dynamic import to avoid having this in final distribution
|
|
93
|
+
proxy = lib.default('127.0.0.1:' + port, {
|
|
94
|
+
proxyReqPathResolver: (ctx) => shouldServeApp(ctx) ? '/' : ctx.path,
|
|
95
|
+
userResDecorator(res, data, ctx) {
|
|
96
|
+
return shouldServeApp(ctx) ? treatIndex(ctx, String(data), uri)
|
|
97
|
+
: adjustBundlerLinks(ctx.path, uri, data);
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
100
|
+
return function () {
|
|
101
|
+
return proxy.apply(this, arguments);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function pluginsInjection() {
|
|
105
|
+
const css = (0, plugins_1.mapPlugins)((plug, k) => { var _a; return (_a = plug.frontend_css) === null || _a === void 0 ? void 0 : _a.map(f => const_1.PLUGINS_PUB_URI + k + '/' + f); }).flat().filter(Boolean);
|
|
106
|
+
const js = (0, plugins_1.mapPlugins)((plug, k) => { var _a; return (_a = plug.frontend_js) === null || _a === void 0 ? void 0 : _a.map(f => const_1.PLUGINS_PUB_URI + k + '/' + f); }).flat().filter(Boolean);
|
|
107
|
+
return css.map(uri => `\n<link rel='stylesheet' type='text/css' href='${uri}'/>`).join('')
|
|
108
|
+
+ js.map(uri => `\n<script defer src='${uri}'></script>`).join('');
|
|
109
|
+
}
|
|
110
|
+
function serveGuiFiles(proxyPort, uri) {
|
|
111
|
+
return serveProxied(proxyPort, uri) || serveStatic(uri);
|
|
112
|
+
}
|
|
113
|
+
exports.serveGuiFiles = serveGuiFiles;
|
package/src/sse.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const stream_1 = require("stream");
|
|
5
|
+
function createSSE(ctx) {
|
|
6
|
+
const { socket } = ctx.req;
|
|
7
|
+
socket.setTimeout(0);
|
|
8
|
+
socket.setNoDelay(true);
|
|
9
|
+
socket.setKeepAlive(true);
|
|
10
|
+
ctx.set({
|
|
11
|
+
'Content-Type': 'text/event-stream',
|
|
12
|
+
'Cache-Control': 'no-cache',
|
|
13
|
+
'Connection': 'keep-alive',
|
|
14
|
+
'X-Accel-Buffering': 'no', // avoid buffering when reverse-proxied through nginx
|
|
15
|
+
});
|
|
16
|
+
ctx.status = 200;
|
|
17
|
+
return ctx.body = new stream_1.Transform({
|
|
18
|
+
objectMode: true,
|
|
19
|
+
transform(chunk, encoding, cb) {
|
|
20
|
+
this.push(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
21
|
+
cb();
|
|
22
|
+
},
|
|
23
|
+
flush(cb) {
|
|
24
|
+
this.push('data:\n\n');
|
|
25
|
+
cb();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
exports.default = createSSE;
|
package/src/throttler.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.totalInSpeed = exports.totalOutSpeed = exports.totalGot = exports.totalSent = exports.throttler = void 0;
|
|
8
|
+
const stream_1 = require("stream");
|
|
9
|
+
const ThrottledStream_1 = require("./ThrottledStream");
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
const misc_1 = require("./misc");
|
|
12
|
+
const connections_1 = require("./connections");
|
|
13
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
14
|
+
const events_1 = __importDefault(require("./events"));
|
|
15
|
+
const mainThrottleGroup = new ThrottledStream_1.ThrottleGroup(Infinity);
|
|
16
|
+
(0, config_1.defineConfig)('max_kbps', Infinity).sub(v => mainThrottleGroup.updateLimit(v));
|
|
17
|
+
const ip2group = {};
|
|
18
|
+
const SymThrStr = Symbol('stream');
|
|
19
|
+
const SymTimeout = Symbol('timeout');
|
|
20
|
+
const maxKbpsPerIp = (0, config_1.defineConfig)('max_kbps_per_ip', Infinity);
|
|
21
|
+
const throttler = async (ctx, next) => {
|
|
22
|
+
await next();
|
|
23
|
+
const { body } = ctx;
|
|
24
|
+
if (!body || !(body instanceof stream_1.Readable))
|
|
25
|
+
return;
|
|
26
|
+
// we wrap the stream also for unlimited connections to get speed and other features
|
|
27
|
+
const ipGroup = (0, misc_1.getOrSet)(ip2group, ctx.ip, () => {
|
|
28
|
+
var _a;
|
|
29
|
+
const doLimit = ((_a = ctx.state.account) === null || _a === void 0 ? void 0 : _a.ignore_limits) || (0, misc_1.isLocalHost)(ctx) ? undefined : true;
|
|
30
|
+
const group = new ThrottledStream_1.ThrottleGroup(Infinity, doLimit && mainThrottleGroup);
|
|
31
|
+
const unsub = doLimit && maxKbpsPerIp.sub(v => group.updateLimit(v));
|
|
32
|
+
return { group, count: 0, destroy: unsub };
|
|
33
|
+
});
|
|
34
|
+
const conn = ctx.state.connection;
|
|
35
|
+
if (!conn)
|
|
36
|
+
throw 'assert throttler connection';
|
|
37
|
+
const ts = conn[SymThrStr] = new ThrottledStream_1.ThrottledStream(ipGroup.group, conn[SymThrStr]);
|
|
38
|
+
let closed = false;
|
|
39
|
+
const DELAY = 1000;
|
|
40
|
+
const update = lodash_1.default.debounce(() => {
|
|
41
|
+
const ts = conn[SymThrStr];
|
|
42
|
+
const outSpeed = roundKb(ts.getSpeed());
|
|
43
|
+
(0, connections_1.updateConnection)(conn, { outSpeed, sent: ts.getBytesSent() });
|
|
44
|
+
/* in case this stream stands still for a while (before the end), we'll have neither 'sent' or 'close' events,
|
|
45
|
+
* so who will take care to updateConnection? This artificial next-call will ensure just that */
|
|
46
|
+
clearTimeout(conn[SymTimeout]);
|
|
47
|
+
if (outSpeed || !closed)
|
|
48
|
+
conn[SymTimeout] = setTimeout(update, DELAY);
|
|
49
|
+
}, DELAY, { maxWait: DELAY });
|
|
50
|
+
ts.on('sent', (n) => {
|
|
51
|
+
exports.totalSent += n;
|
|
52
|
+
update();
|
|
53
|
+
});
|
|
54
|
+
++ipGroup.count;
|
|
55
|
+
ts.on('close', () => {
|
|
56
|
+
var _a;
|
|
57
|
+
update.flush();
|
|
58
|
+
closed = true;
|
|
59
|
+
if (--ipGroup.count)
|
|
60
|
+
return; // any left?
|
|
61
|
+
(_a = ipGroup.destroy) === null || _a === void 0 ? void 0 : _a.call(ipGroup);
|
|
62
|
+
delete ip2group[ctx.ip];
|
|
63
|
+
});
|
|
64
|
+
const bak = ctx.response.length; // preserve
|
|
65
|
+
ctx.body = ctx.body.pipe(ts);
|
|
66
|
+
if (bak)
|
|
67
|
+
ctx.response.length = bak;
|
|
68
|
+
};
|
|
69
|
+
exports.throttler = throttler;
|
|
70
|
+
function roundKb(n) {
|
|
71
|
+
return lodash_1.default.round(n, 1) || lodash_1.default.round(n, 3); // further precision if necessary
|
|
72
|
+
}
|
|
73
|
+
exports.totalSent = 0;
|
|
74
|
+
exports.totalGot = 0;
|
|
75
|
+
exports.totalOutSpeed = 0;
|
|
76
|
+
exports.totalInSpeed = 0;
|
|
77
|
+
let lastSent = exports.totalSent;
|
|
78
|
+
let lastGot = exports.totalGot;
|
|
79
|
+
let last = Date.now();
|
|
80
|
+
setInterval(() => {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
const past = (now - last) / 1000; // seconds
|
|
83
|
+
last = now;
|
|
84
|
+
const deltaSentKb = (exports.totalSent - lastSent) / 1000;
|
|
85
|
+
lastSent = exports.totalSent;
|
|
86
|
+
const deltaGotKb = (exports.totalGot - lastGot) / 1000;
|
|
87
|
+
lastGot = exports.totalGot;
|
|
88
|
+
exports.totalOutSpeed = roundKb(deltaSentKb / past);
|
|
89
|
+
exports.totalInSpeed = roundKb(deltaGotKb / past);
|
|
90
|
+
}, 1000);
|
|
91
|
+
events_1.default.on('connection', (c) => c.socket.on('data', data => exports.totalGot += data.length));
|
package/src/update.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.update = exports.getUpdate = void 0;
|
|
4
|
+
const github_1 = require("./github");
|
|
5
|
+
const const_1 = require("./const");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const misc_1 = require("./misc");
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const plugins_1 = require("./plugins");
|
|
11
|
+
const promises_1 = require("fs/promises");
|
|
12
|
+
const HFS_REPO = 'rejetto/hfs';
|
|
13
|
+
async function getUpdate() {
|
|
14
|
+
const [latest] = await (0, github_1.getRepoInfo)(HFS_REPO + '/releases?per_page=1');
|
|
15
|
+
if (latest.name === const_1.VERSION)
|
|
16
|
+
throw "you already have the latest version: " + const_1.VERSION;
|
|
17
|
+
return latest;
|
|
18
|
+
}
|
|
19
|
+
exports.getUpdate = getUpdate;
|
|
20
|
+
async function update() {
|
|
21
|
+
if (process.argv0.endsWith('node'))
|
|
22
|
+
throw "only binary versions are supported for now";
|
|
23
|
+
const update = await getUpdate();
|
|
24
|
+
const assetSearch = { win32: 'windows', darwin: 'mac', linux: 'linux' }[process.platform];
|
|
25
|
+
if (!assetSearch)
|
|
26
|
+
throw "this feature doesn't support your platform: " + process.platform;
|
|
27
|
+
const asset = update.assets.find((x) => x.name.endsWith('.zip') && x.name.includes(assetSearch));
|
|
28
|
+
if (!asset)
|
|
29
|
+
throw "asset not found";
|
|
30
|
+
const url = asset.browser_download_url;
|
|
31
|
+
console.log("downloading", url);
|
|
32
|
+
const bin = process.argv[0];
|
|
33
|
+
const binPath = (0, path_1.dirname)(bin);
|
|
34
|
+
const binFile = (0, path_1.basename)(bin);
|
|
35
|
+
const newBinFile = 'new-' + binFile;
|
|
36
|
+
plugins_1.pluginsWatcher.pause();
|
|
37
|
+
try {
|
|
38
|
+
await (0, misc_1.unzip)(await (0, misc_1.httpsStream)(url), path => (0, path_1.join)(binPath, path === binFile ? newBinFile : path));
|
|
39
|
+
const newBin = (0, path_1.join)(binPath, newBinFile);
|
|
40
|
+
if (!const_1.IS_WINDOWS) {
|
|
41
|
+
const { mode } = await (0, promises_1.stat)(bin);
|
|
42
|
+
await (0, promises_1.chmod)(newBin, mode).catch(console.error);
|
|
43
|
+
}
|
|
44
|
+
(0, misc_1.onProcessExit)(() => {
|
|
45
|
+
const oldBinFile = 'old-' + binFile;
|
|
46
|
+
console.log("old version is kept as", oldBinFile);
|
|
47
|
+
const oldBin = (0, path_1.join)(binPath, oldBinFile);
|
|
48
|
+
try {
|
|
49
|
+
(0, fs_1.unlinkSync)(oldBin);
|
|
50
|
+
}
|
|
51
|
+
catch (_a) { }
|
|
52
|
+
(0, fs_1.renameSync)(bin, oldBin);
|
|
53
|
+
console.log("launching new version in background", newBinFile);
|
|
54
|
+
(0, child_process_1.spawn)(newBin, ['--updating', binFile], { detached: true, shell: true })
|
|
55
|
+
.on('error', console.error);
|
|
56
|
+
});
|
|
57
|
+
console.log('quitting');
|
|
58
|
+
process.exit();
|
|
59
|
+
}
|
|
60
|
+
catch (_a) {
|
|
61
|
+
plugins_1.pluginsWatcher.unpause();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.update = update;
|
|
65
|
+
if (const_1.argv.updating) { // we were launched with a temporary name, restore original name to avoid breaking references
|
|
66
|
+
const bin = process.argv[0];
|
|
67
|
+
(0, fs_1.renameSync)(bin, (0, path_1.join)((0, path_1.dirname)(bin), const_1.argv.updating));
|
|
68
|
+
console.log("renamed binary file to", const_1.argv.updating);
|
|
69
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.unzip = exports.run = exports.dirStream = exports.adjustStaticPathForGlob = exports.isWindowsDrive = exports.dirTraversal = exports.watchDir = exports.readFileBusy = exports.isFile = exports.isDirectory = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const misc_1 = require("./misc");
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
12
|
+
const const_1 = require("./const");
|
|
13
|
+
const child_process_1 = require("child_process");
|
|
14
|
+
const stream_1 = require("stream");
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
const unzip_stream_1 = __importDefault(require("unzip-stream"));
|
|
17
|
+
async function isDirectory(path) {
|
|
18
|
+
try {
|
|
19
|
+
return (await promises_1.default.stat(path)).isDirectory();
|
|
20
|
+
}
|
|
21
|
+
catch (_a) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.isDirectory = isDirectory;
|
|
26
|
+
async function isFile(path) {
|
|
27
|
+
try {
|
|
28
|
+
return (await promises_1.default.stat(path)).isFile();
|
|
29
|
+
}
|
|
30
|
+
catch (_a) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.isFile = isFile;
|
|
35
|
+
async function readFileBusy(path) {
|
|
36
|
+
return promises_1.default.readFile(path, 'utf8').catch(e => {
|
|
37
|
+
if ((e === null || e === void 0 ? void 0 : e.code) !== 'EBUSY')
|
|
38
|
+
throw e;
|
|
39
|
+
console.debug('busy');
|
|
40
|
+
return (0, misc_1.wait)(100).then(() => readFileBusy(path));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
exports.readFileBusy = readFileBusy;
|
|
44
|
+
function watchDir(dir, cb) {
|
|
45
|
+
let watcher;
|
|
46
|
+
let paused = false;
|
|
47
|
+
try {
|
|
48
|
+
watcher = (0, fs_1.watch)(dir, controlledCb);
|
|
49
|
+
}
|
|
50
|
+
catch (_a) {
|
|
51
|
+
// failing watching the content of the dir, we try to monitor its parent, but filtering events only for our target dir
|
|
52
|
+
const base = (0, path_1.basename)(dir);
|
|
53
|
+
try {
|
|
54
|
+
watcher = (0, fs_1.watch)((0, path_1.dirname)(dir), (event, name) => {
|
|
55
|
+
if (name !== base)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
watcher.close(); // if we succeed, we give up the parent watching
|
|
59
|
+
watcher = (0, fs_1.watch)(dir, controlledCb); // attempt at passing to a more specific watching
|
|
60
|
+
}
|
|
61
|
+
catch (_a) { }
|
|
62
|
+
controlledCb();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
console.debug(String(e));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
working() { return Boolean(watcher); },
|
|
71
|
+
stop() { watcher === null || watcher === void 0 ? void 0 : watcher.close(); },
|
|
72
|
+
pause() { paused = true; },
|
|
73
|
+
unpause() { paused = false; },
|
|
74
|
+
};
|
|
75
|
+
function controlledCb() {
|
|
76
|
+
if (!paused)
|
|
77
|
+
cb();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.watchDir = watchDir;
|
|
81
|
+
function dirTraversal(s) {
|
|
82
|
+
return s && /(^|[/\\])\.\.($|[/\\])/.test(s);
|
|
83
|
+
}
|
|
84
|
+
exports.dirTraversal = dirTraversal;
|
|
85
|
+
function isWindowsDrive(s) {
|
|
86
|
+
return s && /^[a-zA-Z]:$/.test(s);
|
|
87
|
+
}
|
|
88
|
+
exports.isWindowsDrive = isWindowsDrive;
|
|
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))
|
|
96
|
+
throw Error('ENOTDIR');
|
|
97
|
+
const dirStream = fast_glob_1.default.stream(deep ? '**/*' : '*', {
|
|
98
|
+
cwd: path,
|
|
99
|
+
dot: true,
|
|
100
|
+
deep,
|
|
101
|
+
onlyFiles: false,
|
|
102
|
+
suppressErrors: true,
|
|
103
|
+
objectMode: true,
|
|
104
|
+
});
|
|
105
|
+
const skip = await getItemsToSkip(path);
|
|
106
|
+
for await (const entry of dirStream) {
|
|
107
|
+
let { path, dirent } = entry;
|
|
108
|
+
if (!dirent.isDirectory() && !dirent.isFile())
|
|
109
|
+
continue;
|
|
110
|
+
path = String(path);
|
|
111
|
+
if (!(skip === null || skip === void 0 ? void 0 : skip.includes(path)))
|
|
112
|
+
yield path;
|
|
113
|
+
}
|
|
114
|
+
async function getItemsToSkip(path) {
|
|
115
|
+
if (!const_1.IS_WINDOWS)
|
|
116
|
+
return;
|
|
117
|
+
const out = await run('dir', ['/ah', '/b', path.replace(/\//g, '\\')])
|
|
118
|
+
.catch(() => ''); // error in case of no matching file
|
|
119
|
+
return out.split('\r\n').slice(0, -1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.dirStream = dirStream;
|
|
123
|
+
function run(cmd, args = []) {
|
|
124
|
+
return new Promise((resolve, reject) => (0, child_process_1.execFile)('cmd', ['/c', cmd, ...args], (err, stdout) => {
|
|
125
|
+
if (err)
|
|
126
|
+
reject(err);
|
|
127
|
+
else
|
|
128
|
+
resolve(stdout);
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
exports.run = run;
|
|
132
|
+
async function unzip(stream, cb) {
|
|
133
|
+
let pending = Promise.resolve();
|
|
134
|
+
return new Promise(resolve => stream.pipe(unzip_stream_1.default.Parse())
|
|
135
|
+
.on('end', () => pending.then(resolve))
|
|
136
|
+
.on('entry', async (entry) => {
|
|
137
|
+
const { path, type } = entry;
|
|
138
|
+
const dest = cb(path);
|
|
139
|
+
if (!dest || type !== 'File')
|
|
140
|
+
return entry.autodrain();
|
|
141
|
+
await pending; // don't overlap writings
|
|
142
|
+
console.debug('unzip', dest);
|
|
143
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(dest), { recursive: true }); // easy way be sure to have the folder ready before proceeding
|
|
144
|
+
const thisFile = entry.pipe((0, fs_1.createWriteStream)(dest));
|
|
145
|
+
pending = (0, stream_1.once)(thisFile, 'finish');
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
exports.unzip = unzip;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.asyncGeneratorToReadable = exports.asyncGeneratorToArray = exports.filterMapGenerator = void 0;
|
|
4
|
+
// callback can return undefined to skip element
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
|
+
async function* filterMapGenerator(generator, filterMap) {
|
|
7
|
+
for await (const x of generator) {
|
|
8
|
+
const res = await filterMap(x);
|
|
9
|
+
if (res !== undefined)
|
|
10
|
+
yield res;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.filterMapGenerator = filterMapGenerator;
|
|
14
|
+
async function asyncGeneratorToArray(generator) {
|
|
15
|
+
const ret = [];
|
|
16
|
+
for await (const x of generator)
|
|
17
|
+
ret.push(x);
|
|
18
|
+
return ret;
|
|
19
|
+
}
|
|
20
|
+
exports.asyncGeneratorToArray = asyncGeneratorToArray;
|
|
21
|
+
function asyncGeneratorToReadable(generator) {
|
|
22
|
+
const iterator = generator[Symbol.asyncIterator]();
|
|
23
|
+
return new stream_1.Readable({
|
|
24
|
+
objectMode: true,
|
|
25
|
+
read() {
|
|
26
|
+
iterator.next().then(it => this.push(it.done ? null : it.value));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
exports.asyncGeneratorToReadable = asyncGeneratorToReadable;
|
package/src/util-http.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.httpsStream = exports.httpsString = void 0;
|
|
7
|
+
const node_https_1 = __importDefault(require("node:https"));
|
|
8
|
+
function httpsString(url, options = {}) {
|
|
9
|
+
return httpsStream(url, options).then(res => new Promise(resolve => {
|
|
10
|
+
let buf = '';
|
|
11
|
+
res.on('data', chunk => buf += chunk.toString());
|
|
12
|
+
res.on('end', () => resolve(Object.assign(res, {
|
|
13
|
+
ok: (res.statusCode || 400) < 400,
|
|
14
|
+
body: buf
|
|
15
|
+
})));
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
exports.httpsString = httpsString;
|
|
19
|
+
function httpsStream(url, options = {}) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
node_https_1.default.request(url, options, res => {
|
|
22
|
+
if (!res.statusCode || res.statusCode >= 400)
|
|
23
|
+
throw res;
|
|
24
|
+
if (res.statusCode === 302 && res.headers.location)
|
|
25
|
+
return resolve(httpsStream(res.headers.location, options));
|
|
26
|
+
resolve(res);
|
|
27
|
+
}).on('error', reject).end();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
exports.httpsStream = httpsStream;
|