hfs 0.49.0 → 0.49.2
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 +2 -0
- package/admin/assets/{index-3c80e1c3.js → index-79c48481.js} +53 -53
- package/{frontend/assets/sha512-37516713.js → admin/assets/sha512-bdc531ce.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/{index-0be475af.js → index-5f1c6cb8.js} +1 -1
- package/frontend/assets/{index-669a2741.css → index-b50efd06.css} +1 -1
- package/{admin/assets/sha512-03998350.js → frontend/assets/sha512-da48f0d4.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +1 -1
- package/src/acme.js +14 -14
- package/src/adminApis.js +1 -1
- package/src/block.js +1 -1
- package/src/const.js +1 -1
- package/src/cross.js +23 -1
- package/src/listen.js +16 -5
- package/src/nat.js +1 -0
- package/src/selfCheck.js +26 -36
package/package.json
CHANGED
package/src/acme.js
CHANGED
|
@@ -21,7 +21,7 @@ const acmeListener = (req, res) => {
|
|
|
21
21
|
var _a;
|
|
22
22
|
const BASE = '/.well-known/acme-challenge/';
|
|
23
23
|
if (!((_a = req.url) === null || _a === void 0 ? void 0 : _a.startsWith(BASE)))
|
|
24
|
-
return;
|
|
24
|
+
return res.end('HFS'); // to satisfy self-check with the temporary server
|
|
25
25
|
const token = req.url.slice(BASE.length);
|
|
26
26
|
console.debug("got http challenge", token);
|
|
27
27
|
res.statusCode = cross_1.HTTP_OK;
|
|
@@ -35,23 +35,23 @@ const acmeMiddleware = (ctx, next) => {
|
|
|
35
35
|
exports.acmeMiddleware = acmeMiddleware;
|
|
36
36
|
async function generateSSLCert(domain, email) {
|
|
37
37
|
// will answer challenge through our koa app (if on port 80) or must we spawn a dedicated server?
|
|
38
|
-
const
|
|
38
|
+
const nat = await (0, nat_1.getNatInfo)();
|
|
39
39
|
const { http } = await (0, listen_1.getServerStatus)();
|
|
40
|
-
const tempSrv = externalPort === 80 || http.listening && http.port === 80 ? undefined : (0, http_1.createServer)(acmeListener);
|
|
40
|
+
const tempSrv = nat.externalPort === 80 || http.listening && http.port === 80 ? undefined : (0, http_1.createServer)(acmeListener);
|
|
41
41
|
if (tempSrv)
|
|
42
|
-
await new Promise(
|
|
42
|
+
await new Promise(resolve => tempSrv.listen(80, resolve).on('error', (e) => {
|
|
43
43
|
console.debug("cannot listen on 80", e.code || e);
|
|
44
44
|
resolve(); // go on anyway
|
|
45
45
|
}));
|
|
46
46
|
acmeMiddlewareEnabled = true;
|
|
47
47
|
console.debug('acme challenge server ready');
|
|
48
|
+
let tempMap;
|
|
48
49
|
try {
|
|
49
50
|
const checkUrl = `http://${domain}`;
|
|
50
51
|
let check = await (0, selfCheck_1.selfCheck)(checkUrl); // some check services may not consider the domain, but we already verified that
|
|
51
|
-
if (check && !check.success && upnp &&
|
|
52
|
+
if (check && !check.success && nat.upnp && !nat.mapped80) {
|
|
52
53
|
console.debug("setting temporary port forward");
|
|
53
|
-
|
|
54
|
-
await nat_1.upnpClient.createMapping({ private: 80, public: { host: '', port: 80 }, description: 'hfs temporary', ttl: 30 }).catch(() => { });
|
|
54
|
+
tempMap = await nat_1.upnpClient.createMapping({ private: 80, public: { host: '', port: 80 }, description: 'hfs temporary', ttl: 0 }).catch(() => { });
|
|
55
55
|
check = await (0, selfCheck_1.selfCheck)(checkUrl); // repeat test
|
|
56
56
|
}
|
|
57
57
|
//if (!check) throw new ApiError(HTTP_FAILED_DEPENDENCY, "couldn't test port 80")
|
|
@@ -61,6 +61,7 @@ async function generateSSLCert(domain, email) {
|
|
|
61
61
|
accountKey: await acme_client_1.default.crypto.createPrivateKey(),
|
|
62
62
|
directoryUrl: acme_client_1.default.directory.letsencrypt.production
|
|
63
63
|
});
|
|
64
|
+
acme_client_1.default.setLogger(console.debug);
|
|
64
65
|
const [key, csr] = await acme_client_1.default.crypto.createCsr({ commonName: domain });
|
|
65
66
|
const cert = await acmeClient.auto({
|
|
66
67
|
csr,
|
|
@@ -68,17 +69,16 @@ async function generateSSLCert(domain, email) {
|
|
|
68
69
|
challengePriority: ['http-01'],
|
|
69
70
|
skipChallengeVerification: true,
|
|
70
71
|
termsOfServiceAgreed: true,
|
|
71
|
-
async challengeCreateFn(_, c, ka) {
|
|
72
|
-
|
|
73
|
-
acmeTokens[c.token] = ka;
|
|
74
|
-
},
|
|
75
|
-
async challengeRemoveFn(_, c) {
|
|
76
|
-
delete acmeTokens[c.token];
|
|
77
|
-
},
|
|
72
|
+
async challengeCreateFn(_, c, ka) { acmeTokens[c.token] = ka; },
|
|
73
|
+
async challengeRemoveFn(_, c) { delete acmeTokens[c.token]; },
|
|
78
74
|
});
|
|
79
75
|
return { key, cert };
|
|
80
76
|
}
|
|
81
77
|
finally {
|
|
78
|
+
if (tempMap) {
|
|
79
|
+
console.debug("removing temporary port forward");
|
|
80
|
+
nat_1.upnpClient.removeMapping({ public: { host: '', port: 80 } }).catch(() => { });
|
|
81
|
+
}
|
|
82
82
|
acmeMiddlewareEnabled = false;
|
|
83
83
|
if (tempSrv)
|
|
84
84
|
await new Promise(res => tempSrv.close(res));
|
package/src/adminApis.js
CHANGED
|
@@ -113,7 +113,7 @@ exports.adminApis = {
|
|
|
113
113
|
version: const_1.VERSION,
|
|
114
114
|
apiVersion: const_1.API_VERSION,
|
|
115
115
|
compatibleApiVersion: const_1.COMPATIBLE_API_VERSION,
|
|
116
|
-
...await (0, listen_1.getServerStatus)(),
|
|
116
|
+
...await (0, listen_1.getServerStatus)(false),
|
|
117
117
|
platform: process.platform,
|
|
118
118
|
urls: await (0, listen_1.getUrls)(),
|
|
119
119
|
ips: await (0, listen_1.getIps)(false),
|
package/src/block.js
CHANGED
|
@@ -10,7 +10,7 @@ const block = (0, config_1.defineConfig)('block', [], rules => {
|
|
|
10
10
|
const ret = !Array.isArray(rules) ? []
|
|
11
11
|
: (0, misc_1.onlyTruthy)(rules.map(rule => {
|
|
12
12
|
rule.expire && (rule.expire = new Date(rule.expire));
|
|
13
|
-
return !(rule.expire > now) && (0, misc_1.makeNetMatcher)(rule.ip
|
|
13
|
+
return !(rule.expire > now) && (0, misc_1.makeNetMatcher)(rule.ip);
|
|
14
14
|
}));
|
|
15
15
|
// reapply new block to existing connections
|
|
16
16
|
for (const { socket, ip } of (0, connections_1.getConnections)())
|
package/src/const.js
CHANGED
|
@@ -42,7 +42,7 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
|
|
|
42
42
|
exports.ORIGINAL_CWD = process.cwd();
|
|
43
43
|
exports.HFS_STARTED = new Date();
|
|
44
44
|
const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
|
|
45
|
-
exports.BUILD_TIMESTAMP = "2023-11-
|
|
45
|
+
exports.BUILD_TIMESTAMP = "2023-11-06T09:23:00.007Z";
|
|
46
46
|
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
47
47
|
exports.VERSION = pkg.version;
|
|
48
48
|
exports.RUNNING_BETA = exports.VERSION.includes('-');
|
package/src/cross.js
CHANGED
|
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.isWindowsDrive = exports.isIP = exports.isPrimitive = exports.formatTimestamp = 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._dbg = exports._log = exports.wantArray = exports.formatPerc = exports.with_ = exports.try_ = exports.setHidden = exports.onlyTruthy = exports.truthy = exports.enforceFinal = exports.objSameKeys = exports.haveTimeout = exports.wait = exports.prefix = exports.formatBytes = exports.isWhoObject = exports.PERM_KEYS = exports.defaultPerms = exports.WHO_ANY_ACCOUNT = exports.WHO_NO_ONE = exports.WHO_ANYONE = exports.MAX_TILES_SIZE = exports.DAY = exports.HOUR = exports.MINUTE = exports.WIKI_URL = exports.REPO_URL = void 0;
|
|
21
|
-
exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.ipLocalHost = exports.xlate = exports.isEqualLax = void 0;
|
|
21
|
+
exports.runAt = exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.ipLocalHost = exports.xlate = exports.isEqualLax = void 0;
|
|
22
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
|
|
23
23
|
// all content here is shared between client and server
|
|
24
24
|
const lodash_1 = __importDefault(require("lodash"));
|
|
@@ -312,3 +312,25 @@ async function promiseBestEffort(promises) {
|
|
|
312
312
|
return res.filter(x => x.status === 'fulfilled').map((x) => x.value);
|
|
313
313
|
}
|
|
314
314
|
exports.promiseBestEffort = promiseBestEffort;
|
|
315
|
+
function runAt(ts, cb) {
|
|
316
|
+
let cancel = false;
|
|
317
|
+
let t;
|
|
318
|
+
setTimeout(async () => {
|
|
319
|
+
if (missing() < 0)
|
|
320
|
+
return;
|
|
321
|
+
const max = 0x7FFFFFFF;
|
|
322
|
+
while (!cancel && missing() > max)
|
|
323
|
+
await wait(max);
|
|
324
|
+
if (cancel)
|
|
325
|
+
return;
|
|
326
|
+
t = setTimeout(cb, missing());
|
|
327
|
+
function missing() {
|
|
328
|
+
return ts - Date.now();
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
return () => {
|
|
332
|
+
cancel = true;
|
|
333
|
+
clearTimeout(t);
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
exports.runAt = runAt;
|
package/src/listen.js
CHANGED
|
@@ -118,9 +118,20 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
|
118
118
|
if (cn)
|
|
119
119
|
console.log("certificate loaded for", cn);
|
|
120
120
|
const now = new Date();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
const from = new Date(cert.validFrom);
|
|
122
|
+
const to = new Date(cert.validTo);
|
|
123
|
+
updateError(); // error will change at from and to dates of the certificate
|
|
124
|
+
const cancelTo = (0, misc_1.runAt)(to.getTime(), updateError);
|
|
125
|
+
const cancelFrom = (0, misc_1.runAt)(from.getTime(), updateError);
|
|
126
|
+
httpsSrv.on('close', () => {
|
|
127
|
+
cancelTo();
|
|
128
|
+
cancelFrom();
|
|
129
|
+
});
|
|
130
|
+
function updateError() {
|
|
131
|
+
if (!httpsSrv)
|
|
132
|
+
return;
|
|
133
|
+
httpsSrv.error = from > now ? "certificate not valid yet" : to < now ? "certificate expired" : undefined;
|
|
134
|
+
}
|
|
124
135
|
const namesForOutput = { cert: 'certificate', private_key: 'private key' };
|
|
125
136
|
const missing = (_c = httpsNeeds.find(x => !x.get())) === null || _c === void 0 ? void 0 : _c.key();
|
|
126
137
|
if (missing)
|
|
@@ -247,7 +258,7 @@ function getCurrentPort(srv) {
|
|
|
247
258
|
var _a;
|
|
248
259
|
return (_a = srv === null || srv === void 0 ? void 0 : srv.address()) === null || _a === void 0 ? void 0 : _a.port;
|
|
249
260
|
}
|
|
250
|
-
async function getServerStatus() {
|
|
261
|
+
async function getServerStatus(includeSrv = true) {
|
|
251
262
|
return {
|
|
252
263
|
http: await serverStatus(httpSrv, exports.portCfg.get()),
|
|
253
264
|
https: await serverStatus(httpsSrv, exports.httpsPortCfg.get()),
|
|
@@ -260,7 +271,7 @@ async function getServerStatus() {
|
|
|
260
271
|
busy,
|
|
261
272
|
port: getCurrentPort(srv) || configuredPort,
|
|
262
273
|
configuredPort,
|
|
263
|
-
srv,
|
|
274
|
+
srv: includeSrv ? srv : undefined,
|
|
264
275
|
};
|
|
265
276
|
}
|
|
266
277
|
}
|
package/src/nat.js
CHANGED
|
@@ -85,6 +85,7 @@ exports.getNatInfo = (0, debounceAsync_1.debounceAsync)(async () => {
|
|
|
85
85
|
publicIps: exports.defaultBaseUrl.publicIps = await gettingIps,
|
|
86
86
|
externalIp: exports.defaultBaseUrl.externalIp,
|
|
87
87
|
mapped,
|
|
88
|
+
mapped80: lodash_1.default.find(mappings, x => x.private.host === localIp && x.private.port === 80 && x.public.port === 80),
|
|
88
89
|
internalPort,
|
|
89
90
|
externalPort,
|
|
90
91
|
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' : '',
|
package/src/selfCheck.js
CHANGED
|
@@ -10,51 +10,41 @@ const net_1 = require("net");
|
|
|
10
10
|
const lodash_1 = __importDefault(require("lodash"));
|
|
11
11
|
const cross_1 = require("./cross");
|
|
12
12
|
const util_http_1 = require("./util-http");
|
|
13
|
-
let selfCheckMiddlewareEnabled = false;
|
|
14
13
|
const CHECK_URL = cross_const_1.SPECIAL_URI + 'self-check';
|
|
15
|
-
const selfCheckMiddleware = (ctx, next) =>
|
|
16
|
-
|
|
17
|
-
return next();
|
|
18
|
-
ctx.body = 'HFS';
|
|
19
|
-
};
|
|
14
|
+
const selfCheckMiddleware = (ctx, next) => // koa format
|
|
15
|
+
ctx.url.startsWith(CHECK_URL) ? ctx.body = 'HFS' : next();
|
|
20
16
|
exports.selfCheckMiddleware = selfCheckMiddleware;
|
|
21
17
|
async function selfCheck(url) {
|
|
22
18
|
var _a;
|
|
23
19
|
const prjInfo = await (0, github_1.getProjectInfo)();
|
|
24
20
|
console.log(`checking server ${url}`);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return { success, service, url };
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
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));
|
|
50
|
-
}
|
|
21
|
+
const parsed = new URL(url);
|
|
22
|
+
const family = !(0, net_1.isIP)(parsed.hostname) ? undefined : (0, net_1.isIPv6)(parsed.hostname) ? 6 : 4;
|
|
23
|
+
for (const services of lodash_1.default.chunk(lodash_1.default.shuffle(prjInfo.selfCheckServices), 2)) {
|
|
24
|
+
try {
|
|
25
|
+
return await Promise.any(services.map(async (svc) => {
|
|
26
|
+
if (!svc.url || svc.type)
|
|
27
|
+
throw 'unsupported ' + svc.type; // only default type supported for now
|
|
28
|
+
let { url: serviceUrl, body, regexpSuccess, regexpFailure, ...rest } = svc;
|
|
29
|
+
const service = new URL(serviceUrl).hostname;
|
|
30
|
+
console.log('trying external service', service);
|
|
31
|
+
body = applySymbols(body);
|
|
32
|
+
serviceUrl = applySymbols(serviceUrl);
|
|
33
|
+
const res = await (0, cross_1.haveTimeout)(6000, (0, util_http_1.httpString)(serviceUrl, { family, ...rest, body }));
|
|
34
|
+
const success = new RegExp(regexpSuccess).test(res);
|
|
35
|
+
const failure = new RegExp(regexpFailure).test(res);
|
|
36
|
+
if (success === failure)
|
|
37
|
+
throw 'inconsistent: ' + service + ': ' + res; // this result cannot be trusted
|
|
38
|
+
console.debug(service, 'responded', success);
|
|
39
|
+
return { success, service, url };
|
|
40
|
+
}));
|
|
51
41
|
}
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
catch (e) {
|
|
43
|
+
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));
|
|
54
44
|
}
|
|
55
45
|
}
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
function applySymbols(s) {
|
|
47
|
+
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);
|
|
58
48
|
}
|
|
59
49
|
}
|
|
60
50
|
exports.selfCheck = selfCheck;
|