hfs 0.47.3 → 0.48.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/admin/assets/index-1c2b890b.css +1 -0
- package/admin/assets/index-344723f8.js +520 -0
- package/{frontend/assets/sha512-108e3b0a.js → admin/assets/sha512-136b5a7c.js} +1 -1
- package/admin/index.html +2 -2
- package/frontend/assets/index-05dfd82f.css +1 -0
- package/frontend/assets/{index-7eec0199.js → index-12411ff6.js} +15 -15
- package/{admin/assets/sha512-0935647a.js → frontend/assets/sha512-549e6c3a.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +20 -13
- package/plugins/vhosting/plugin.js +15 -15
- package/src/adminApis.js +7 -4
- package/src/api.auth.js +0 -3
- package/src/api.file_list.js +8 -7
- package/src/api.lang.js +1 -1
- package/src/api.net.js +136 -0
- package/src/api.plugins.js +31 -36
- package/src/api.vfs.js +1 -1
- package/src/apiMiddleware.js +22 -20
- package/src/config.js +2 -16
- package/src/const.js +4 -2
- package/src/cross.js +221 -0
- package/src/customHtml.js +7 -22
- package/src/debounceAsync.js +12 -9
- package/src/frontEndApis.js +2 -4
- package/src/github.js +86 -40
- package/src/langs/embedded.js +2 -1
- package/src/langs/hfs-lang-de.json +131 -0
- package/src/langs/hfs-lang-it.json +1 -1
- package/src/listen.js +44 -37
- package/src/log.js +8 -5
- package/src/middlewares.js +1 -3
- package/src/misc.js +33 -137
- package/src/plugins.js +46 -26
- package/src/serveFile.js +1 -0
- package/src/update.js +1 -1
- package/src/upload.js +2 -1
- package/src/util-http.js +11 -8
- package/admin/assets/index-62247236.css +0 -1
- package/admin/assets/index-ca5ac85e.js +0 -518
- package/frontend/assets/index-0ea37f5f.css +0 -1
- package/src/util-generators.js +0 -31
package/src/config.js
CHANGED
|
@@ -14,7 +14,6 @@ const misc_1 = require("./misc");
|
|
|
14
14
|
const fs_1 = require("fs");
|
|
15
15
|
const path_1 = require("path");
|
|
16
16
|
const events_2 = __importDefault(require("./events"));
|
|
17
|
-
const os_1 = require("os");
|
|
18
17
|
const promises_1 = require("fs/promises");
|
|
19
18
|
const FILE = 'config.yaml';
|
|
20
19
|
// keep definition of config properties
|
|
@@ -34,19 +33,6 @@ const filePath = (0, misc_1.with_)(const_1.argv.config || process.env.HFS_CONFIG
|
|
|
34
33
|
catch (_a) { }
|
|
35
34
|
return p;
|
|
36
35
|
});
|
|
37
|
-
const legacyPosition = (0, path_1.join)((0, os_1.homedir)(), FILE); // this was happening with npx on Windows for some time. Remove around v0.47
|
|
38
|
-
if (!(0, fs_1.existsSync)(filePath) && (0, fs_1.existsSync)(legacyPosition))
|
|
39
|
-
try {
|
|
40
|
-
(0, fs_1.renameSync)(legacyPosition, filePath);
|
|
41
|
-
console.log("moved from legacy position", legacyPosition);
|
|
42
|
-
}
|
|
43
|
-
catch (_a) {
|
|
44
|
-
try { // attempt copying, in case moving the source file proves to be impractical
|
|
45
|
-
(0, fs_1.copyFileSync)(legacyPosition, filePath);
|
|
46
|
-
console.log("copied from legacy position", legacyPosition);
|
|
47
|
-
}
|
|
48
|
-
catch (_b) { }
|
|
49
|
-
}
|
|
50
36
|
// takes a semver like 1.2.3-alpha1, but alpha and beta numbers must share the number progression
|
|
51
37
|
exports.versionToScalar = lodash_1.default.memoize((ver) => {
|
|
52
38
|
// this regexp is supposed to be resistant to optional leading "v" and an optional custom name after a space
|
|
@@ -71,7 +57,7 @@ exports.currentVersion = new Version(const_1.VERSION);
|
|
|
71
57
|
const configVersion = defineConfig('version', const_1.VERSION, v => new Version(v));
|
|
72
58
|
function defineConfig(k, defaultValue, compiler) {
|
|
73
59
|
configProps[k] = { defaultValue };
|
|
74
|
-
let compiled = compiler === null || compiler === void 0 ? void 0 : compiler(defaultValue, { version: exports.currentVersion, defaultValue });
|
|
60
|
+
let compiled = compiler === null || compiler === void 0 ? void 0 : compiler(defaultValue, { k, version: exports.currentVersion, defaultValue });
|
|
75
61
|
const ret = {
|
|
76
62
|
key() {
|
|
77
63
|
return k;
|
|
@@ -81,7 +67,7 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
81
67
|
},
|
|
82
68
|
sub(cb) {
|
|
83
69
|
if (started) // initial event already passed, we'll make the first call
|
|
84
|
-
cb(getConfig(k), { was: defaultValue, defaultValue, version: configVersion.compiled() });
|
|
70
|
+
cb(getConfig(k), { k, was: defaultValue, defaultValue, version: configVersion.compiled() });
|
|
85
71
|
const eventName = CONFIG_CHANGE_EVENT_PREFIX + k;
|
|
86
72
|
return (0, misc_1.onOff)(cfgEvents, {
|
|
87
73
|
[eventName]() {
|
package/src/const.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.APP_PATH = exports.IS_BINARY = exports.IS_WINDOWS = exports.HTTP_SERVER_ERROR = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.HFS_REPO = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.DAY = exports.RUNNING_BETA = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
|
|
30
|
+
exports.APP_PATH = exports.IS_BINARY = exports.IS_MAC = exports.IS_WINDOWS = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.HFS_REPO = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.DAY = exports.RUNNING_BETA = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
|
|
31
31
|
const minimist_1 = __importDefault(require("minimist"));
|
|
32
32
|
const fs = __importStar(require("fs"));
|
|
33
33
|
const os_1 = require("os");
|
|
@@ -38,7 +38,7 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
|
|
|
38
38
|
exports.ORIGINAL_CWD = process.cwd();
|
|
39
39
|
exports.HFS_STARTED = new Date();
|
|
40
40
|
const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
|
|
41
|
-
exports.BUILD_TIMESTAMP = "2023-
|
|
41
|
+
exports.BUILD_TIMESTAMP = "2023-09-07T17:51:10.938Z";
|
|
42
42
|
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
43
43
|
exports.VERSION = pkg.version;
|
|
44
44
|
exports.RUNNING_BETA = exports.VERSION.includes('-');
|
|
@@ -68,7 +68,9 @@ exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
|
|
|
68
68
|
exports.HTTP_FOOL = 418;
|
|
69
69
|
exports.HTTP_FAILED_DEPENDENCY = 424;
|
|
70
70
|
exports.HTTP_SERVER_ERROR = 500;
|
|
71
|
+
exports.HTTP_SERVICE_UNAVAILABLE = 503;
|
|
71
72
|
exports.IS_WINDOWS = process.platform === 'win32';
|
|
73
|
+
exports.IS_MAC = process.platform === 'darwin';
|
|
72
74
|
exports.IS_BINARY = !(0, path_1.basename)(process.execPath).includes('node'); // this won't be node if pkg was used
|
|
73
75
|
exports.APP_PATH = (0, path_1.dirname)(exports.IS_BINARY ? process.execPath : __dirname);
|
|
74
76
|
// we want this to be the first stuff to be printed, then we print it in this module, that is executed at the beginning
|
package/src/cross.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
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.repeat = exports.asyncGeneratorToArray = exports.filterMapGenerator = exports.throw_ = exports.hasProp = exports.typedEntries = exports.typedKeys = exports.objRenameKey = exports.randomId = exports.getOrSet = exports.waitFor = exports.newObj = exports.removeStarting = exports.findDefined = exports.isOrderedEqual = exports.swap = exports.tryJson = exports.basename = exports.pendingPromise = exports._log = exports.wantArray = exports.formatPerc = exports.with_ = exports.try_ = exports.setHidden = exports.onlyTruthy = exports.truthy = exports.enforceFinal = exports.objSameKeys = exports.wait = exports.prefix = exports.formatBytes = exports.MINUTE = exports.WIKI_URL = exports.REPO_URL = void 0;
|
|
7
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
8
|
+
// all content here is shared between client and server
|
|
9
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
10
|
+
exports.REPO_URL = 'https://github.com/rejetto/hfs/';
|
|
11
|
+
exports.WIKI_URL = exports.REPO_URL + 'wiki/';
|
|
12
|
+
exports.MINUTE = 60000;
|
|
13
|
+
const MULTIPLIERS = ['', 'K', 'M', 'G', 'T'];
|
|
14
|
+
function formatBytes(n, { post = 'B', k = 1024, digits = NaN } = {}) {
|
|
15
|
+
if (isNaN(Number(n)) || n < 0)
|
|
16
|
+
return '';
|
|
17
|
+
const i = n && Math.floor(Math.log2(n) / Math.log2(k));
|
|
18
|
+
n /= k ** i;
|
|
19
|
+
const nAsString = i && !isNaN(digits) ? n.toFixed(digits)
|
|
20
|
+
: lodash_1.default.round(n, isNaN(digits) ? (n >= 100 ? 0 : 1) : digits);
|
|
21
|
+
return nAsString + ' ' + (MULTIPLIERS[i] || '') + post;
|
|
22
|
+
} // formatBytes
|
|
23
|
+
exports.formatBytes = formatBytes;
|
|
24
|
+
function prefix(pre, v, post = '') {
|
|
25
|
+
return v ? pre + v + post : '';
|
|
26
|
+
}
|
|
27
|
+
exports.prefix = prefix;
|
|
28
|
+
function wait(ms, val) {
|
|
29
|
+
return new Promise(res => setTimeout(res, ms, val));
|
|
30
|
+
}
|
|
31
|
+
exports.wait = wait;
|
|
32
|
+
function objSameKeys(src, newValue) {
|
|
33
|
+
return Object.fromEntries(Object.entries(src).map(([k, v]) => [k, newValue(v, k)]));
|
|
34
|
+
}
|
|
35
|
+
exports.objSameKeys = objSameKeys;
|
|
36
|
+
function enforceFinal(sub, s) {
|
|
37
|
+
return !s || s.endsWith(sub) ? s : s + sub;
|
|
38
|
+
}
|
|
39
|
+
exports.enforceFinal = enforceFinal;
|
|
40
|
+
function truthy(value) {
|
|
41
|
+
return Boolean(value);
|
|
42
|
+
}
|
|
43
|
+
exports.truthy = truthy;
|
|
44
|
+
function onlyTruthy(arr) {
|
|
45
|
+
return arr.filter(truthy);
|
|
46
|
+
}
|
|
47
|
+
exports.onlyTruthy = onlyTruthy;
|
|
48
|
+
function setHidden(dest, src) {
|
|
49
|
+
return Object.defineProperties(dest, newObj(src, value => ({
|
|
50
|
+
enumerable: false,
|
|
51
|
+
writable: true,
|
|
52
|
+
value,
|
|
53
|
+
})));
|
|
54
|
+
}
|
|
55
|
+
exports.setHidden = setHidden;
|
|
56
|
+
function try_(cb, onException) {
|
|
57
|
+
try {
|
|
58
|
+
return cb();
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
return onException === null || onException === void 0 ? void 0 : onException(e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.try_ = try_;
|
|
65
|
+
function with_(par, cb) {
|
|
66
|
+
return cb(par);
|
|
67
|
+
}
|
|
68
|
+
exports.with_ = with_;
|
|
69
|
+
function formatPerc(p) {
|
|
70
|
+
return (p * 100).toFixed(1) + '%';
|
|
71
|
+
}
|
|
72
|
+
exports.formatPerc = formatPerc;
|
|
73
|
+
function wantArray(x) {
|
|
74
|
+
return x == null ? [] : Array.isArray(x) ? x : [x];
|
|
75
|
+
}
|
|
76
|
+
exports.wantArray = wantArray;
|
|
77
|
+
function _log(...args) {
|
|
78
|
+
console.log('**', ...args);
|
|
79
|
+
return args[args.length - 1];
|
|
80
|
+
}
|
|
81
|
+
exports._log = _log;
|
|
82
|
+
function pendingPromise() {
|
|
83
|
+
let takeOut;
|
|
84
|
+
const ret = new Promise((resolve, reject) => takeOut = { resolve, reject });
|
|
85
|
+
return Object.assign(ret, takeOut);
|
|
86
|
+
}
|
|
87
|
+
exports.pendingPromise = pendingPromise;
|
|
88
|
+
function basename(path) {
|
|
89
|
+
return path.slice(path.lastIndexOf('/') + 1 || path.lastIndexOf('\\') + 1);
|
|
90
|
+
}
|
|
91
|
+
exports.basename = basename;
|
|
92
|
+
function tryJson(s) {
|
|
93
|
+
try {
|
|
94
|
+
return s && JSON.parse(s);
|
|
95
|
+
}
|
|
96
|
+
catch (_a) { }
|
|
97
|
+
}
|
|
98
|
+
exports.tryJson = tryJson;
|
|
99
|
+
function swap(obj, k1, k2) {
|
|
100
|
+
const temp = obj[k1];
|
|
101
|
+
obj[k1] = obj[k2];
|
|
102
|
+
obj[k2] = temp;
|
|
103
|
+
return obj;
|
|
104
|
+
}
|
|
105
|
+
exports.swap = swap;
|
|
106
|
+
function isOrderedEqual(a, b) {
|
|
107
|
+
return lodash_1.default.isEqualWith(a, b, (a1, b1) => {
|
|
108
|
+
if (!lodash_1.default.isPlainObject(a1) || !lodash_1.default.isPlainObject(b1))
|
|
109
|
+
return;
|
|
110
|
+
const ka = Object.keys(a1);
|
|
111
|
+
const kb = Object.keys(b1);
|
|
112
|
+
return ka.length === kb.length && ka.every((ka1, i) => {
|
|
113
|
+
const kb1 = kb[i];
|
|
114
|
+
return ka1 === kb1 && isOrderedEqual(a1[ka1], b1[kb1]);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
exports.isOrderedEqual = isOrderedEqual;
|
|
119
|
+
function findDefined(a, cb) {
|
|
120
|
+
if (a)
|
|
121
|
+
for (const k in a) {
|
|
122
|
+
const ret = cb(a[k], k);
|
|
123
|
+
if (ret !== undefined)
|
|
124
|
+
return ret;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.findDefined = findDefined;
|
|
128
|
+
function removeStarting(sub, s) {
|
|
129
|
+
return s.startsWith(sub) ? s.slice(sub.length) : s;
|
|
130
|
+
}
|
|
131
|
+
exports.removeStarting = removeStarting;
|
|
132
|
+
function newObj(src, returnNewValue, recur = false) {
|
|
133
|
+
if (!src)
|
|
134
|
+
return {};
|
|
135
|
+
const pairs = Object.entries(src).map(([k, v]) => {
|
|
136
|
+
if (typeof k === 'symbol')
|
|
137
|
+
return;
|
|
138
|
+
let _k = k;
|
|
139
|
+
const curDepth = typeof recur === 'number' ? recur : 0;
|
|
140
|
+
let newV = returnNewValue(v, k, (newK) => {
|
|
141
|
+
_k = newK;
|
|
142
|
+
return true; // for convenient expression concatenation
|
|
143
|
+
}, curDepth);
|
|
144
|
+
if ((recur !== false || returnNewValue.length === 4) // if callback is using depth parameter, then it wants recursion
|
|
145
|
+
&& lodash_1.default.isPlainObject(newV)) // is it recurrable?
|
|
146
|
+
newV = newObj(newV, returnNewValue, curDepth + 1);
|
|
147
|
+
return _k !== undefined && [_k, newV];
|
|
148
|
+
});
|
|
149
|
+
return Object.fromEntries(onlyTruthy(pairs));
|
|
150
|
+
}
|
|
151
|
+
exports.newObj = newObj;
|
|
152
|
+
async function waitFor(cb, { interval = 200, timeout = Infinity } = {}) {
|
|
153
|
+
const started = Date.now();
|
|
154
|
+
while (1) {
|
|
155
|
+
const res = await cb();
|
|
156
|
+
if (res)
|
|
157
|
+
return res;
|
|
158
|
+
if (Date.now() - started >= timeout)
|
|
159
|
+
return;
|
|
160
|
+
await wait(interval);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.waitFor = waitFor;
|
|
164
|
+
function getOrSet(o, k, creator) {
|
|
165
|
+
return k in o ? o[k]
|
|
166
|
+
: (o[k] = creator());
|
|
167
|
+
}
|
|
168
|
+
exports.getOrSet = getOrSet;
|
|
169
|
+
// 10 chars is 51+bits, 8 is 41+bits
|
|
170
|
+
function randomId(len = 10) {
|
|
171
|
+
if (len > 10)
|
|
172
|
+
return randomId(10) + randomId(len - 10);
|
|
173
|
+
return Math.random()
|
|
174
|
+
.toString(36)
|
|
175
|
+
.substring(2, 2 + len)
|
|
176
|
+
.replace(/l/g, 'L'); // avoid confusion reading l1
|
|
177
|
+
}
|
|
178
|
+
exports.randomId = randomId;
|
|
179
|
+
function objRenameKey(o, from, to) {
|
|
180
|
+
if (!o || !o.hasOwnProperty(from) || from === to)
|
|
181
|
+
return;
|
|
182
|
+
o[to] = o[from];
|
|
183
|
+
delete o[from];
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
exports.objRenameKey = objRenameKey;
|
|
187
|
+
function typedKeys(o) {
|
|
188
|
+
return Object.keys(o);
|
|
189
|
+
}
|
|
190
|
+
exports.typedKeys = typedKeys;
|
|
191
|
+
function typedEntries(o) {
|
|
192
|
+
return Object.entries(o);
|
|
193
|
+
}
|
|
194
|
+
exports.typedEntries = typedEntries;
|
|
195
|
+
function hasProp(obj, key) {
|
|
196
|
+
return key in obj;
|
|
197
|
+
}
|
|
198
|
+
exports.hasProp = hasProp;
|
|
199
|
+
function throw_(err) {
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
exports.throw_ = throw_;
|
|
203
|
+
async function* filterMapGenerator(generator, filterMap) {
|
|
204
|
+
for await (const x of generator) {
|
|
205
|
+
const res = await filterMap(x);
|
|
206
|
+
if (res !== undefined)
|
|
207
|
+
yield res;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
exports.filterMapGenerator = filterMapGenerator;
|
|
211
|
+
async function asyncGeneratorToArray(generator) {
|
|
212
|
+
const ret = [];
|
|
213
|
+
for await (const x of generator)
|
|
214
|
+
ret.push(x);
|
|
215
|
+
return ret;
|
|
216
|
+
}
|
|
217
|
+
exports.asyncGeneratorToArray = asyncGeneratorToArray;
|
|
218
|
+
function repeat(every, cb) {
|
|
219
|
+
Promise.allSettled([cb()]).then(() => setTimeout(() => repeat(every, cb), every));
|
|
220
|
+
}
|
|
221
|
+
exports.repeat = repeat;
|
package/src/customHtml.js
CHANGED
|
@@ -1,36 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.saveCustomHtml = exports.getSection = exports.watchLoadCustomHtml = exports.
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
|
-
const events_1 = __importDefault(require("./events"));
|
|
3
|
+
exports.saveCustomHtml = exports.getSection = exports.watchLoadCustomHtml = exports.customHtmlState = exports.customHtmlSections = void 0;
|
|
9
4
|
const misc_1 = require("./misc");
|
|
10
|
-
const frontEndApis_1 = require("./frontEndApis");
|
|
11
5
|
const watchLoad_1 = require("./watchLoad");
|
|
12
6
|
const vanilla_1 = require("valtio/vanilla"); // without /vanilla we trigger react dependency
|
|
13
7
|
const promises_1 = require("fs/promises");
|
|
14
8
|
const plugins_1 = require("./plugins");
|
|
9
|
+
const FILE = 'custom.html';
|
|
15
10
|
exports.customHtmlSections = ['beforeHeader', 'afterHeader', 'afterMenuBar', 'afterList',
|
|
16
11
|
'top', 'bottom', 'afterEntryName', 'beforeLogin'];
|
|
17
12
|
exports.customHtmlState = (0, vanilla_1.proxy)({
|
|
18
|
-
sections:
|
|
13
|
+
sections: watchLoadCustomHtml().state
|
|
19
14
|
});
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
exports.newCustomHtmlState = newCustomHtmlState;
|
|
24
|
-
const FILE = 'custom.html';
|
|
25
|
-
if (!(0, fs_1.existsSync)(FILE))
|
|
26
|
-
events_1.default.once('config ready', () => {
|
|
27
|
-
const legacy = (0, misc_1.prefix)('[beforeHeader]\n', frontEndApis_1.customHeader.get());
|
|
28
|
-
(0, fs_1.writeFileSync)(FILE, legacy);
|
|
29
|
-
frontEndApis_1.customHeader.set(''); // get rid of it
|
|
30
|
-
});
|
|
31
|
-
watchLoadCustomHtml(exports.customHtmlState.sections);
|
|
32
|
-
function watchLoadCustomHtml(state, folder = '') {
|
|
33
|
-
return (0, watchLoad_1.watchLoad)((0, misc_1.prefix)('', folder, '/') + FILE, data => {
|
|
15
|
+
function watchLoadCustomHtml(folder = '') {
|
|
16
|
+
const state = new Map();
|
|
17
|
+
const res = (0, watchLoad_1.watchLoad)((0, misc_1.prefix)('', folder, '/') + FILE, data => {
|
|
34
18
|
var _a;
|
|
35
19
|
const re = /^\[(\w+)] *$/gm;
|
|
36
20
|
state.clear();
|
|
@@ -46,6 +30,7 @@ function watchLoadCustomHtml(state, folder = '') {
|
|
|
46
30
|
name = match === null || match === void 0 ? void 0 : match[1];
|
|
47
31
|
} while (name);
|
|
48
32
|
});
|
|
33
|
+
return Object.assign(res, { state });
|
|
49
34
|
}
|
|
50
35
|
exports.watchLoadCustomHtml = watchLoadCustomHtml;
|
|
51
36
|
function getSection(name) {
|
package/src/debounceAsync.js
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
// like lodash.debounce, but also avoids async invocations to overlap
|
|
5
|
-
function debounceAsync(callback, wait = 100,
|
|
5
|
+
function debounceAsync(callback, wait = 100, options = {}) {
|
|
6
|
+
const { leading = false, maxWait = Infinity, cancelable = false } = options;
|
|
6
7
|
let started = 0; // latest callback invocation
|
|
7
8
|
let runningCallback; // latest callback invocation result
|
|
8
9
|
let runningDebouncer; // latest wrapper invocation
|
|
9
10
|
let waitingSince = 0; // we are delaying invocation since
|
|
10
|
-
let whoIsWaiting; // args
|
|
11
|
-
const interceptingWrapper = (...args) => runningDebouncer = debouncer
|
|
11
|
+
let whoIsWaiting; // args object identifies the pending instance, and incidentally stores args
|
|
12
|
+
const interceptingWrapper = (...args) => runningDebouncer = debouncer(...args);
|
|
12
13
|
return Object.assign(interceptingWrapper, {
|
|
13
|
-
cancel: () => {
|
|
14
|
-
waitingSince = 0;
|
|
15
|
-
whoIsWaiting = undefined;
|
|
16
|
-
},
|
|
17
14
|
flush: () => runningCallback !== null && runningCallback !== void 0 ? runningCallback : exec(),
|
|
15
|
+
...cancelable && {
|
|
16
|
+
cancel() {
|
|
17
|
+
waitingSince = 0;
|
|
18
|
+
whoIsWaiting = undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
18
21
|
});
|
|
19
22
|
async function debouncer(...args) {
|
|
20
23
|
if (runningCallback)
|
|
@@ -33,11 +36,11 @@ function debounceAsync(callback, wait = 100, { leading = false, maxWait = Infini
|
|
|
33
36
|
}
|
|
34
37
|
async function exec() {
|
|
35
38
|
if (!whoIsWaiting)
|
|
36
|
-
return;
|
|
39
|
+
return undefined;
|
|
37
40
|
waitingSince = 0;
|
|
38
41
|
started = Date.now();
|
|
39
42
|
try {
|
|
40
|
-
runningCallback = callback
|
|
43
|
+
runningCallback = callback(...whoIsWaiting);
|
|
41
44
|
return await runningCallback; // await necessary to go-finally at the right time and even on exceptions
|
|
42
45
|
}
|
|
43
46
|
finally {
|
package/src/frontEndApis.js
CHANGED
|
@@ -27,11 +27,10 @@ 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.notifyClient = exports.frontEndApis =
|
|
30
|
+
exports.notifyClient = exports.frontEndApis = void 0;
|
|
31
31
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
32
32
|
const api_file_list_1 = require("./api.file_list");
|
|
33
33
|
const api_auth = __importStar(require("./api.auth"));
|
|
34
|
-
const config_1 = require("./config");
|
|
35
34
|
const events_1 = __importDefault(require("./events"));
|
|
36
35
|
const util_files_1 = require("./util-files");
|
|
37
36
|
const const_1 = require("./const");
|
|
@@ -39,9 +38,8 @@ const vfs_1 = require("./vfs");
|
|
|
39
38
|
const promises_1 = require("fs/promises");
|
|
40
39
|
const path_1 = require("path");
|
|
41
40
|
const upload_1 = require("./upload");
|
|
42
|
-
exports.customHeader = (0, config_1.defineConfig)('custom_header', '');
|
|
43
41
|
exports.frontEndApis = {
|
|
44
|
-
|
|
42
|
+
get_file_list: api_file_list_1.get_file_list,
|
|
45
43
|
...api_auth,
|
|
46
44
|
get_notifications({ channel }, ctx) {
|
|
47
45
|
apiAssertTypes({ string: { channel } });
|
package/src/github.js
CHANGED
|
@@ -13,6 +13,7 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
|
13
13
|
const const_1 = require("./const");
|
|
14
14
|
const promises_1 = require("fs/promises");
|
|
15
15
|
const path_1 = require("path");
|
|
16
|
+
const fs_1 = require("fs");
|
|
16
17
|
const DIST_ROOT = 'dist';
|
|
17
18
|
const downloading = {};
|
|
18
19
|
function downloadProgress(id, status) {
|
|
@@ -23,45 +24,60 @@ function downloadProgress(id, status) {
|
|
|
23
24
|
events_1.default.emit('pluginDownload_' + id, status);
|
|
24
25
|
}
|
|
25
26
|
async function downloadPlugin(repo, branch = '', overwrite) {
|
|
27
|
+
var _a, _b, _c;
|
|
26
28
|
if (downloading[repo])
|
|
27
29
|
return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT, "already downloading");
|
|
28
30
|
console.log('downloading plugin', repo);
|
|
29
31
|
downloadProgress(repo, true);
|
|
30
32
|
try {
|
|
33
|
+
if (repo.includes('//')) { // custom repo
|
|
34
|
+
const pl = (0, plugins_1.findPluginByRepo)(repo);
|
|
35
|
+
if (!pl)
|
|
36
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, "bad repo");
|
|
37
|
+
const customRepo = (((_b = (_a = pl).getData) === null || _b === void 0 ? void 0 : _b.call(_a)) || pl).repo;
|
|
38
|
+
let url = customRepo === null || customRepo === void 0 ? void 0 : customRepo.zip;
|
|
39
|
+
if (!url)
|
|
40
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, "bad plugin");
|
|
41
|
+
if (!url.includes('//'))
|
|
42
|
+
url = customRepo.web + url;
|
|
43
|
+
return await go(url, pl === null || pl === void 0 ? void 0 : pl.id, (_c = customRepo.zipRoot) !== null && _c !== void 0 ? _c : DIST_ROOT);
|
|
44
|
+
}
|
|
31
45
|
const rec = await getRepoInfo(repo);
|
|
32
46
|
if (!branch)
|
|
33
47
|
branch = rec.default_branch;
|
|
34
48
|
const short = repo.split('/')[1]; // second part, repo without the owner
|
|
35
49
|
if (!short)
|
|
36
50
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, "bad repo");
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
: folder2repo.hasOwnProperty(short) ? repo.replace('/', '-') // longer form only if another plugin is using short form
|
|
51
|
+
const folder = overwrite ? lodash_1.default.findKey(getFolder2repo(), x => x === repo) // use existing folder
|
|
52
|
+
: getFolder2repo().hasOwnProperty(short) ? repo.replace('/', '-') // longer form only if another plugin is using short form, to avoid overwriting
|
|
40
53
|
: short;
|
|
41
|
-
const installPath = plugins_1.PATH + '/' + folder;
|
|
42
54
|
const GITHUB_ZIP_ROOT = short + '-' + branch; // GitHub puts everything within this folder
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
dest
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
return await go(`https://github.com/${repo}/archive/refs/heads/${branch}.zip`, folder, GITHUB_ZIP_ROOT + '/' + DIST_ROOT);
|
|
56
|
+
async function go(url, folder, zipRoot) {
|
|
57
|
+
const installPath = plugins_1.PATH + '/' + folder;
|
|
58
|
+
const foldersToCopy = [
|
|
59
|
+
zipRoot + '-' + process.platform + '-' + process.arch,
|
|
60
|
+
zipRoot + '-' + process.platform,
|
|
61
|
+
zipRoot,
|
|
62
|
+
].map(x => x + '/');
|
|
63
|
+
// github zip doesn't have content-length, so we cannot produce progress event
|
|
64
|
+
const stream = await (0, misc_1.httpStream)(url);
|
|
65
|
+
const MAIN = 'plugin.js';
|
|
66
|
+
await (0, misc_1.unzip)(stream, async (path) => {
|
|
67
|
+
const folder = foldersToCopy.find(x => path.startsWith(x));
|
|
68
|
+
if (!folder || path.endsWith('/'))
|
|
69
|
+
return false;
|
|
70
|
+
let dest = path.slice(folder.length);
|
|
71
|
+
if (dest === MAIN) // avoid being possibly loaded before the download is complete
|
|
72
|
+
dest += plugins_1.DISABLING_POSTFIX;
|
|
73
|
+
dest = (0, path_1.join)(installPath, dest);
|
|
74
|
+
return (0, promises_1.rm)(dest, { force: true }).then(() => dest, () => false);
|
|
75
|
+
});
|
|
76
|
+
const main = (0, path_1.join)(installPath, MAIN);
|
|
77
|
+
await (0, promises_1.rename)(main + plugins_1.DISABLING_POSTFIX, main) // we are good now, restore name
|
|
78
|
+
.catch(e => { throw e.code !== 'ENOENT' ? e : new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "missing main file"); });
|
|
79
|
+
return folder;
|
|
80
|
+
}
|
|
65
81
|
}
|
|
66
82
|
finally {
|
|
67
83
|
downloadProgress(repo, undefined);
|
|
@@ -73,16 +89,39 @@ function getRepoInfo(id) {
|
|
|
73
89
|
}
|
|
74
90
|
exports.getRepoInfo = getRepoInfo;
|
|
75
91
|
function readGithubFile(uri) {
|
|
76
|
-
return (0, misc_1.
|
|
92
|
+
return (0, misc_1.httpString)('https://raw.githubusercontent.com/' + uri)
|
|
77
93
|
.then(res => res.body);
|
|
78
94
|
}
|
|
79
95
|
exports.readGithubFile = readGithubFile;
|
|
80
96
|
async function readOnlinePlugin(repo, branch = '') {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
if (typeof repo !== 'string') { // non-github plugin
|
|
98
|
+
const folder = lodash_1.default.findKey(getFolder2repo(), x => x === repo);
|
|
99
|
+
if (!folder)
|
|
100
|
+
throw Error();
|
|
101
|
+
const pl = (0, plugins_1.getPluginInfo)(folder);
|
|
102
|
+
let { main } = pl.repo;
|
|
103
|
+
if (!main)
|
|
104
|
+
throw Error("missing repo.main");
|
|
105
|
+
if (!main.includes('//'))
|
|
106
|
+
main = pl.repo.web + main;
|
|
107
|
+
const res = await (0, misc_1.httpString)(main);
|
|
108
|
+
if (!res.ok)
|
|
109
|
+
throw Error("bad repo.main");
|
|
110
|
+
return (0, plugins_1.parsePluginSource)(main, res.body); // use 'repo' as 'id' client-side
|
|
111
|
+
}
|
|
112
|
+
const branches = branch ? [branch] : (async function* () {
|
|
113
|
+
var _a;
|
|
114
|
+
yield 'main'; // getRepoInfo consumes github-api-quota, so give 'main' a shot first, and if it fails we'll ask
|
|
115
|
+
yield (_a = (await getRepoInfo(repo))) === null || _a === void 0 ? void 0 : _a.default_branch;
|
|
116
|
+
})();
|
|
117
|
+
for await (const b of branches) {
|
|
118
|
+
const res = await readGithubFile(`${repo}/${b}/${DIST_ROOT}/plugin.js`);
|
|
119
|
+
if (!res)
|
|
120
|
+
continue;
|
|
121
|
+
const pl = (0, plugins_1.parsePluginSource)(repo, res); // use 'repo' as 'id' client-side
|
|
122
|
+
pl.branch = b || undefined;
|
|
123
|
+
return pl;
|
|
124
|
+
}
|
|
86
125
|
}
|
|
87
126
|
exports.readOnlinePlugin = readOnlinePlugin;
|
|
88
127
|
function getFolder2repo() {
|
|
@@ -93,7 +132,7 @@ function getFolder2repo() {
|
|
|
93
132
|
exports.getFolder2repo = getFolder2repo;
|
|
94
133
|
async function apiGithub(uri) {
|
|
95
134
|
try {
|
|
96
|
-
const res = await (0, misc_1.
|
|
135
|
+
const res = await (0, misc_1.httpString)('https://api.github.com/' + uri, {
|
|
97
136
|
headers: {
|
|
98
137
|
'User-Agent': 'HFS',
|
|
99
138
|
Accept: 'application/vnd.github.v3+json',
|
|
@@ -118,7 +157,7 @@ async function* searchPlugins(text = '') {
|
|
|
118
157
|
if ((_a = projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.plugins_blacklist) === null || _a === void 0 ? void 0 : _a.includes(repo))
|
|
119
158
|
continue;
|
|
120
159
|
let pl = await readOnlinePlugin(repo, it.default_branch);
|
|
121
|
-
if (!pl.apiRequired)
|
|
160
|
+
if (!(pl === null || pl === void 0 ? void 0 : pl.apiRequired))
|
|
122
161
|
continue; // mandatory field
|
|
123
162
|
if (pl.badApi) { // we try other branches (starting with 'api')
|
|
124
163
|
const res = await apiGithub('repos/' + it.full_name + '/branches');
|
|
@@ -126,14 +165,16 @@ async function* searchPlugins(text = '') {
|
|
|
126
165
|
.filter((x) => typeof x === 'string' && x.startsWith('api'))
|
|
127
166
|
.sort().reverse();
|
|
128
167
|
for (const branch of branches) {
|
|
129
|
-
pl = await readOnlinePlugin(
|
|
168
|
+
pl = await readOnlinePlugin(repo, branch);
|
|
169
|
+
if (!pl)
|
|
170
|
+
continue;
|
|
130
171
|
if (!pl.apiRequired)
|
|
131
172
|
pl.badApi = '-';
|
|
132
173
|
if (!pl.badApi)
|
|
133
174
|
break;
|
|
134
175
|
}
|
|
135
176
|
}
|
|
136
|
-
if (pl.badApi)
|
|
177
|
+
if (!pl || pl.badApi)
|
|
137
178
|
continue;
|
|
138
179
|
Object.assign(pl, {
|
|
139
180
|
downloading: downloading[repo],
|
|
@@ -145,14 +186,19 @@ async function* searchPlugins(text = '') {
|
|
|
145
186
|
exports.searchPlugins = searchPlugins;
|
|
146
187
|
// centralized hosted information, to be used as little as possible
|
|
147
188
|
let cache;
|
|
189
|
+
const FN = 'central.json';
|
|
190
|
+
let latest = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', FN), 'utf8')); // initially built-in is our latest
|
|
148
191
|
function getProjectInfo() {
|
|
149
|
-
return cache || (cache = readGithubFile(const_1.HFS_REPO + '/main/
|
|
192
|
+
return cache || (cache = readGithubFile(const_1.HFS_REPO + '/main/' + FN)
|
|
193
|
+
.catch(() => latest) // fall back to latest
|
|
194
|
+
.then(x => {
|
|
150
195
|
if (!x)
|
|
151
196
|
throw x; // go catch
|
|
152
|
-
setTimeout(() => cache =
|
|
153
|
-
return JSON.parse(x);
|
|
197
|
+
setTimeout(() => cache = undefined, const_1.DAY); // invalidate cache
|
|
198
|
+
return latest = JSON.parse(x);
|
|
154
199
|
}).catch(() => {
|
|
155
|
-
setTimeout(() => cache =
|
|
200
|
+
setTimeout(() => cache = undefined, 10000); // invalidate cache sooner on errors
|
|
201
|
+
return latest;
|
|
156
202
|
}));
|
|
157
203
|
}
|
|
158
204
|
exports.getProjectInfo = getProjectInfo;
|
package/src/langs/embedded.js
CHANGED
|
@@ -16,4 +16,5 @@ const hfs_lang_vi_json_1 = __importDefault(require("./hfs-lang-vi.json"));
|
|
|
16
16
|
const hfs_lang_es_json_1 = __importDefault(require("./hfs-lang-es.json"));
|
|
17
17
|
const hfs_lang_nl_json_1 = __importDefault(require("./hfs-lang-nl.json"));
|
|
18
18
|
const hfs_lang_el_json_1 = __importDefault(require("./hfs-lang-el.json"));
|
|
19
|
-
|
|
19
|
+
const hfs_lang_de_json_1 = __importDefault(require("./hfs-lang-de.json"));
|
|
20
|
+
exports.default = { it: hfs_lang_it_json_1.default, zh: hfs_lang_zh_json_1.default, ru: hfs_lang_ru_json_1.default, sr: hfs_lang_sr_json_1.default, ko: hfs_lang_ko_json_1.default, ms: hfs_lang_ms_json_1.default, 'zh-tw': hfs_lang_zh_tw_json_1.default, fr: hfs_lang_fr_json_1.default, pt: hfs_lang_pt_json_1.default, vi: hfs_lang_vi_json_1.default, es: hfs_lang_es_json_1.default, nl: hfs_lang_nl_json_1.default, el: hfs_lang_el_json_1.default, de: hfs_lang_de_json_1.default };
|