hfs 0.49.0-alpha2 → 0.49.0-alpha3
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 +8 -17
- package/admin/assets/{index-6afd4b06.js → index-01a34c44.js} +49 -44
- package/admin/assets/{sha512-f5f12dc1.js → sha512-7ee6016e.js} +1 -1
- package/admin/index.html +1 -1
- package/central.json +12 -0
- package/frontend/assets/{index-3a379840.js → index-51e1daa3.js} +2 -2
- package/frontend/assets/{sha512-3392c31f.js → sha512-33a5ce93.js} +1 -1
- package/frontend/index.html +1 -1
- package/package.json +1 -1
- package/src/adminApis.js +9 -2
- package/src/api.net.js +74 -37
- package/src/commands.js +3 -10
- package/src/config.js +9 -6
- package/src/const.js +6 -29
- package/src/cross-const.js +31 -0
- package/src/cross.js +16 -1
- package/src/index.js +2 -1
- package/src/listen.js +1 -1
- package/src/misc.js +1 -1
- package/src/perm.js +16 -1
- package/src/plugins.js +2 -1
- package/src/serveFile.js +1 -1
- package/src/serveGuiFiles.js +2 -1
- package/src/update.js +13 -5
- package/src/util-files.js +3 -2
- package/src/util-http.js +1 -3
- package/src/watchLoad.js +6 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{g as OF,c as UF}from"./index-
|
|
1
|
+
import{g as OF,c as UF}from"./index-51e1daa3.js";function gF(sF,hF){for(var eF=0;eF<hF.length;eF++){const tF=hF[eF];if(typeof tF!="string"&&!Array.isArray(tF)){for(const w in tF)if(w!=="default"&&!(w in sF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(sF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(sF,Symbol.toStringTag,{value:"Module"}))}var dF={exports:{}};/*
|
|
2
2
|
* [js-sha512]{@link https://github.com/emn178/js-sha512}
|
|
3
3
|
*
|
|
4
4
|
* @version 0.8.0
|
package/frontend/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0" />
|
|
6
6
|
<link href="/fontello.css" rel="stylesheet" />
|
|
7
7
|
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-51e1daa3.js"></script>
|
|
9
9
|
<link rel="stylesheet" href="/assets/index-db5f0e4b.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/package.json
CHANGED
package/src/adminApis.js
CHANGED
|
@@ -53,6 +53,7 @@ const customHtml_1 = require("./customHtml");
|
|
|
53
53
|
const lodash_1 = __importDefault(require("lodash"));
|
|
54
54
|
const update_1 = require("./update");
|
|
55
55
|
const consoleLog_1 = require("./consoleLog");
|
|
56
|
+
const path_1 = require("path");
|
|
56
57
|
exports.adminApis = {
|
|
57
58
|
...api_vfs_1.default,
|
|
58
59
|
...api_accounts_1.default,
|
|
@@ -76,9 +77,15 @@ exports.adminApis = {
|
|
|
76
77
|
return {};
|
|
77
78
|
},
|
|
78
79
|
get_config: config_1.getWholeConfig,
|
|
79
|
-
|
|
80
|
-
return
|
|
80
|
+
get_config_text() {
|
|
81
|
+
return {
|
|
82
|
+
path: config_1.configFile.getPath(),
|
|
83
|
+
fullPath: (0, path_1.resolve)(config_1.configFile.getPath()),
|
|
84
|
+
text: config_1.configFile.getText(),
|
|
85
|
+
};
|
|
81
86
|
},
|
|
87
|
+
set_config_text: ({ text }) => config_1.configFile.save(text, { reparse: true }),
|
|
88
|
+
update: ({ tag }) => (0, update_1.update)(tag),
|
|
82
89
|
async check_update() {
|
|
83
90
|
return { options: await (0, update_1.getUpdates)() };
|
|
84
91
|
},
|
package/src/api.net.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.makeCert = exports.acme_email = exports.acme_domain = exports.acmeMiddleware = exports.externalIp = void 0;
|
|
7
|
+
exports.makeCert = exports.acme_email = exports.acme_domain = exports.selfCheckMiddleware = exports.acmeMiddleware = exports.externalIp = void 0;
|
|
8
8
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
9
9
|
const nat_upnp_ts_1 = require("nat-upnp-ts");
|
|
10
10
|
const const_1 = require("./const");
|
|
@@ -27,9 +27,13 @@ const originalMethod = upnpClient.getGateway;
|
|
|
27
27
|
upnpClient.getGateway = (0, misc_1.debounceAsync)(() => originalMethod.apply(upnpClient), 0, { retain: misc_1.HOUR, retainFailure: 30000 });
|
|
28
28
|
upnpClient.getGateway().catch(() => { });
|
|
29
29
|
exports.externalIp = ''; // poll external ip
|
|
30
|
-
(0, misc_1.repeat)(10 * misc_1.MINUTE, () => upnpClient.getPublicIp().then(v =>
|
|
30
|
+
(0, misc_1.repeat)(10 * misc_1.MINUTE, () => upnpClient.getPublicIp().then(v => {
|
|
31
|
+
if (v !== exports.externalIp)
|
|
32
|
+
getPublicIps.clearRetain();
|
|
33
|
+
return exports.externalIp = v;
|
|
34
|
+
}));
|
|
31
35
|
const getNatInfo = (0, misc_1.debounceAsync)(async () => {
|
|
32
|
-
var _a, _b;
|
|
36
|
+
var _a, _b, _c, _d;
|
|
33
37
|
const gettingIps = getPublicIps(); // don't wait, do it in parallel
|
|
34
38
|
const res = await upnpClient.getGateway().catch(() => null);
|
|
35
39
|
const status = await (0, listen_1.getServerStatus)();
|
|
@@ -48,9 +52,10 @@ const getNatInfo = (0, misc_1.debounceAsync)(async () => {
|
|
|
48
52
|
mapped,
|
|
49
53
|
internalPort,
|
|
50
54
|
externalPort: mapped === null || mapped === void 0 ? void 0 : mapped.public.port,
|
|
55
|
+
proto: ((_c = status === null || status === void 0 ? void 0 : status.https) === null || _c === void 0 ? void 0 : _c.listening) ? 'https' : ((_d = status === null || status === void 0 ? void 0 : status.http) === null || _d === void 0 ? void 0 : _d.listening) ? 'http' : undefined,
|
|
51
56
|
};
|
|
52
57
|
});
|
|
53
|
-
|
|
58
|
+
const getPublicIps = (0, misc_1.debounceAsync)(async () => {
|
|
54
59
|
const res = await (0, github_1.getProjectInfo)();
|
|
55
60
|
const groupedByVersion = Object.values(lodash_1.default.groupBy(res.publicIpServices, x => { var _a; return (_a = x.v) !== null && _a !== void 0 ? _a : 4; }));
|
|
56
61
|
const ips = await (0, misc_1.promiseBestEffort)(groupedByVersion.map(singleVersion => Promise.any(singleVersion.map(async (svc) => {
|
|
@@ -71,7 +76,7 @@ async function getPublicIps() {
|
|
|
71
76
|
return validIps;
|
|
72
77
|
}))));
|
|
73
78
|
return lodash_1.default.uniq(ips.flat());
|
|
74
|
-
}
|
|
79
|
+
}, 0, { retain: 10 * misc_1.MINUTE });
|
|
75
80
|
function findGateway() {
|
|
76
81
|
return new Promise((resolve, reject) => (0, child_process_1.exec)(const_1.IS_WINDOWS || const_1.IS_MAC ? 'netstat -rn' : 'route -n', (err, out) => {
|
|
77
82
|
var _a;
|
|
@@ -99,6 +104,14 @@ const acmeMiddleware = (ctx, next) => {
|
|
|
99
104
|
return next();
|
|
100
105
|
};
|
|
101
106
|
exports.acmeMiddleware = acmeMiddleware;
|
|
107
|
+
let selfCheckMiddlewareEnabled = false;
|
|
108
|
+
const CHECK_URL = const_1.SPECIAL_URI + 'self-check';
|
|
109
|
+
const selfCheckMiddleware = (ctx, next) => {
|
|
110
|
+
if (!selfCheckMiddlewareEnabled || !ctx.url.startsWith(CHECK_URL))
|
|
111
|
+
return next();
|
|
112
|
+
ctx.body = 'HFS';
|
|
113
|
+
};
|
|
114
|
+
exports.selfCheckMiddleware = selfCheckMiddleware;
|
|
102
115
|
async function checkDomain(domain) {
|
|
103
116
|
const resolver = new promises_2.Resolver();
|
|
104
117
|
const prjInfo = await (0, github_1.getProjectInfo)();
|
|
@@ -117,11 +130,10 @@ async function checkDomain(domain) {
|
|
|
117
130
|
const domainIpsThisVersion = domainIps.filter(x => (0, net_1.isIPv6)(x) === v6);
|
|
118
131
|
const ipsThisVersion = publicIps.filter(x => (0, net_1.isIPv6)(x) === v6);
|
|
119
132
|
if (domainIpsThisVersion.length && ipsThisVersion.length && !lodash_1.default.intersection(domainIpsThisVersion, ipsThisVersion).length)
|
|
120
|
-
throw new apiMiddleware_1.ApiError(const_1.
|
|
133
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_PRECONDITION_FAILED, `configure your domain to point to ${ipsThisVersion} (currently on ${domainIpsThisVersion[0]}) – a change can take hours to be effective`);
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
136
|
async function generateSSLCert(domain, email) {
|
|
124
|
-
await checkDomain(domain);
|
|
125
137
|
// will answer challenge through our koa app (if on port 80) or must we spawn a dedicated server?
|
|
126
138
|
const { upnp, externalPort } = await getNatInfo();
|
|
127
139
|
const { http } = await (0, listen_1.getServerStatus)();
|
|
@@ -134,15 +146,16 @@ async function generateSSLCert(domain, email) {
|
|
|
134
146
|
acmeMiddlewareEnabled = true;
|
|
135
147
|
console.debug('acme challenge server ready');
|
|
136
148
|
try {
|
|
137
|
-
|
|
149
|
+
const checkUrl = `http://${domain}`;
|
|
150
|
+
let check = await selfCheck(checkUrl); // some check services may not consider the domain, but we already verified that
|
|
138
151
|
if (check && !check.success && upnp && externalPort !== 80) { // consider a short-lived mapping
|
|
152
|
+
console.debug("setting temporary port forward");
|
|
139
153
|
// @ts-ignore
|
|
140
154
|
await upnpClient.createMapping({ private: 80, public: { host: '', port: 80 }, description: 'hfs temporary', ttl: 30 }).catch(() => { });
|
|
141
|
-
check = await
|
|
155
|
+
check = await selfCheck(checkUrl); // repeat test
|
|
142
156
|
}
|
|
143
|
-
if (!check)
|
|
144
|
-
|
|
145
|
-
if (!check.success)
|
|
157
|
+
//if (!check) throw new ApiError(HTTP_FAILED_DEPENDENCY, "couldn't test port 80")
|
|
158
|
+
if (!(check === null || check === void 0 ? void 0 : check.success))
|
|
146
159
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "port 80 is not working on the specified domain");
|
|
147
160
|
const acmeClient = new acme_client_1.default.Client({
|
|
148
161
|
accountKey: await acme_client_1.default.crypto.createPrivateKey(),
|
|
@@ -172,29 +185,48 @@ async function generateSSLCert(domain, email) {
|
|
|
172
185
|
console.debug('acme terminated');
|
|
173
186
|
}
|
|
174
187
|
}
|
|
175
|
-
async function
|
|
188
|
+
async function checkService(url, serviceKey) {
|
|
189
|
+
var _a;
|
|
176
190
|
const prjInfo = await (0, github_1.getProjectInfo)();
|
|
177
|
-
console.log(`checking server ${
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
console.log(`checking server ${url}`);
|
|
192
|
+
selfCheckMiddlewareEnabled = true;
|
|
193
|
+
try {
|
|
194
|
+
const parsed = new URL(url);
|
|
195
|
+
const family = !(0, net_1.isIP)(parsed.hostname) ? undefined : (0, net_1.isIPv6)(parsed.hostname) ? 6 : 4;
|
|
196
|
+
for (const services of lodash_1.default.chunk(lodash_1.default.shuffle(prjInfo[serviceKey]), 2)) {
|
|
197
|
+
try {
|
|
198
|
+
return await Promise.any(services.map(async (svc) => {
|
|
199
|
+
if (!svc.url || svc.type)
|
|
200
|
+
throw 'unsupported ' + svc.type; // only default type supported for now
|
|
201
|
+
let { url: serviceUrl, body, regexpSuccess, regexpFailure, ...rest } = svc;
|
|
202
|
+
const service = new URL(serviceUrl).hostname;
|
|
203
|
+
console.log('trying external service', service);
|
|
204
|
+
body = applySymbols(body);
|
|
205
|
+
serviceUrl = applySymbols(serviceUrl);
|
|
206
|
+
const res = await (0, misc_1.haveTimeout)(10000, (0, util_http_1.httpString)(serviceUrl, { family, ...rest, body }));
|
|
207
|
+
const success = new RegExp(regexpSuccess).test(res);
|
|
208
|
+
const failure = new RegExp(regexpFailure).test(res);
|
|
209
|
+
if (success === failure)
|
|
210
|
+
throw 'inconsistent: ' + service + ': ' + res; // this result cannot be trusted
|
|
211
|
+
console.debug(service, 'responded', success);
|
|
212
|
+
return { success, service, url };
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
console.debug(((_a = e === null || e === void 0 ? void 0 : e.errors) === null || _a === void 0 ? void 0 : _a.map(String)) || (e === null || e === void 0 ? void 0 : e.cause) || String(e));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function applySymbols(s) {
|
|
220
|
+
return s === null || s === void 0 ? void 0 : s.replace('$IP', parsed.hostname).replace('$PORT', parsed.port || (parsed.protocol === 'https:' ? '443' : '80')).replace('$URL', url.replace(/\/$/, '') + CHECK_URL);
|
|
191
221
|
}
|
|
192
|
-
catch (_a) { }
|
|
193
222
|
}
|
|
194
|
-
|
|
195
|
-
|
|
223
|
+
finally {
|
|
224
|
+
selfCheckMiddlewareEnabled = false;
|
|
196
225
|
}
|
|
197
226
|
}
|
|
227
|
+
function selfCheck(url) {
|
|
228
|
+
return checkService(url, 'selfCheckServices');
|
|
229
|
+
}
|
|
198
230
|
const apis = {
|
|
199
231
|
get_nat: getNatInfo,
|
|
200
232
|
check_domain({ domain }) {
|
|
@@ -218,15 +250,20 @@ const apis = {
|
|
|
218
250
|
await upnpClient.createMapping({ private: internal || internalPort, public: { host: '', port: external }, description: 'hfs', ttl: 0 });
|
|
219
251
|
return {};
|
|
220
252
|
},
|
|
221
|
-
async
|
|
222
|
-
|
|
223
|
-
|
|
253
|
+
async self_check({ url }) {
|
|
254
|
+
if (url)
|
|
255
|
+
return await selfCheck(url)
|
|
256
|
+
|| new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
|
|
257
|
+
const nat = await getNatInfo();
|
|
258
|
+
if (!nat.publicIps.length)
|
|
224
259
|
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'cannot detect public ip');
|
|
225
|
-
if (!internalPort)
|
|
260
|
+
if (!nat.internalPort)
|
|
226
261
|
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'no internal port');
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
262
|
+
const finalPort = nat.externalPort || nat.internalPort;
|
|
263
|
+
const proto = nat.proto || ((0, listen_1.getCertObject)() ? 'https' : 'http');
|
|
264
|
+
const defPort = proto === 'https' ? 443 : 80;
|
|
265
|
+
const results = (0, misc_1.onlyTruthy)(await (0, misc_1.promiseBestEffort)(nat.publicIps.map(ip => selfCheck(`${proto}://${ip}${finalPort === defPort ? '' : ':' + finalPort}`))));
|
|
266
|
+
return results.length ? results : new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
|
|
230
267
|
},
|
|
231
268
|
async make_cert({ domain, email }) {
|
|
232
269
|
await (0, exports.makeCert)(domain, email);
|
package/src/commands.js
CHANGED
|
@@ -57,14 +57,7 @@ const commands = {
|
|
|
57
57
|
},
|
|
58
58
|
'create-admin': {
|
|
59
59
|
params: '<password> [<username>=admin]',
|
|
60
|
-
|
|
61
|
-
if ((0, perm_1.getAccount)(username))
|
|
62
|
-
throw `user ${username} already exists`;
|
|
63
|
-
const acc = (0, perm_1.addAccount)(username, { admin: true });
|
|
64
|
-
await (0, perm_1.updateAccount)(acc, acc => {
|
|
65
|
-
acc.password = password;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
60
|
+
cb: perm_1.createAdmin
|
|
68
61
|
},
|
|
69
62
|
'change-password': {
|
|
70
63
|
params: '<user> <password>',
|
|
@@ -105,8 +98,8 @@ const commands = {
|
|
|
105
98
|
}
|
|
106
99
|
},
|
|
107
100
|
update: {
|
|
108
|
-
params: '',
|
|
109
|
-
cb:
|
|
101
|
+
params: '[<version>=latest]',
|
|
102
|
+
cb: update_1.update
|
|
110
103
|
},
|
|
111
104
|
'check-update': {
|
|
112
105
|
params: '',
|
package/src/config.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.saveConfigAsap = exports.setConfig = exports.getWholeConfig = exports.getConfig = exports.configKeyExists = exports.defineConfig = exports.currentVersion = exports.versionToScalar = void 0;
|
|
7
|
+
exports.configFile = exports.saveConfigAsap = exports.setConfig = exports.getWholeConfig = exports.getConfig = exports.configKeyExists = exports.defineConfig = exports.currentVersion = exports.versionToScalar = void 0;
|
|
8
8
|
const events_1 = __importDefault(require("events"));
|
|
9
9
|
const const_1 = require("./const");
|
|
10
10
|
const watchLoad_1 = require("./watchLoad");
|
|
@@ -70,12 +70,12 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
70
70
|
cb(getConfig(k), { k, was: defaultValue, defaultValue, version: configVersion.compiled() });
|
|
71
71
|
const eventName = CONFIG_CHANGE_EVENT_PREFIX + k;
|
|
72
72
|
return (0, misc_1.onOff)(cfgEvents, {
|
|
73
|
-
[eventName]() {
|
|
73
|
+
[eventName](v, was, version) {
|
|
74
74
|
if (stack.includes(cb))
|
|
75
75
|
return; // avoid infinite loop in case a subscriber changes the value
|
|
76
|
-
stack.push(cb);
|
|
76
|
+
stack.push(cb);
|
|
77
77
|
try {
|
|
78
|
-
return cb
|
|
78
|
+
return cb(v, { k, was, version, defaultValue });
|
|
79
79
|
}
|
|
80
80
|
finally {
|
|
81
81
|
stack.pop();
|
|
@@ -177,13 +177,16 @@ const saveDebounced = (0, misc_1.debounceAsync)(async () => {
|
|
|
177
177
|
const aWeekAgo = Date.now() - misc_1.DAY * 7;
|
|
178
178
|
if (await (0, promises_1.stat)(bak).then(x => aWeekAgo > Number(x.mtime || x.ctime), () => true))
|
|
179
179
|
await (0, promises_1.copyFile)(filePath, bak).catch(() => { }); // ignore errors
|
|
180
|
-
await save(
|
|
180
|
+
await exports.configFile.save(stringify({ ...state, version: const_1.VERSION }))
|
|
181
181
|
.catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
|
|
182
182
|
});
|
|
183
183
|
const saveConfigAsap = () => void (saveDebounced());
|
|
184
184
|
exports.saveConfigAsap = saveConfigAsap;
|
|
185
|
+
function stringify(obj) {
|
|
186
|
+
return yaml_1.default.stringify(obj, { lineWidth: 1000 });
|
|
187
|
+
}
|
|
185
188
|
console.log("config", filePath);
|
|
186
|
-
|
|
189
|
+
exports.configFile = (0, watchLoad_1.watchLoad)(filePath, text => setConfig(yaml_1.default.parse(text, { uniqueKeys: false }) || {}, false), {
|
|
187
190
|
failedOnFirstAttempt() {
|
|
188
191
|
console.log("No config file, using defaults");
|
|
189
192
|
setConfig({}, false);
|
package/src/const.js
CHANGED
|
@@ -23,53 +23,30 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
__setModuleDefault(result, mod);
|
|
24
24
|
return result;
|
|
25
25
|
};
|
|
26
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
27
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
28
|
+
};
|
|
26
29
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
30
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
31
|
};
|
|
29
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.APP_PATH = exports.IS_BINARY = exports.IS_MAC = exports.IS_WINDOWS = exports.
|
|
33
|
+
exports.APP_PATH = exports.IS_BINARY = exports.IS_MAC = exports.IS_WINDOWS = exports.HFS_REPO_BRANCH = exports.RUNNING_BETA = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
|
|
31
34
|
const minimist_1 = __importDefault(require("minimist"));
|
|
32
35
|
const fs = __importStar(require("fs"));
|
|
33
36
|
const os_1 = require("os");
|
|
34
37
|
const fs_1 = require("fs");
|
|
35
38
|
const path_1 = require("path");
|
|
39
|
+
__exportStar(require("./cross-const"), exports);
|
|
36
40
|
exports.argv = (0, minimist_1.default)(process.argv.slice(2));
|
|
37
41
|
exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
|
|
38
42
|
exports.ORIGINAL_CWD = process.cwd();
|
|
39
43
|
exports.HFS_STARTED = new Date();
|
|
40
44
|
const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
|
|
41
|
-
exports.BUILD_TIMESTAMP = "2023-10-
|
|
45
|
+
exports.BUILD_TIMESTAMP = "2023-10-12T14:44:47.246Z";
|
|
42
46
|
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
43
47
|
exports.VERSION = pkg.version;
|
|
44
48
|
exports.RUNNING_BETA = exports.VERSION.includes('-');
|
|
45
|
-
exports.API_VERSION = 8.4;
|
|
46
|
-
exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
|
|
47
|
-
exports.HFS_REPO = 'rejetto/hfs';
|
|
48
49
|
exports.HFS_REPO_BRANCH = exports.RUNNING_BETA ? 'next' : 'main';
|
|
49
|
-
exports.SPECIAL_URI = '/~/';
|
|
50
|
-
exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
|
|
51
|
-
exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
|
|
52
|
-
exports.API_URI = exports.SPECIAL_URI + 'api/';
|
|
53
|
-
exports.PLUGINS_PUB_URI = exports.SPECIAL_URI + 'plugins/';
|
|
54
|
-
exports.HTTP_OK = 200;
|
|
55
|
-
exports.HTTP_NO_CONTENT = 204;
|
|
56
|
-
exports.HTTP_PARTIAL_CONTENT = 206;
|
|
57
|
-
exports.HTTP_MOVED_PERMANENTLY = 301;
|
|
58
|
-
exports.HTTP_TEMPORARY_REDIRECT = 302;
|
|
59
|
-
exports.HTTP_NOT_MODIFIED = 304;
|
|
60
|
-
exports.HTTP_BAD_REQUEST = 400;
|
|
61
|
-
exports.HTTP_UNAUTHORIZED = 401;
|
|
62
|
-
exports.HTTP_FORBIDDEN = 403;
|
|
63
|
-
exports.HTTP_NOT_FOUND = 404;
|
|
64
|
-
exports.HTTP_METHOD_NOT_ALLOWED = 405;
|
|
65
|
-
exports.HTTP_NOT_ACCEPTABLE = 406;
|
|
66
|
-
exports.HTTP_CONFLICT = 409;
|
|
67
|
-
exports.HTTP_PAYLOAD_TOO_LARGE = 413;
|
|
68
|
-
exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
|
|
69
|
-
exports.HTTP_FOOL = 418;
|
|
70
|
-
exports.HTTP_FAILED_DEPENDENCY = 424;
|
|
71
|
-
exports.HTTP_SERVER_ERROR = 500;
|
|
72
|
-
exports.HTTP_SERVICE_UNAVAILABLE = 503;
|
|
73
50
|
exports.IS_WINDOWS = process.platform === 'win32';
|
|
74
51
|
exports.IS_MAC = process.platform === 'darwin';
|
|
75
52
|
exports.IS_BINARY = !(0, path_1.basename)(process.execPath).includes('node'); // this won't be node if pkg was used
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
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_PRECONDITION_FAILED = 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_MOVED_PERMANENTLY = 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 = void 0;
|
|
4
|
+
exports.API_VERSION = 8.4;
|
|
5
|
+
exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
|
|
6
|
+
exports.HFS_REPO = 'rejetto/hfs';
|
|
7
|
+
exports.SPECIAL_URI = '/~/';
|
|
8
|
+
exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
|
|
9
|
+
exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
|
|
10
|
+
exports.API_URI = exports.SPECIAL_URI + 'api/';
|
|
11
|
+
exports.PLUGINS_PUB_URI = exports.SPECIAL_URI + 'plugins/';
|
|
12
|
+
exports.HTTP_OK = 200;
|
|
13
|
+
exports.HTTP_NO_CONTENT = 204;
|
|
14
|
+
exports.HTTP_PARTIAL_CONTENT = 206;
|
|
15
|
+
exports.HTTP_MOVED_PERMANENTLY = 301;
|
|
16
|
+
exports.HTTP_TEMPORARY_REDIRECT = 302;
|
|
17
|
+
exports.HTTP_NOT_MODIFIED = 304;
|
|
18
|
+
exports.HTTP_BAD_REQUEST = 400;
|
|
19
|
+
exports.HTTP_UNAUTHORIZED = 401;
|
|
20
|
+
exports.HTTP_FORBIDDEN = 403;
|
|
21
|
+
exports.HTTP_NOT_FOUND = 404;
|
|
22
|
+
exports.HTTP_METHOD_NOT_ALLOWED = 405;
|
|
23
|
+
exports.HTTP_NOT_ACCEPTABLE = 406;
|
|
24
|
+
exports.HTTP_CONFLICT = 409;
|
|
25
|
+
exports.HTTP_PRECONDITION_FAILED = 412;
|
|
26
|
+
exports.HTTP_PAYLOAD_TOO_LARGE = 413;
|
|
27
|
+
exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
|
|
28
|
+
exports.HTTP_FOOL = 418;
|
|
29
|
+
exports.HTTP_FAILED_DEPENDENCY = 424;
|
|
30
|
+
exports.HTTP_SERVER_ERROR = 500;
|
|
31
|
+
exports.HTTP_SERVICE_UNAVAILABLE = 503;
|
package/src/cross.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
18
|
};
|
|
@@ -8,6 +22,7 @@ exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.ipLo
|
|
|
8
22
|
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
9
23
|
// all content here is shared between client and server
|
|
10
24
|
const lodash_1 = __importDefault(require("lodash"));
|
|
25
|
+
__exportStar(require("./cross-const"), exports);
|
|
11
26
|
exports.REPO_URL = 'https://github.com/rejetto/hfs/';
|
|
12
27
|
exports.WIKI_URL = exports.REPO_URL + 'wiki/';
|
|
13
28
|
exports.MINUTE = 60000;
|
|
@@ -50,7 +65,7 @@ function wait(ms, val) {
|
|
|
50
65
|
}
|
|
51
66
|
exports.wait = wait;
|
|
52
67
|
function haveTimeout(ms, job, error) {
|
|
53
|
-
return Promise.race([job, wait(ms).then(() => { throw error; })]);
|
|
68
|
+
return Promise.race([job, wait(ms).then(() => { throw error || Error('timeout'); })]);
|
|
54
69
|
}
|
|
55
70
|
exports.haveTimeout = haveTimeout;
|
|
56
71
|
function objSameKeys(src, newValue) {
|
package/src/index.js
CHANGED
|
@@ -31,6 +31,7 @@ process.title = 'HFS ' + const_1.VERSION;
|
|
|
31
31
|
const keys = ((_a = process.env.COOKIE_SIGN_KEYS) === null || _a === void 0 ? void 0 : _a.split(',')) || [(0, misc_1.randomId)(30)];
|
|
32
32
|
exports.app = new koa_1.default({ keys });
|
|
33
33
|
exports.app.use(middlewares_1.someSecurity)
|
|
34
|
+
.use(api_net_1.selfCheckMiddleware)
|
|
34
35
|
.use(api_net_1.acmeMiddleware)
|
|
35
36
|
.use((0, koa_session_1.default)({ key: 'hfs_$id', signed: true, rolling: true, sameSite: 'lax' }, exports.app))
|
|
36
37
|
.use(middlewares_1.prepareState)
|
|
@@ -56,7 +57,7 @@ function errorHandler(err) {
|
|
|
56
57
|
}
|
|
57
58
|
process.on('uncaughtException', err => {
|
|
58
59
|
if (err.syscall !== 'watch')
|
|
59
|
-
console.error(err);
|
|
60
|
+
console.error("uncaught:", err);
|
|
60
61
|
});
|
|
61
62
|
(0, config_1.defineConfig)('proxies', 0).sub(n => {
|
|
62
63
|
exports.app.proxy = n > 0;
|
package/src/listen.js
CHANGED
|
@@ -212,7 +212,7 @@ function startServer(srv, { port, host }) {
|
|
|
212
212
|
}
|
|
213
213
|
console.error(srv.name, srv.error);
|
|
214
214
|
const k = (srv === httpSrv ? exports.portCfg : exports.httpsPortCfg).key();
|
|
215
|
-
console.log(` >> try specifying a different port, enter this command: config ${k}
|
|
215
|
+
console.log(` >> try specifying a different port, enter this command: config ${k} 1080`);
|
|
216
216
|
resolve(0);
|
|
217
217
|
}
|
|
218
218
|
});
|
package/src/misc.js
CHANGED
|
@@ -106,7 +106,7 @@ function parseAddress(s) {
|
|
|
106
106
|
return new node_net_1.SocketAddress({ address: s, family: (0, net_1.isIPv6)(s) ? 'ipv6' : 'ipv4' });
|
|
107
107
|
}
|
|
108
108
|
function makeMatcher(mask, emptyMaskReturns = false) {
|
|
109
|
-
return mask ? (0, micromatch_1.matcher)(mask.replace(/^(!)?/, '$1(') + ')') // adding () will allow us to use the pipe at root level
|
|
109
|
+
return mask ? (0, micromatch_1.matcher)(mask.replace(/^(!)?/, '$1(') + ')', { nocase: true }) // adding () will allow us to use the pipe at root level
|
|
110
110
|
: () => emptyMaskReturns;
|
|
111
111
|
}
|
|
112
112
|
exports.makeMatcher = makeMatcher;
|
package/src/perm.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.expandUsername = exports.getCurrentUsername = void 0;
|
|
7
|
+
exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.createAdmin = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.expandUsername = exports.getCurrentUsername = void 0;
|
|
8
8
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
9
|
const crypt_1 = require("./crypt");
|
|
10
10
|
const misc_1 = require("./misc");
|
|
@@ -43,6 +43,21 @@ function saveSrpInfo(account, salt, verifier) {
|
|
|
43
43
|
}
|
|
44
44
|
exports.saveSrpInfo = saveSrpInfo;
|
|
45
45
|
exports.allowClearTextLogin = (0, config_1.defineConfig)('allow_clear_text_login', false);
|
|
46
|
+
const createAdminConfig = (0, config_1.defineConfig)('create-admin', '');
|
|
47
|
+
createAdminConfig.sub(v => {
|
|
48
|
+
if (!v)
|
|
49
|
+
return;
|
|
50
|
+
createAdmin(v);
|
|
51
|
+
createAdminConfig.set('');
|
|
52
|
+
});
|
|
53
|
+
function createAdmin(pass, username = 'admin') {
|
|
54
|
+
const acc = addAccount(username, { admin: true });
|
|
55
|
+
if (!acc)
|
|
56
|
+
return console.log("cannot create, already exists");
|
|
57
|
+
updateAccount(acc, acc => { acc.password = pass; });
|
|
58
|
+
console.log("account admin created");
|
|
59
|
+
}
|
|
60
|
+
exports.createAdmin = createAdmin;
|
|
46
61
|
const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters());
|
|
47
62
|
async function updateAccount(account, changer) {
|
|
48
63
|
const was = JSON.stringify(account);
|
package/src/plugins.js
CHANGED
|
@@ -35,6 +35,7 @@ const const_1 = require("./const");
|
|
|
35
35
|
const Const = __importStar(require("./const"));
|
|
36
36
|
const misc_1 = require("./misc");
|
|
37
37
|
const config_1 = require("./config");
|
|
38
|
+
const vfs_1 = require("./vfs");
|
|
38
39
|
const serveFile_1 = require("./serveFile");
|
|
39
40
|
const events_1 = __importDefault(require("./events"));
|
|
40
41
|
const promises_1 = require("fs/promises");
|
|
@@ -153,7 +154,7 @@ const pluginsMiddleware = async (ctx, next) => {
|
|
|
153
154
|
const a = path.substring(const_1.PLUGINS_PUB_URI.length).split('/');
|
|
154
155
|
const name = a.shift();
|
|
155
156
|
if (plugins.hasOwnProperty(name)) // do it only if the plugin is loaded
|
|
156
|
-
await (0, serveFile_1.serveFile)(ctx, plugins[name].folder + '/public/' + a.join('/'),
|
|
157
|
+
await (0, serveFile_1.serveFile)(ctx, plugins[name].folder + '/public/' + a.join('/'), vfs_1.MIME_AUTO);
|
|
157
158
|
return;
|
|
158
159
|
}
|
|
159
160
|
await next();
|
package/src/serveFile.js
CHANGED
|
@@ -38,7 +38,7 @@ function serveFileNode(ctx, node) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
exports.serveFileNode = serveFileNode;
|
|
41
|
-
const mimeCfg = (0, config_1.defineConfig)('mime', { '*':
|
|
41
|
+
const mimeCfg = (0, config_1.defineConfig)('mime', { '*': vfs_1.MIME_AUTO });
|
|
42
42
|
async function serveFile(ctx, source, mime, content) {
|
|
43
43
|
if (!source)
|
|
44
44
|
return;
|
package/src/serveGuiFiles.js
CHANGED
|
@@ -42,6 +42,7 @@ const customHtml_1 = require("./customHtml");
|
|
|
42
42
|
const lodash_1 = __importDefault(require("lodash"));
|
|
43
43
|
const config_1 = require("./config");
|
|
44
44
|
const lang_1 = require("./lang");
|
|
45
|
+
const vfs_1 = require("./vfs");
|
|
45
46
|
const logGui = (0, config_1.defineConfig)('log_gui', false);
|
|
46
47
|
// in case of dev env we have our static files within the 'dist' folder'
|
|
47
48
|
const DEV_STATIC = process.env.DEV ? 'dist/' : '';
|
|
@@ -69,7 +70,7 @@ function serveStatic(uri) {
|
|
|
69
70
|
if (content === null)
|
|
70
71
|
return ctx.status = const_1.HTTP_NOT_FOUND;
|
|
71
72
|
if (!serveApp)
|
|
72
|
-
return (0, serveFile_1.serveFile)(ctx, fullPath,
|
|
73
|
+
return (0, serveFile_1.serveFile)(ctx, fullPath, vfs_1.MIME_AUTO, content);
|
|
73
74
|
// we don't cache the index as it's small and may prevent plugins change to apply
|
|
74
75
|
ctx.body = await treatIndex(ctx, uri, String(content));
|
|
75
76
|
};
|
package/src/update.js
CHANGED
|
@@ -63,15 +63,22 @@ function updateSupported() {
|
|
|
63
63
|
return const_1.IS_BINARY;
|
|
64
64
|
}
|
|
65
65
|
exports.updateSupported = updateSupported;
|
|
66
|
-
async function update(tag) {
|
|
66
|
+
async function update(tag = '') {
|
|
67
67
|
if (!updateSupported())
|
|
68
|
-
throw "only binary versions
|
|
68
|
+
throw "only binary versions supports automatic update for now";
|
|
69
69
|
let updateSource = await localUpdateAvailable() && (0, fs_1.createReadStream)(LOCAL_UPDATE);
|
|
70
70
|
if (!updateSource) {
|
|
71
|
+
if (/^\d/.test(tag)) // work even if the tag is passed without the initial 'v' (useful for console commands)
|
|
72
|
+
tag = 'v' + tag;
|
|
71
73
|
const update = !tag ? (await getUpdates())[0]
|
|
72
|
-
: await (0, github_1.getRepoInfo)(const_1.HFS_REPO + '/releases/tags/' + tag)
|
|
74
|
+
: await (0, github_1.getRepoInfo)(const_1.HFS_REPO + '/releases/tags/' + tag).catch(e => {
|
|
75
|
+
if (e.message === '404')
|
|
76
|
+
console.error("version not found");
|
|
77
|
+
else
|
|
78
|
+
throw e;
|
|
79
|
+
});
|
|
73
80
|
if (!update)
|
|
74
|
-
throw "
|
|
81
|
+
throw "update not found";
|
|
75
82
|
const assetSearch = { win32: 'windows', darwin: 'mac', linux: 'linux' }[process.platform];
|
|
76
83
|
if (!assetSearch)
|
|
77
84
|
throw "this feature doesn't support your platform: " + process.platform;
|
|
@@ -109,7 +116,8 @@ async function update(tag) {
|
|
|
109
116
|
console.log('quitting');
|
|
110
117
|
setTimeout(() => process.exit()); // give time to return (and caller to complete, eg: rest api to reply)
|
|
111
118
|
}
|
|
112
|
-
catch (
|
|
119
|
+
catch (e) {
|
|
120
|
+
console.error((e === null || e === void 0 ? void 0 : e.message) || String(e));
|
|
113
121
|
plugins_1.pluginsWatcher.unpause();
|
|
114
122
|
}
|
|
115
123
|
}
|
package/src/util-files.js
CHANGED
|
@@ -138,8 +138,9 @@ async function* dirStream(path, deep = 0) {
|
|
|
138
138
|
exports.dirStream = dirStream;
|
|
139
139
|
async function unzip(stream, cb) {
|
|
140
140
|
let pending = Promise.resolve();
|
|
141
|
-
return new Promise(resolve => stream.pipe(unzip_stream_1.default.Parse())
|
|
141
|
+
return new Promise((resolve, reject) => stream.pipe(unzip_stream_1.default.Parse())
|
|
142
142
|
.on('end', () => pending.then(resolve))
|
|
143
|
+
.on('error', reject)
|
|
143
144
|
.on('entry', (entry) => pending = pending.then(async () => {
|
|
144
145
|
const { path, type } = entry;
|
|
145
146
|
const dest = await (0, misc_1.try_)(() => cb(path), e => {
|
|
@@ -150,7 +151,7 @@ async function unzip(stream, cb) {
|
|
|
150
151
|
return entry.autodrain();
|
|
151
152
|
console.debug('unzip', dest);
|
|
152
153
|
await prepareFolder(dest);
|
|
153
|
-
const thisFile = entry.pipe((0, fs_1.createWriteStream)(dest));
|
|
154
|
+
const thisFile = entry.pipe((0, fs_1.createWriteStream)(dest).on('error', reject));
|
|
154
155
|
await (0, stream_1.once)(thisFile, 'finish');
|
|
155
156
|
})));
|
|
156
157
|
}
|
package/src/util-http.js
CHANGED