UNPKG

country-linkify

Version:

Simple service to redirect links based on the client's country.

205 lines (204 loc) 8.98 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LinkManager = void 0; const utils_1 = require("./utils"); const countrymanager_1 = __importDefault(require("./countrymanager")); const lodash_1 = __importDefault(require("lodash")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const anyhow_1 = __importDefault(require("anyhow")); let settings; class LinkManager { constructor() { } static _instance; static get Instance() { return this._instance || (this._instance = new this()); } watchers = []; sources = []; links = {}; aliases = {}; init = async () => { settings = require("setmeup").settings.countryLinkify; try { await this.load(); } catch (ex) { anyhow_1.default.error("LinkManager.init", "Failed to init", ex); throw ex; } }; load = async (reset) => { try { const directory = settings.links.path.substring(0, 1) == "/" ? settings.links.path : path_1.default.join(process.cwd(), settings.links.path); if (!fs_1.default.existsSync(directory)) { anyhow_1.default.warn("LinkManager.load", `Directory ${directory} not found, no links were loaded`); return; } const files = fs_1.default.readdirSync(directory); if (reset) { const count = Object.keys(this.links).length; anyhow_1.default.info("LinkManager.load", "Reset", `Cleared ${count} links`); for (let watcher of this.watchers) { try { watcher.close(); } catch (ex) { anyhow_1.default.error("LinkManager.load.reset", ex); } } this.links = {}; this.watchers = []; } anyhow_1.default.debug("LinkManager.load", `Will load ${files.length} file(s)`); for (let filename of files) { try { const filepath = path_1.default.join(directory, filename); await this.loadFile(filepath); if (settings.links.autoReload) { const watcher = fs_1.default.watch(filepath, async (e, changedfile) => { if (changedfile && e == "change") { const basename = path_1.default.basename(changedfile, ".json"); try { changedfile = path_1.default.join(directory, changedfile); anyhow_1.default.info("LinkManager.load.watch", basename, "Reloading"); await this.loadFile(changedfile); } catch (ex) { anyhow_1.default.warn("LinkManager.load.watch", basename, "Failed to reload"); } } }); this.watchers.push(watcher); } } catch (fileEx) { anyhow_1.default.warn("LinkManager.load", filename, "Failed to load"); } } anyhow_1.default.info("LinkManager.load", `${Object.keys(this.links).length} links loaded`); const aEntries = Object.entries(settings.links.aliases); if (aEntries.length > 0) { aEntries.forEach((e) => e[1].forEach((a) => { this.aliases[a] = e[0]; if (settings.links.autoPlural) { this.aliases[`${a}s`] = e[0]; } })); anyhow_1.default.info("LinkManager.load", `${Object.keys(this.aliases).length} aliases loaded (${settings.links.autoPlural ? "with" : "no"} plurals)`); } } catch (ex) { anyhow_1.default.error("LinkManager.load", ex); } }; loadFile = async (filepath) => { try { const source = path_1.default.basename(filepath, ".json").toLowerCase(); const data = fs_1.default.readFileSync(filepath, { encoding: settings.encoding }); const entries = Object.entries(JSON.parse(data)); if (!this.sources.includes(source)) { this.sources.push(source); } for (let [id, countryUrls] of entries) { id = id.toLowerCase(); if (lodash_1.default.isString(countryUrls)) { countryUrls = { any: [countryUrls] }; } else { const targets = Object.entries(countryUrls); for (let [country, urls] of targets) { if (!Array.isArray(urls)) { countryUrls[country] = [urls]; } } } const urlReducer = (total, arr) => total + arr.length; const urlCount = Object.values(countryUrls).reduce(urlReducer, 0); const countryCodes = Object.keys(countryUrls).join(", "); anyhow_1.default.debug("LinkManager.loadFile", id, `${urlCount} links for countries ${countryCodes}`); const linkData = { id: id, source: source, urls: countryUrls }; if (id == "default" || id == source) { this.links[source] = linkData; } else { this.links[`${source}-${id}`] = linkData; } } } catch (ex) { anyhow_1.default.error("LinkManager.loadFile", filepath, ex); throw ex; } }; urlFor = (id, country, sources, search) => { const sourcesLog = sources?.length > 0 ? sources.join(", ") : "any source"; if (!sources || sources.length == 0) { sources = this.sources; } try { if (!id) { throw new Error("Missing link ID"); } const foundLinks = []; id = id.toLowerCase(); const alias = this.aliases[id]; const directLink = this.links[id] || this.links[alias]; if (directLink) { foundLinks.push(directLink); } else if (sources?.length > 0) { for (let source of sources) { let sourceLink = this.links[`${source}-${id}`] || this.links[`${source}-${alias}`]; if (!sourceLink && settings.links.autoPlural) { sourceLink = this.links[`${source}-${id}s`] || this.links[`${source}-${alias}s`]; } if (sourceLink) { foundLinks.push(sourceLink); } } } if (foundLinks.length == 0 && search) { for (let source of sources) { const sourceLink = this.links[`${source}-search`]; if (sourceLink) { foundLinks.push(sourceLink); } } } if (foundLinks.length == 0) { anyhow_1.default.warn("LinkManager.urlFor", id, "Link not found"); return null; } const countryAlias = countrymanager_1.default.aliases[country]; const countryLinks = foundLinks.filter((link) => link.urls[country] || link.urls[countryAlias] || link.urls.any); if (countryLinks.length == 0) { anyhow_1.default.warn("LinkManager.urlFor", id, `${search ? "Search" : "Link"} not found for country ${country}`); return null; } const singleLink = lodash_1.default.sample(countryLinks); const result = { url: lodash_1.default.sample(singleLink.urls[country] || singleLink.urls[countryAlias] || singleLink.urls.any), source: singleLink.source }; if (search) { result.url = result.url.replace("{{query}}", (0, utils_1.getSearchQuery)(id, result.source)); } anyhow_1.default.info("LinkManager.urlFor", id, country, sourcesLog, result.url); return result; } catch (ex) { anyhow_1.default.error("LinkManager.urlFor", id, country, sourcesLog, ex); return null; } }; } exports.LinkManager = LinkManager; exports.default = LinkManager.Instance;