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