UNPKG

hono-geo-middleware

Version:
267 lines (257 loc) 8.66 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { GeoMiddleware: () => GeoMiddleware, getGeo: () => getGeo }); module.exports = __toCommonJS(src_exports); var import_factory = require("hono/factory"); // src/util.ts function createInsensitiveHeaderProxy(header) { return new Proxy(header, { get(target, prop) { for (const key in target) { if (key.toLowerCase() === prop.toLowerCase()) { return target[key]; } } return void 0; } }); } function tryDecodeURIText(text) { if (text) { return decodeURIComponent(text); } return void 0; } var EMOJI_FLAG_UNICODE_STARTING_POSITION = 127397; function getFlagFromCountryCode(countryCode) { const regex = new RegExp("^[A-Z]{2}$").test(countryCode); if (!countryCode || !regex) return void 0; return String.fromCodePoint( ...countryCode.split("").map((char) => EMOJI_FLAG_UNICODE_STARTING_POSITION + char.charCodeAt(0)) ); } // src/extractor/vercel.ts var CITY_HEADER_NAME = "x-vercel-ip-city"; var COUNTRY_HEADER_NAME = "x-vercel-ip-country"; var IP_HEADER_NAME = "x-real-ip"; var LATITUDE_HEADER_NAME = "x-vercel-ip-latitude"; var LONGITUDE_HEADER_NAME = "x-vercel-ip-longitude"; var REGION_HEADER_NAME = "x-vercel-ip-country-region"; var REQUEST_ID_HEADER_NAME = "x-vercel-id"; var vercel = (headers) => { if (!headers[REQUEST_ID_HEADER_NAME]) { return null; } return { reqId: headers[REQUEST_ID_HEADER_NAME], ip: headers[IP_HEADER_NAME], city: tryDecodeURIText(headers[CITY_HEADER_NAME]), // country: ?, countryCode: headers[COUNTRY_HEADER_NAME], // region: ?, regionCode: headers[REGION_HEADER_NAME], latitude: headers[LATITUDE_HEADER_NAME], longitude: headers[LONGITUDE_HEADER_NAME], // continent: ? // postalCode: ? // metroCode: ? // timezone: ? // asn: ? flag: getFlagFromCountryCode(headers[COUNTRY_HEADER_NAME]), // Vercel Edge Network region name idcRegion: getRegionFromRequestId(headers[REQUEST_ID_HEADER_NAME]) }; }; function getRegionFromRequestId(requestId) { if (!requestId) { return "dev1"; } return requestId.split(":")[0]; } // src/extractor/cloudflare-worker.ts var import_adapter = require("hono/adapter"); var REQUEST_ID_HEADER_NAME2 = "CF-ray"; var IP_HEADER_NAME2 = "CF-Connecting-IP"; var cloudflareWorker = (headers, c) => { if ((0, import_adapter.getRuntimeKey)() !== "workerd" && !headers[REQUEST_ID_HEADER_NAME2]) { return null; } const req = c.req.raw; return { reqId: headers[REQUEST_ID_HEADER_NAME2], ip: headers[IP_HEADER_NAME2], city: tryDecodeURIText(req.cf?.city), /* ISO 3166-2 code */ countryCode: req.cf?.country, continent: req.cf?.continent, region: req.cf?.region, regionCode: req.cf?.regionCode, latitude: req.cf?.latitude, longitude: req.cf?.longitude, postalCode: req.cf?.postalCode, metroCode: req.cf?.metroCode, timezone: req.cf?.timezone, asn: req.cf?.asn?.toString(), /** flag emoji */ flag: getFlagFromCountryCode(req.cf?.country) }; }; // src/extractor/cloudflare.ts var REQUEST_ID_HEADER_NAME3 = "CF-ray"; var IP_HEADER_NAME3 = "CF-Connecting-IP"; var COUNTRY_HEADER_NAME2 = "CF-IPCountry"; var CITY_HEADER_NAME2 = "CF-ipcity"; var CONTINENT_HEADER_NAME = "cf-ipcontinent"; var LATITUDE_HEADER_NAME2 = "cf-iplatitude"; var LONGITUDE_HEADER_NAME2 = "cf-iplongitude"; var REGION_HEADER_NAME2 = "cf-region"; var REGION_CODE_HEADER_NAME = "cf-region-code"; var TIMEZONE_HEADER_NAME = "cf-timezone"; var METRO_HEADER_NAME = "cf-metro-code"; var POSTAL_HEADER_NAME = "cf-postal-code"; var cloudflare = (headers) => { if (!headers[REQUEST_ID_HEADER_NAME3]) { return null; } return { reqId: headers[REQUEST_ID_HEADER_NAME3], ip: headers[IP_HEADER_NAME3], city: tryDecodeURIText(headers[CITY_HEADER_NAME2]), countryCode: headers[COUNTRY_HEADER_NAME2], region: headers[REGION_HEADER_NAME2], regionCode: headers[REGION_CODE_HEADER_NAME], latitude: headers[LATITUDE_HEADER_NAME2], longitude: headers[LONGITUDE_HEADER_NAME2], continent: headers[CONTINENT_HEADER_NAME], metroCode: headers[METRO_HEADER_NAME], postalCode: headers[POSTAL_HEADER_NAME], timezone: headers[TIMEZONE_HEADER_NAME], flag: getFlagFromCountryCode(headers[COUNTRY_HEADER_NAME2]) }; }; // src/extractor/netlify.ts var REQUEST_ID_HEADER_NAME4 = "X-Nf-request-id"; var IP_HEADER_NAME4 = "X-Nf-Client-Connection-Ip"; var netlify = (headers, c) => { if (!headers[REQUEST_ID_HEADER_NAME4]) { return null; } const ctx = c.env.context; const geo = c.env.context?.geo; return { reqId: headers[REQUEST_ID_HEADER_NAME4], ip: ctx?.ip ?? headers[IP_HEADER_NAME4], city: tryDecodeURIText(geo?.city), country: geo?.country?.name, countryCode: geo?.country?.code, region: geo?.subdivision?.name, regionCode: geo?.subdivision?.code, latitude: geo?.latitude?.toString(), longitude: geo?.longitude?.toString(), // metroCode: , postalCode: geo?.postalCode, timezone: geo?.timezone, flag: getFlagFromCountryCode(geo?.country?.code), idcRegion: ctx?.server.region }; }; // src/extractor/cloudfront.ts var CITY_HEADER_NAME3 = "CloudFront-Viewer-City"; var COUNTRY_CODE_HEADER_NAME = "CloudFront-Viewer-Country"; var COUNTRY_NAME_HEADER_NAME = "CloudFront-Viewer-Country-Name"; var REGION_CODE_HEADER_NAME2 = "CloudFront-Viewer-Country-Region"; var REGION_NAME_HEADER_NAME = "CloudFront-Viewer-Country-Region-Name"; var LATITUDE_HEADER_NAME3 = "CloudFront-Viewer-Latitude"; var LONGITUDE_HEADER_NAME3 = "CloudFront-Viewer-Longitude"; var TIMEZONE_HEADER_NAME2 = "CloudFront-Viewer-Time-Zone"; var METRO_CODE_HEADER_NAME = "CloudFront-Viewer-Metro-Code"; var POSTAL_CODE_HEADER_NAME = "CloudFront-Viewer-Postal-Code"; var ASN_HEADER_NAME = "CloudFront-Viewer-ASN"; var cloudfront = (headers, c) => { const reqId = c.env?.context?.awsRequestId; if (!reqId) { return null; } return { reqId, ip: c.env?.request?.clientIp, city: tryDecodeURIText(headers[CITY_HEADER_NAME3]), country: headers[COUNTRY_NAME_HEADER_NAME], countryCode: headers[COUNTRY_CODE_HEADER_NAME], region: headers[REGION_NAME_HEADER_NAME], regionCode: headers[REGION_CODE_HEADER_NAME2], latitude: headers[LATITUDE_HEADER_NAME3], longitude: headers[LONGITUDE_HEADER_NAME3], // continent?: string; postalCode: headers[POSTAL_CODE_HEADER_NAME], metroCode: headers[METRO_CODE_HEADER_NAME], timezone: headers[TIMEZONE_HEADER_NAME2], asn: headers[ASN_HEADER_NAME], // idcRegion? flag: getFlagFromCountryCode(headers[COUNTRY_CODE_HEADER_NAME]) }; }; // src/extractor/index.ts var extractorFuncs = [ vercel, cloudflareWorker, cloudflare, netlify, cloudfront ]; var extractorFuncsMap = { "vercel": vercel, "cloudflare": cloudflare, "cloudflare-worker": cloudflareWorker, "netlify": netlify, "cloudfront": cloudfront }; // src/index.ts var GeoMiddleware = (options) => { const extractors = options?.extractors ?? extractorFuncs; const availableExtractorFuncs = extractors.map((it) => typeof it === "string" ? extractorFuncsMap[it] : it); return (0, import_factory.createMiddleware)(async (c, next) => { const geo = extractGeo(c, availableExtractorFuncs, options?.extractorOption); c.set("geo", geo); await next(); }); }; function extractGeo(c, extractorFuncs2, options) { const headers = c.req.header(); let ans = null; const h = createInsensitiveHeaderProxy(headers); for (const func of extractorFuncs2) { ans = func(h, c, options); if (ans) break; } return ans || {}; } var getGeo = (c) => { return c.get("geo"); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { GeoMiddleware, getGeo });