country-linkify
Version:
Simple service to redirect links based on the client's country.
205 lines (204 loc) • 8.98 kB
JavaScript
;
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;