UNPKG

country-linkify

Version:

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

170 lines (169 loc) 7.92 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Server = void 0; const countrymanager_1 = __importDefault(require("./countrymanager")); const linkmanager_1 = __importDefault(require("./linkmanager")); const fs_1 = __importDefault(require("fs")); const anyhow_1 = __importDefault(require("anyhow")); const jaul_1 = __importDefault(require("jaul")); const path_1 = __importDefault(require("path")); const express = require("express"); let settings; class Server { constructor() { } static _instance; static get Instance() { return this._instance || (this._instance = new this()); } app; init = async (app) => { settings = require("setmeup").settings.countryLinkify; if (!settings.server.apiKey) { throw new Error(`Missing "server.apiKey" on settings`); } if (!settings.server.apiToken) { throw new Error(`Missing "server.apiToken" on settings`); } if (!app) { if (!settings.server.url) { throw new Error(`Missing "server.url" on settings`); } if (!settings.server.port) { throw new Error(`The "server.port" must be a valid port number`); } this.app = express(); } else { this.app = app; } const basePath = settings.server.basePath; this.app.get(basePath, this.indexRoute); this.app.get(`${basePath}404`, this.notFoundRoute); this.app.get(`${basePath}${settings.server.apiKey}/list`, this.apiListRoute); this.app.get(`${basePath}l/:id`, this.linkRoute); this.app.get(`${basePath}s/:search`, this.linkRoute); if (!app) { this.app.use(basePath, express.static(path_1.default.join(__dirname, "../assets"))); this.app.listen(settings.server.port, () => anyhow_1.default.info("Server", `Listeing on port ${settings.server.port}, bound to ${basePath}`)); } else { const baseImagesPath = `${basePath}${settings.server.imagesPath}`.replace("//", "/"); this.app.get(`${baseImagesPath}:filename`, this.imageRoute); anyhow_1.default.info("Server", `Bound to ${basePath}`); } }; indexRoute = async (req, res) => { anyhow_1.default.debug("Server.indexRoute", req.originalUrl); res.redirect(settings.app.homeUrl || `${settings.server.basePath}404`); }; notFoundRoute = async (req, res) => { anyhow_1.default.debug("Server.notFoundRoute", req.originalUrl); const styles = fs_1.default.readFileSync(path_1.default.join(__dirname, "../assets/styles.css"), "utf8"); const template = fs_1.default.readFileSync(path_1.default.join(__dirname, "../assets/404.html"), "utf8"); res.send(jaul_1.default.data.replaceTags(template, { styles: styles })); }; linkRoute = async (req, res) => { let ip = this.getClientIP(req); let country = await this.getClientCountry(req, ip); let countryLog = country; if (!country) { country = settings.country.default; countryLog = `default ${settings.country.default}`; } const search = req.params.search; const linkId = decodeURIComponent(req.params.id || search); const sources = req.query.sources ? req.query.sources.toString().split(",") : null; const target = linkmanager_1.default.urlFor(linkId, country, sources, search ? true : false); if (!target) { anyhow_1.default.debug("Server.linkRoute", req.params.id, "404"); res.redirect(`${settings.server.basePath}404`); return; } anyhow_1.default.debug("Server.linkRoute", req.params.id, target); if (req.query.rn != "1") { anyhow_1.default.info("Server.linkRoute", req.params.id, `IP: ${ip}`, `Country: ${countryLog}`, target.source, target.url); res.redirect(target ? target.url : `${settings.server.basePath}404`); return; } const logoPath = settings.images.path.substring(0, 1) == "/" ? settings.images.path : path_1.default.join(process.cwd(), settings.images.path); const styles = fs_1.default.readFileSync(path_1.default.join(__dirname, "../assets/styles.css"), "utf8"); const template = fs_1.default.readFileSync(path_1.default.join(__dirname, "../assets/redir.html"), "utf8"); const tags = { from: req.query.from || settings.app.title, title: settings.app.title, styles: styles, target: target.source, url: target.url, imagesPath: settings.server.imagesPath, logo: fs_1.default.existsSync(path_1.default.join(logoPath, `${target.source}.png`)) ? target.source : "nologo" }; anyhow_1.default.info("Server.linkRoute", req.params.id, `IP: ${ip}`, `Country: ${countryLog}`, "Redirection notice", target.source, target.url); res.send(jaul_1.default.data.replaceTags(template, tags)); }; imageRoute = async (req, res) => { anyhow_1.default.debug("Server.imageRoute", req.originalUrl); const imagePath = settings.images.path.substring(0, 1) == "/" ? settings.images.path : path_1.default.join(process.cwd(), settings.images.path); res.send(fs_1.default.readFileSync(path_1.default.join(imagePath, req.params.filename))); }; apiListRoute = async (req, res) => { if (!this.apiCheckCredentials(req, res)) return; res.json(linkmanager_1.default.links); }; apiCheckCredentials = (req, res) => { const denied = { message: "Access denied" }; try { const header = req.headers["authorization"]; const token = header && header.indexOf(" ") > 0 ? header.split(" ")[1] : null; if (token != settings.server.apiToken) { anyhow_1.default.warn("Server.apiCheckCredentials", req.originalUrl, "Access denied"); res.status(401).json(denied); return false; } } catch (ex) { anyhow_1.default.error("Server.apiCheckCredentials", req.originalUrl, ex); res.status(401).json(denied); return false; } return true; }; getClientIP = (req) => { const xfor = req.headers["x-forwarded-for"]; if (xfor != null && xfor != "") { const ip = xfor.toString().split(",")[0]; anyhow_1.default.debug("Server.getClientIP", req.originalUrl, `From header: ${ip}`); return ip; } if (req.socket && req.socket.remoteAddress) { const ip = req.socket.remoteAddress; anyhow_1.default.debug("Server.getClientIP", req.originalUrl, `From socket: ${ip}`); return ip; } anyhow_1.default.debug("Server.getClientIP", req.originalUrl, `From req.ip: ${req.ip}`); return req.ip; }; getClientCountry = async (req, ip) => { if (!ip) ip = this.getClientIP(req); if (!ip || ip.includes("127.0.0.1")) { return settings.country.default; } const cfHeader = req.headers["cf-ipcountry"]; if (cfHeader) { anyhow_1.default.debug("Server.getClientCountry", `IP ${ip}`, `From CF header: ${cfHeader}`); return cfHeader.toString().toLowerCase(); } const ipCountry = await countrymanager_1.default.getForIP(ip); if (ipCountry) { anyhow_1.default.debug("Server.getClientCountry", `IP ${ip}`, `From IP: ${ipCountry}`); return ipCountry; } return null; }; } exports.Server = Server; exports.default = Server.Instance;