UNPKG

hfs

Version:
204 lines (203 loc) 9.42 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 }); exports.title = exports.favicon = exports.adminNet = exports.localhostAdmin = exports.adminApis = void 0; exports.ctxAdminAccess = ctxAdminAccess; exports.anyAccountCanLoginAdmin = anyAccountCanLoginAdmin; exports.allowAdmin = allowAdmin; const apiMiddleware_1 = require("./apiMiddleware"); const config_1 = require("./config"); const listen_1 = require("./listen"); const const_1 = require("./const"); const api_vfs_1 = __importDefault(require("./api.vfs")); const api_accounts_1 = __importDefault(require("./api.accounts")); const api_plugins_1 = __importDefault(require("./api.plugins")); const api_monitor_1 = __importDefault(require("./api.monitor")); const api_lang_1 = __importDefault(require("./api.lang")); const api_net_1 = __importDefault(require("./api.net")); const api_log_1 = __importDefault(require("./api.log")); const api_cert_1 = __importDefault(require("./api.cert")); const connections_1 = require("./connections"); const misc_1 = require("./misc"); const perm_1 = require("./perm"); const middlewares_1 = require("./middlewares"); const child_process_1 = require("child_process"); const util_1 = require("util"); const customHtml_1 = require("./customHtml"); const lodash_1 = __importDefault(require("lodash")); const update_1 = require("./update"); const path_1 = require("path"); const errorPages_1 = require("./errorPages"); const geo_1 = require("./geo"); const roots_1 = require("./roots"); const SendList_1 = require("./SendList"); const ddns_1 = require("./ddns"); const block_1 = require("./block"); const github_1 = require("./github"); const acme_1 = require("./acme"); exports.adminApis = { ...api_vfs_1.default, ...api_accounts_1.default, ...api_plugins_1.default, ...api_monitor_1.default, ...api_lang_1.default, ...api_net_1.default, ...api_log_1.default, ...api_cert_1.default, get_dynamic_dns_error: ddns_1.get_dynamic_dns_error, async set_config({ values }) { var _a; (0, misc_1.apiAssertTypes)({ object: { values } }); (0, config_1.setConfig)(values); if (values.port === 0 || values.https_port === 0) return (_a = await (0, misc_1.waitFor)(async () => { const st = await (0, listen_1.getServerStatus)(); // wait for all random ports to be done, so we communicate new numbers if ((values.port !== 0 || st.http.listening) && (values.https_port !== 0 || st.https.listening)) return st; }, { timeout: 1000 })) !== null && _a !== void 0 ? _a : new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, "something went wrong changing ports"); return {}; }, get_config: config_1.getWholeConfig, get_config_text() { return { path: config_1.configFile.getPath(), fullPath: (0, path_1.resolve)(config_1.configFile.getPath()), text: config_1.configFile.getText(), customHtml: customHtml_1.customHtml.getText(), }; }, set_config_text: ({ text }) => config_1.configFile.save(text, { reparse: true }), update: ({ tag }) => (0, update_1.update)(tag).catch(e => { var _a, _b; throw ((_a = e.cause) === null || _a === void 0 ? void 0 : _a.statusCode) ? new apiMiddleware_1.ApiError((_b = e.cause) === null || _b === void 0 ? void 0 : _b.statusCode) : e; }), async check_update() { return { options: await (0, update_1.getUpdates)() }; }, async get_other_versions() { let left = 50; const res = await (0, update_1.getVersions)(r => !r.prerelease && !left--); return { options: res.filter(x => !x.prerelease) }; }, async wait_project_info() { await (0, github_1.getProjectInfo)(); return {}; }, async ip_country({ ips }) { const res = await Promise.allSettled(ips.map(geo_1.ip2country)); return { codes: res.map(x => x.status === 'rejected' || x.value === '-' ? '' : x.value) }; }, is_ip_blocked({ ips }) { (0, misc_1.apiAssertTypes)({ array: { ips }, string: { ips0: ips[0] } }); return { blocked: ips.map((x) => (0, block_1.isBlocked)(x) ? 1 : 0) }; }, get_custom_html() { return { enabled: !customHtml_1.disableCustomHtml.get(), sections: Object.fromEntries([ ...customHtml_1.customHtmlSections.concat((0, errorPages_1.getErrorSections)()).map(k => [k, '']), // be sure to output all sections ...customHtml_1.customHtml.sections // override entries above ]), }; }, async set_custom_html({ sections }) { await (0, customHtml_1.saveCustomHtml)(sections); return {}; }, quit() { setTimeout(() => process.exit()); return {}; }, async get_status() { return { started: const_1.HFS_STARTED, build: const_1.BUILD_TIMESTAMP, version: const_1.VERSION, apiVersion: const_1.API_VERSION, compatibleApiVersion: const_1.COMPATIBLE_API_VERSION, ...await (0, listen_1.getServerStatus)(false), platform: process.platform, urls: await (0, listen_1.getUrls)(), ips: await (0, listen_1.getIps)(false), baseUrl: await (0, listen_1.getBaseUrlOrDefault)(), roots: roots_1.roots.get(), updatePossible: !await (0, update_1.updateSupported)() ? false : (await (0, update_1.localUpdateAvailable)()) ? 'local' : true, previousVersionAvailable: await (0, update_1.previousAvailable)(), autoCheckUpdateResult: update_1.autoCheckUpdateResult.get(), // in this form, we get the same type of the serialized json alerts: github_1.alerts.get(), proxyDetected: (0, middlewares_1.getProxyDetected)(), cloudflareDetected: middlewares_1.cloudflareDetected, ram: process.memoryUsage.rss(), acmeRenewError: acme_1.acmeRenewError, blacklistedInstalledPlugins: github_1.blacklistedInstalledPlugins, frpDetected: exports.localhostAdmin.get() && !(0, middlewares_1.getProxyDetected)() && (0, connections_1.getConnections)().every(misc_1.isLocalHost) && await frpDebounced(), }; }, async add_block({ merge, ip, expire, comment }) { (0, misc_1.apiAssertTypes)({ string: { ip }, string_undefined: { comment, expire }, object_undefined: { merge }, }); const optionals = lodash_1.default.pickBy({ expire, comment }, v => v !== undefined); // passing undefined-s would override values in merge (0, block_1.addBlock)({ ip, ...optionals }, merge); return {}; }, async geo_ip({ ip }) { (0, misc_1.apiAssertTypes)({ string: { ip } }); return { country: await (0, geo_1.ip2country)(ip) }; }, validate_net_mask({ mask }) { (0, misc_1.apiAssertTypes)({ string: { mask } }); return { result: Boolean((0, misc_1.try_)(() => (0, misc_1.makeNetMatcher)(mask))) }; }, }; for (const [k, was] of (0, misc_1.typedEntries)(exports.adminApis)) exports.adminApis[k] = ((params, ctx) => { if (!allowAdmin(ctx)) return new apiMiddleware_1.ApiError(const_1.HTTP_FORBIDDEN); if (ctxAdminAccess(ctx)) return was(params, ctx); const props = { possible: anyAccountCanLoginAdmin() }; return ctx.headers.accept === 'text/event-stream' ? new SendList_1.SendListReadable({ doAtStart: x => x.error(const_1.HTTP_UNAUTHORIZED, true, props) }) : new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, props); }); exports.localhostAdmin = (0, config_1.defineConfig)('localhost_admin', true); exports.adminNet = (0, config_1.defineConfig)('admin_net', '', v => (0, misc_1.makeNetMatcher)(v, true)); exports.favicon = (0, config_1.defineConfig)('favicon', ''); exports.title = (0, config_1.defineConfig)('title', "File server"); function ctxAdminAccess(ctx) { return !ctx.ips.length // we consider localhost_admin only if no proxy is being used && exports.localhostAdmin.get() && (0, misc_1.isLocalHost)(ctx) || ctx.state.account && (0, perm_1.accountCanLoginAdmin)(ctx.state.account); } const frpDebounced = (0, misc_1.debounceAsync)(async () => { if (!const_1.IS_WINDOWS) return false; try { // guy with win11 reported missing tasklist, so don't take it for granted const { stdout } = await (0, util_1.promisify)(child_process_1.execFile)('tasklist', ['/fi', 'imagename eq frpc.exe', '/nh']); return stdout.includes('frpc'); } catch (_a) { return false; } }, { retain: 10000 }); function anyAccountCanLoginAdmin() { return Boolean(lodash_1.default.find(perm_1.accounts.get(), perm_1.accountCanLoginAdmin)); } function allowAdmin(ctx) { return (0, misc_1.isLocalHost)(ctx) || exports.adminNet.compiled()(ctx.ip); }