UNPKG

hfs

Version:
92 lines (91 loc) 5.22 kB
"use strict"; // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const apiMiddleware_1 = require("./apiMiddleware"); const const_1 = require("./const"); const lodash_1 = __importDefault(require("lodash")); const listen_1 = require("./listen"); const github_1 = require("./github"); const misc_1 = require("./misc"); const promises_1 = require("dns/promises"); const net_1 = require("net"); const nat_1 = require("./nat"); const acme_1 = require("./acme"); const selfCheck_1 = require("./selfCheck"); const apis = { get_nat: nat_1.getNatInfo, get_public_ips: nat_1.getPublicIps, async check_domain({ domain }) { (0, misc_1.apiAssertTypes)({ string: domain }); const resolver = new promises_1.Resolver(); const prjInfo = await (0, github_1.getProjectInfo)(); resolver.setServers(prjInfo.dnsServers); const settled = await Promise.allSettled([ resolver.resolve(domain, 'A'), resolver.resolve(domain, 'AAAA'), (0, promises_1.lookup)(domain).then(x => [x.address]), ]); if (settled[0].status === 'rejected' && settled[0].reason.code === 'ECONNREFUSED') throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE, "cannot resolve domain"); // merge all results const domainIps = lodash_1.default.uniq((0, misc_1.onlyTruthy)(settled.map(x => x.status === 'fulfilled' && x.value)).flat()); if (!domainIps.length) throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "domain not working"); const publicIps = await (0, nat_1.getPublicIps)(); // do this before stopping the server for (const v6 of [false, true]) { const domainIpsThisVersion = domainIps.filter(x => (0, net_1.isIPv6)(x) === v6); const ipsThisVersion = publicIps.filter(x => (0, net_1.isIPv6)(x) === v6); if (domainIpsThisVersion.length && ipsThisVersion.length && !lodash_1.default.intersection(domainIpsThisVersion, ipsThisVersion).length) 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`); } return {}; }, async map_port({ external, internal }) { const { upnp, externalPort, internalPort } = await (0, nat_1.getNatInfo)(); if (!upnp) return new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE, "upnp failed"); if (!internalPort) return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "no internal port"); if (externalPort) try { await nat_1.upnpClient.removeMapping({ public: { host: '', port: externalPort } }); } catch (e) { return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, "removeMapping failed: " + String(e)); } if (external) // must use the object form of 'public' to work around a bug of the library await nat_1.upnpClient.createMapping({ private: internal || internalPort, public: { host: '', port: external }, description: 'hfs', ttl: 0 }) .catch(res => { throw new apiMiddleware_1.ApiError(res.errorCode || res.statusCode, res.errorCode === 718 ? "Port not available" : res.errorDescription || "unknown error"); }); return {}; }, async self_check({ url }) { if (url) return await (0, selfCheck_1.selfCheck)(url) || new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE); const [publicIps, nat] = await Promise.all([(0, nat_1.getPublicIps)(), (0, nat_1.getNatInfo)()]); if (!publicIps.length) return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'cannot detect public ip'); if (!nat.internalPort) return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'no internal port'); const finalPort = nat.externalPort || nat.internalPort; const proto = nat.proto || ((0, listen_1.getCertObject)() ? 'https' : 'http'); const defPort = proto === 'https' ? 443 : 80; const results = (0, misc_1.onlyTruthy)(await (0, misc_1.promiseBestEffort)(publicIps.map(ip => (0, selfCheck_1.selfCheck)(`${proto}://${ip}${finalPort === defPort ? '' : ':' + finalPort}`)))); return results.length ? results : new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE); }, async make_cert({ domain, email, altNames }) { await (0, acme_1.makeCert)(domain, email, altNames).catch(e => { throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e.message || String(e)); }); return {}; }, get_cert() { return (0, listen_1.getCertObject)() || { none: true }; } }; exports.default = apis;