├── .npmignore ├── .gitignore ├── src ├── index.ts ├── common │ ├── enums │ │ └── enums.ts │ └── interfaces │ │ ├── uaParser.interface.ts │ │ ├── options.interface.ts │ │ ├── cpu.interface.ts │ │ ├── engine.interface.ts │ │ ├── Device.interface.ts │ │ ├── iplocate.interface.ts │ │ ├── os.interface.ts │ │ └── browser.interface.ts ├── plugins │ ├── ua-parser-js.plugin.ts │ └── iplocate.plugins.ts ├── utils │ └── axios.ts └── request_detail.ts ├── jest.config.ts ├── test ├── data │ └── fakeHeader.ts ├── RequestDetail.e2e-spec.ts └── RequestDetail.spec.ts ├── tsconfig.json ├── CHANGELOG.md ├── jsdoc.json ├── package.json └── README.MD /.npmignore: -------------------------------------------------------------------------------- 1 | testPackage -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea 4 | coverage 5 | docs 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import RequestDetail from "./request_detail"; 2 | export default RequestDetail; 3 | -------------------------------------------------------------------------------- /src/common/enums/enums.ts: -------------------------------------------------------------------------------- 1 | export enum urls { 2 | iplocate = "https://www.iplocate.io/api/lookup/", 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/ua-parser-js.plugin.ts: -------------------------------------------------------------------------------- 1 | import parser from "ua-parser-js"; 2 | const ua = parser; 3 | 4 | export default ua; 5 | -------------------------------------------------------------------------------- /src/common/interfaces/uaParser.interface.ts: -------------------------------------------------------------------------------- 1 | import { IResult } from "ua-parser-js"; 2 | 3 | export interface UaParser extends IResult {} 4 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | testMatch: ["**/*.spec.ts", "**/*.e2e-spec.ts"], 5 | collectCoverage: true, 6 | coverageDirectory: "coverage", 7 | }; 8 | -------------------------------------------------------------------------------- /test/data/fakeHeader.ts: -------------------------------------------------------------------------------- 1 | export const fakeHeader = { 2 | ip: "127.0.0.1", 3 | headers: { 4 | "user-agent": 5 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/common/interfaces/options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Options { 2 | iPlocateToken?: string; 3 | } 4 | 5 | /** 6 | * Options used in the constructor of RequestDetails class. 7 | * 8 | * @interface Options 9 | * @property {string} iPlocateToken - An optional token to be used with the iPlocate API for IP geolocation. [see](https://www.iplocate.io) 10 | */ 11 | -------------------------------------------------------------------------------- /src/common/interfaces/cpu.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CPU { 2 | /** 3 | * Possible architecture: 4 | * 68k, amd64, arm, arm64, avr, ia32, ia64, irix, irix64, mips, mips64, pa-risc, 5 | * ppc, sparc, sparc64 6 | */ 7 | architecture: string | undefined; 8 | } 9 | /** 10 | 11 | Interface representing a CPU with properties for architecture. 12 | @interface CPU 13 | @property {string | undefined} architecture - The architecture of the CPU. Possible values: 68k, amd64, arm, arm64, avr, ia32, ia64, irix, irix64, mips, mips64, pa-risc, ppc, sparc, sparc64. 14 | */ 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "strictNullChecks": true, 10 | "noImplicitAny": true, 11 | "declaration": true, 12 | "sourceMap": true, 13 | "outDir": "dist", 14 | "typeRoots": [ 15 | "node_modules/@types", 16 | ], 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "node_modules", 23 | "**/*.spec.ts", 24 | "**/*.test.ts" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) and this 6 | project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | 9 | ================== 10 | ###### Changed 11 | ###### Added 12 | ###### Fixed 13 | 14 | 1.0.4 / 2022-04-24 15 | ------------------ 16 | ### Changed 17 | * [uaParser.interface.ts](src/common/interfaces/uaParser.interface.ts) 18 | 19 | 1.0.3 / 2022-04-20 20 | ================== 21 | 22 | * Initial release 23 | 24 | 25 | [repo]: https://github.com/sajjadmrx/request-details 26 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "opts": { 3 | "destination": "./docs/", 4 | "recurse": true, 5 | "encoding": "utf8", 6 | "verbose": true, 7 | "template": "./node_modules/clean-jsdoc-theme", 8 | "theme_opts": { 9 | "default_theme": "dark" 10 | } 11 | }, 12 | "source": { 13 | "include": [ 14 | ".", 15 | "./README.md" 16 | ], 17 | "exclude": ["./node_modules","docs","out","coverage"] 18 | }, 19 | "plugins": [ 20 | "plugins/markdown" 21 | ], 22 | "markdown": { 23 | "hardwrap": false, 24 | "idInHeadings": true 25 | }, 26 | "templates": { 27 | "default": { 28 | "outputSourceFiles": false 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/plugins/iplocate.plugins.ts: -------------------------------------------------------------------------------- 1 | import { urls } from "../common/enums/enums"; 2 | import I_iplocate from "../common/interfaces/iplocate.interface"; 3 | import Axios from "../utils/axios"; 4 | 5 | class Iplocate { 6 | private header: any = { 7 | "Content-Type": "application/json", 8 | Accept: "application/json", 9 | }; 10 | private ipLocateRequest: Axios; 11 | constructor(private token?: string) { 12 | if (this.token) { 13 | this.header["X-API-Key"] = this.token; 14 | } 15 | this.ipLocateRequest = new Axios(this.header); 16 | } 17 | 18 | async getIpInfo(ip: string): Promise { 19 | const response = await this.ipLocateRequest.get(urls.iplocate + ip); 20 | return response.data as I_iplocate; 21 | } 22 | } 23 | 24 | export default Iplocate; 25 | -------------------------------------------------------------------------------- /src/common/interfaces/engine.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Engine { 2 | /** 3 | * Possible name: 4 | * Amaya, EdgeHTML, Gecko, iCab, KHTML, Links, Lynx, NetFront, NetSurf, Presto, 5 | * Tasman, Trident, w3m, WebKit 6 | */ 7 | name: string | undefined; 8 | /** 9 | * Determined dynamically 10 | */ 11 | version: string | undefined; 12 | } 13 | 14 | /** 15 | 16 | Interface representing a web rendering engine with properties for name and version. 17 | @interface Engine 18 | @property {string | undefined} name - The name of the rendering engine. Possible values: Amaya, EdgeHTML, Gecko, iCab, KHTML, Links, Lynx, NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit. 19 | @property {string | undefined} version - The version of the rendering engine, determined dynamically. 20 | */ 21 | -------------------------------------------------------------------------------- /src/utils/axios.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance, AxiosRequestConfig } from "axios"; 2 | 3 | class Axios { 4 | instance: AxiosInstance; 5 | constructor(options?: AxiosRequestConfig) { 6 | const _options = options || { 7 | headers: { 8 | "Content-Type": "application/json", 9 | Accept: "application/json", 10 | }, 11 | }; 12 | this.instance = axios.create(_options); 13 | } 14 | 15 | get(url: string) { 16 | return this.instance.get(url); 17 | } 18 | 19 | post(url: string, data: any) { 20 | return this.instance.post(url, data); 21 | } 22 | 23 | put(url: string, data: any) { 24 | return this.instance.put(url, data); 25 | } 26 | 27 | delete(url: string) { 28 | return this.instance.delete(url); 29 | } 30 | } 31 | 32 | export default Axios; 33 | -------------------------------------------------------------------------------- /src/common/interfaces/Device.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Device { 2 | /** 3 | * Determined dynamically 4 | */ 5 | model: string | undefined; 6 | 7 | /** 8 | * Possible type: 9 | * console, mobile, tablet, smarttv, wearable, embedded 10 | */ 11 | type: string | undefined; 12 | 13 | /** 14 | * Possible vendor: 15 | * Acer, Alcatel, Amazon, Apple, Archos, Asus, BenQ, BlackBerry, Dell, GeeksPhone, 16 | * Google, HP, HTC, Huawei, Jolla, Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, 17 | * Nintendo, Nokia, Nvidia, Ouya, Palm, Panasonic, Polytron, RIM, Samsung, Sharp, 18 | * Siemens, Sony-Ericsson, Sprint, Xbox, ZTE 19 | */ 20 | vendor: string | undefined; 21 | } 22 | 23 | /** 24 | 25 | Interface representing a device with properties for model, type, and vendor. 26 | @interface Device 27 | @property {string | undefined} model - The model of the device, determined dynamically. 28 | @property {string | undefined} type - The type of the device. Possible values: console, mobile, tablet, smarttv, wearable, embedded. 29 | @property {string | undefined} vendor - The vendor of the device. Possible values: Acer, Alcatel, Amazon, Apple, Archos, Asus, BenQ, BlackBerry, Dell, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, Ouya, Palm, Panasonic, Polytron, RIM, Samsung, Sharp, Siemens, Sony-Ericsson, Sprint, Xbox, ZTE. 30 | */ 31 | -------------------------------------------------------------------------------- /src/common/interfaces/iplocate.interface.ts: -------------------------------------------------------------------------------- 1 | interface I_iplocate { 2 | ip: string; 3 | country: string; 4 | country_code: string; 5 | city: string | null; 6 | continent: string; 7 | latitude: number; 8 | longitude: number; 9 | time_zone: string; 10 | postal_code: string | null; 11 | org: string; 12 | asn: string; 13 | subdivision: string | null; 14 | subdivision_code: string | null; 15 | } 16 | 17 | export default I_iplocate; 18 | 19 | /** 20 | * Represents the data structure for IP location information. 21 | * 22 | * @interface 23 | * @name I_iplocate 24 | * @property {string} ip - The IP address. 25 | * @property {string} country - The country name. 26 | * @property {string} country_code - The country code. 27 | * @property {string | null} city - The city name, or `null` if not available. 28 | * @property {string} continent - The continent name. 29 | * @property {number} latitude - The latitude coordinate. 30 | * @property {number} longitude - The longitude coordinate. 31 | * @property {string} time_zone - The time zone. 32 | * @property {string | null} postal_code - The postal code, or `null` if not available. 33 | * @property {string} org - The organization name. 34 | * @property {string} asn - The Autonomous System Number (ASN). 35 | * @property {string | null} subdivision - The subdivision name, or `null` if not available. 36 | * @property {string | null} subdivision_code - The subdivision code, or `null` if not available. 37 | */ 38 | -------------------------------------------------------------------------------- /src/common/interfaces/os.interface.ts: -------------------------------------------------------------------------------- 1 | export interface OS { 2 | /** 3 | * Possible 'os.name' 4 | * AIX, Amiga OS, Android, Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS, Contiki, 5 | * Fedora, Firefox OS, FreeBSD, Debian, DragonFly, Gentoo, GNU, Haiku, Hurd, iOS, 6 | * Joli, Linpus, Linux, Mac OS, Mageia, Mandriva, MeeGo, Minix, Mint, Morph OS, NetBSD, 7 | * Nintendo, OpenBSD, OpenVMS, OS/2, Palm, PCLinuxOS, Plan9, Playstation, QNX, RedHat, 8 | * RIM Tablet OS, RISC OS, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, 9 | * Ubuntu, UNIX, VectorLinux, WebOS, Windows [Phone/Mobile], Zenwalk 10 | */ 11 | name: string | undefined; 12 | /** 13 | * Determined dynamically 14 | */ 15 | version: string | undefined; 16 | } 17 | 18 | /** 19 | 20 | Interface representing an operating system (OS) with properties for name and version. 21 | @interface OS 22 | @property {string | undefined} name - The name of the operating system. Possible values: AIX, Amiga OS, Android, Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS, Contiki, Fedora, Firefox OS, FreeBSD, Debian, DragonFly, Gentoo, GNU, Haiku, Hurd, iOS, Joli, Linpus, Linux, Mac OS, Mageia, Mandriva, MeeGo, Minix, Mint, Morph OS, NetBSD, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, PCLinuxOS, Plan9, Playstation, QNX, RedHat, RIM Tablet OS, RISC OS, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, Ubuntu, UNIX, VectorLinux, WebOS, Windows [Phone/Mobile], Zenwalk. 23 | @property {string | undefined} version - The version of the operating system, determined dynamically. 24 | */ 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "request-details", 3 | "version": "1.0.6", 4 | "description": "show request details", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "files": [ 8 | "/dist", 9 | "/types" 10 | ], 11 | "author": "sajjadmrx", 12 | "license": "ISC", 13 | "keywords": [ 14 | "request", 15 | "details", 16 | "http", 17 | "ip", 18 | "request-details", 19 | "express", 20 | "os", 21 | "browser", 22 | "node", 23 | "ts-ip" 24 | ], 25 | "homepage": "https://sajjadmrx.github.io/request-details/", 26 | "bugs": { 27 | "url": "https://github.com/sajjadmrx/request-details/issues" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/sajjadmrx/request-details.git" 32 | }, 33 | "devDependencies": { 34 | "@types/jest": "^29.5.0", 35 | "@types/node": "^17.0.23", 36 | "@types/supertest": "^2.0.12", 37 | "@types/ua-parser-js": "^0.7.36", 38 | "@typescript-eslint/eslint-plugin": "^5.58.0", 39 | "@typescript-eslint/parser": "^5.58.0", 40 | "clean-jsdoc-theme": "^4.2.7", 41 | "eslint": "^8.38.0", 42 | "eslint-config-prettier": "^8.8.0", 43 | "eslint-plugin-prettier": "^4.2.1", 44 | "jsdoc": "^4.0.2", 45 | "jsdoc-plugin-typescript": "^2.2.1", 46 | "prettier": "^2.8.7", 47 | "supertest": "^6.3.3", 48 | "ts-jest": "^29.1.0", 49 | "ts-node": "^10.9.1" 50 | }, 51 | "dependencies": { 52 | "@types/express": "^4.17.13", 53 | "axios": "^0.26.1", 54 | "express": "^4.17.3", 55 | "iplocate": "^1.0.7", 56 | "node-iplocate": "^1.0.3", 57 | "ua-parser-js": "^1.0.2" 58 | }, 59 | "scripts": { 60 | "test": "jest", 61 | "build": "tsc && npm run doc", 62 | "doc": "jsdoc -c jsdoc.json" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/RequestDetail.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import request from "supertest"; 2 | import express from "express"; 3 | import RequestDetail from "../src/request_detail"; 4 | import Request_detail from "../src/request_detail"; 5 | import { fakeHeader } from "./data/fakeHeader"; 6 | import ua from "../src/plugins/ua-parser-js.plugin"; 7 | import * as UAParser from "ua-parser-js"; 8 | 9 | // Create an Express app 10 | const app = express(); 11 | 12 | // Middleware to add RequestDetail to the request object 13 | app.use(RequestDetail.middleware); 14 | 15 | // Route for testing 16 | app.get("/", async (req, res) => { 17 | const ipInfo = await req.info.getIpInfo(); 18 | res.json({ ipInfo }); 19 | }); 20 | app.get("/without-mid", (req, res) => { 21 | // @ts-ignore 22 | req = { 23 | ...req, 24 | ...fakeHeader, 25 | }; 26 | const requestDetail: Request_detail = new RequestDetail(req); 27 | res.json({ os: requestDetail.getOs() }); 28 | }); 29 | describe("RequestDetail E2E Test", () => { 30 | test("GET / should return ipInfo", async () => { 31 | // Make a request to the Express app 32 | const res = await request(app).get("/"); 33 | 34 | // Assert status code 35 | expect(res.status).toEqual(200); 36 | 37 | // Assert response body 38 | expect(res.body.ipInfo).toBeDefined(); 39 | expect(res.body.ipInfo).toHaveProperty("ip"); 40 | expect(res.body.ipInfo).toHaveProperty("country"); 41 | expect(res.body.ipInfo).toHaveProperty("city"); 42 | }); 43 | 44 | test("should return os info", async () => { 45 | const res = await request(app).get("/without-mid"); 46 | expect(res.status).toEqual(200); 47 | expect(res.body.os).toEqual({ 48 | name: "Windows", 49 | version: "10", 50 | }); 51 | }); 52 | describe("fetchUserAgent", () => { 53 | it("should return the ua function", () => { 54 | const result = RequestDetail.fetchUserAgent; 55 | expect(result).toEqual(ua); 56 | }); 57 | it("should correctly parse OS name from user agent string", () => { 58 | const userAgent = 59 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"; 60 | jest.mock("ua-parser-js", () => { 61 | return jest.fn(() => ({ 62 | getUA: jest.fn(() => userAgent), 63 | })); 64 | }); 65 | 66 | const result = RequestDetail.fetchUserAgent; 67 | expect(result(userAgent).os.name).toBe("Windows"); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/RequestDetail.spec.ts: -------------------------------------------------------------------------------- 1 | import RequestDetail from "../src"; 2 | import Iplocate from "../src/plugins/iplocate.plugins"; 3 | import I_iplocate from "../src/common/interfaces/iplocate.interface"; 4 | 5 | jest.mock("../src/plugins/iplocate.plugins"); 6 | jest.mock("../src/plugins/ua-parser-js.plugin"); 7 | 8 | describe("RequestDetail", () => { 9 | let req: any; 10 | let res: any; 11 | let next: any; 12 | 13 | beforeEach(() => { 14 | req = { 15 | ip: "127.0.0.1", 16 | headers: { 17 | "user-agent": 18 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", 19 | }, 20 | }; 21 | res = {}; 22 | next = jest.fn(); 23 | }); 24 | 25 | afterEach(() => { 26 | jest.resetAllMocks(); 27 | }); 28 | 29 | describe("getIpInfo", () => { 30 | it("should call Iplocate.getIpInfo with the correct IP and return the result", async () => { 31 | const expectedIpInfo: I_iplocate = { 32 | country: "US", 33 | city: "New York", 34 | asn: "", 35 | ip: "", 36 | country_code: "", 37 | continent: "", 38 | org: "", 39 | latitude: 1, 40 | time_zone: "", 41 | longitude: 1, 42 | postal_code: "", 43 | subdivision: null, 44 | subdivision_code: null, 45 | }; 46 | jest 47 | .spyOn(Iplocate.prototype, "getIpInfo") 48 | .mockImplementation(async () => expectedIpInfo); 49 | 50 | const requestDetail = new RequestDetail(req, { 51 | iPlocateToken: "TEST", 52 | }); 53 | const ipInfo = await requestDetail.getIpInfo(); 54 | expect(Iplocate.prototype.getIpInfo).toHaveBeenCalledWith("127.0.0.1"); 55 | expect(ipInfo).toEqual(expectedIpInfo); 56 | }); 57 | }); 58 | 59 | describe("constructor", () => { 60 | it("should set the correct initial values", () => { 61 | const requestDetail = new RequestDetail(req); 62 | expect(requestDetail["ip"]).toEqual("127.0.0.1"); 63 | expect(requestDetail["req"]).toEqual(req); 64 | }); 65 | }); 66 | 67 | describe("getOs", () => { 68 | it("should call ua with the correct user agent and return the null", () => { 69 | const requestDetail = new RequestDetail(req); 70 | const os = requestDetail.getOs(); 71 | expect(os).toEqual(null); 72 | }); 73 | }); 74 | 75 | describe("middleware", () => { 76 | it("should create a new RequestDetail instance and set it on the request object", () => { 77 | const requestDetail = new RequestDetail(req); 78 | RequestDetail.middleware(req, res, next); 79 | expect(req.info).toEqual(requestDetail); 80 | expect(next).toHaveBeenCalled(); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /src/common/interfaces/browser.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Browser { 2 | /** 3 | * Possible values : 4 | * Amaya, Android Browser, Arora, Avant, Baidu, Blazer, Bolt, Camino, Chimera, Chrome, 5 | * Chromium, Comodo Dragon, Conkeror, Dillo, Dolphin, Doris, Edge, Epiphany, Fennec, 6 | * Firebird, Firefox, Flock, GoBrowser, iCab, ICE Browser, IceApe, IceCat, IceDragon, 7 | * Iceweasel, IE [Mobile], Iron, Jasmine, K-Meleon, Konqueror, Kindle, Links, 8 | * Lunascape, Lynx, Maemo, Maxthon, Midori, Minimo, MIUI Browser, [Mobile] Safari, 9 | * Mosaic, Mozilla, Netfront, Netscape, NetSurf, Nokia, OmniWeb, Opera [Mini/Mobi/Tablet], 10 | * Phoenix, Polaris, QQBrowser, RockMelt, Silk, Skyfire, SeaMonkey, SlimBrowser, Swiftfox, 11 | * Tizen, UCBrowser, Vivaldi, w3m, Yandex 12 | * 13 | */ 14 | name: string | undefined; 15 | 16 | /** 17 | * Determined dynamically 18 | */ 19 | version: string | undefined; 20 | 21 | /** 22 | * Determined dynamically 23 | * @deprecated 24 | */ 25 | major: string | undefined; 26 | } 27 | 28 | /** 29 | * Represents the possible browser names. 30 | * 31 | * @typedef {string} BrowserName 32 | * @memberof Browser 33 | * @enum {string} 34 | * @readonly 35 | */ 36 | type BrowserName = 37 | | "Amaya" 38 | | "Android Browser" 39 | | "Arora" 40 | | "Avant" 41 | | "Baidu" 42 | | "Blazer" 43 | | "Bolt" 44 | | "Camino" 45 | | "Chimera" 46 | | "Chrome" 47 | | "Chromium" 48 | | "Comodo Dragon" 49 | | "Conkeror" 50 | | "Dillo" 51 | | "Dolphin" 52 | | "Doris" 53 | | "Edge" 54 | | "Epiphany" 55 | | "Fennec" 56 | | "Firebird" 57 | | "Firefox" 58 | | "Flock" 59 | | "GoBrowser" 60 | | "iCab" 61 | | "ICE Browser" 62 | | "IceApe" 63 | | "IceCat" 64 | | "IceDragon" 65 | | "Iceweasel" 66 | | "IE [Mobile]" 67 | | "Iron" 68 | | "Jasmine" 69 | | "K-Meleon" 70 | | "Konqueror" 71 | | "Kindle" 72 | | "Links" 73 | | "Lunascape" 74 | | "Lynx" 75 | | "Maemo" 76 | | "Maxthon" 77 | | "Midori" 78 | | "Minimo" 79 | | "MIUI Browser" 80 | | "[Mobile] Safari" 81 | | "Mosaic" 82 | | "Mozilla" 83 | | "Netfront" 84 | | "Netscape" 85 | | "NetSurf" 86 | | "Nokia" 87 | | "OmniWeb" 88 | | "Opera [Mini/Mobi/Tablet]" 89 | | "Phoenix" 90 | | "Polaris" 91 | | "QQBrowser" 92 | | "RockMelt" 93 | | "Silk" 94 | | "Skyfire" 95 | | "SeaMonkey" 96 | | "SlimBrowser" 97 | | "Swiftfox" 98 | | "Tizen" 99 | | "UCBrowser" 100 | | "Vivaldi" 101 | | "w3m" 102 | | "Yandex"; 103 | 104 | /** 105 | * Interface representing a web browser with properties for name, version, and major version. 106 | * 107 | * @interface Browser 108 | * 109 | * @property {string | undefined} name - The name of the browser. Possible values: Amaya, Android Browser, Arora, Avant, Baidu, Blazer, Bolt, Camino, Chimera, Chrome, Chromium, Comodo Dragon, Conkeror, Dillo, Dolphin, Doris, Edge, Epiphany, Fennec, Firebird, Firefox, Flock, GoBrowser, iCab, ICE Browser, IceApe, IceCat, IceDragon, Iceweasel, IE [Mobile], Iron, Jasmine, K-Meleon, Konqueror, Kindle, Links, Lunascape, Lynx, Maemo, Maxthon, Midori, Minimo, MIUI Browser, [Mobile] Safari, Mosaic, Mozilla, Netfront, Netscape, NetSurf, Nokia, OmniWeb, Opera [Mini/Mobi/Tablet], Phoenix, Polaris, QQBrowser, RockMelt, Silk, Skyfire, SeaMonkey, SlimBrowser, Swiftfox, Tizen, UCBrowser, Vivaldi, w3m, Yandex. 110 | * 111 | * @property {string | undefined} version - The version of the browser, determined dynamically. 112 | * 113 | * @property {string | undefined} major - The major version of the browser, determined dynamically. (Deprecated) 114 | */ 115 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | # request-details 3 | a Node.js package that provides a class for handling request details in an Express application. It extracts information related to the user's IP, operating system (OS), browser, device, and CPU from the user agent string in the HTTP request headers. 4 | 5 |

6 | languages 7 | 8 |

9 | 10 | 11 | ## ⌨️ Installation 12 | 13 | ```bash 14 | npm install request-details 15 | ``` 16 | 17 | ## ⚙️ Usage 18 | To use Request-Details in your Express application, you can import the RequestDetail class from the package and create an instance of it with the Express request object. Here's an example: 19 | 20 | 21 | ```js 22 | // CommonJS: 23 | const RequestDetails = require('request-details').default; 24 | 25 | import RequestDetails from 'request-details'; 26 | 27 | import express from "express"; 28 | 29 | const app = express(); 30 | 31 | app.get("/", (req, res) => { 32 | const requestDetail = new RequestDetail(req); 33 | // Get IP info 34 | requestDetail.getIpInfo() 35 | .then(ipInfo => { 36 | console.log(ipInfo); 37 | // { ip: '127.0.0.1', country: 'US', city: 'New York', ... } 38 | }) 39 | .catch(err => { 40 | console.error(err); 41 | }); 42 | 43 | // Get OS info 44 | const os = requestDetail.getOs(); 45 | console.log(os); // { name: 'Windows', version: '10' } 46 | 47 | // Get browser info 48 | const browser = requestDetail.getBrowser(); 49 | console.log(browser); // { name: 'Chrome', version: '58.0.3029.110' } 50 | 51 | // Get device info 52 | const device = requestDetail.getDevice(); 53 | console.log(device); // { type: 'desktop', vendor: 'Unknown', model: 'Unknown' } 54 | 55 | // Get CPU info 56 | const cpu = requestDetail.getCPU(); 57 | console.log(cpu); // { architecture: 'amd64' } 58 | 59 | res.send("Hello world!"); 60 | }); 61 | 62 | app.use(RequestDetails.middleware); // It will add the details to the request object (optional) 63 | app.get('/middleware', async (req, res, next) => { 64 | const ipDetails = await req.info.getIpInfo() 65 | res.json(ipDetails) 66 | }) 67 | 68 | app.listen(3000, () => { 69 | console.log("Server is running on port 3000"); 70 | }); 71 | 72 | ``` 73 | 74 | ## 💡 Features 75 | 76 | * Retrieve IP address, operating system, and browser information from incoming HTTP requests. 77 | * Optional middleware to automatically add request details to the request object. 78 | * Simple and easy-to-use API with intuitive methods for retrieving request details. 79 | * Works with Express, NestJs, and other Node.js web frameworks. 80 | * Supports CommonJS and ES modules for importing in your project. 81 | 82 | ## 🚀 API 83 | 84 | ### `RequestDetail` 85 | * `constructor(req: Request, options?: Options)`: Creates an instance of the RequestDetail class with the Express request object and optional options to configure the instance. 86 | * `getIpInfo(): Promise`: Get information related to user's IP. Returns a Promise that resolves with the IP information. 87 | * `setOptions(options: Options): void`: Sets the options for the RequestDetail instance. 88 | * `getOs(): OS | null`: Get the operating system (OS) information from the user agent. 89 | * `getBrowser(): Browser | null`: Get the browser information from the user agent. 90 | * `getDevice(): Device | null`: Get the device information from the user agent. 91 | * `getCPU(): CPU | null`: Get the CPU information from the user agent. 92 | * `static fetchUserAgent(userAgent: string)`: Get user agent from a user agent string. 93 | * `static getIpInfoByIp(ip: string, token?: string): Promise`: Get IP information for a specific IP address. 94 | 95 | 96 | ## 🤝 Contributing 97 | If you would like to contribute to Request-Details, please open an issue or submit a pull request on the GitHub repository. 🔧💻🔍 98 | -------------------------------------------------------------------------------- /src/request_detail.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from "express"; 2 | 3 | import I_iplocate from "./common/interfaces/iplocate.interface"; 4 | import Iplocate from "./plugins/iplocate.plugins"; 5 | import "ua-parser-js"; 6 | import ua from "./plugins/ua-parser-js.plugin"; 7 | import { Browser } from "./common/interfaces/browser.interface"; 8 | import { Device } from "./common/interfaces/Device.interface"; 9 | import { CPU } from "./common/interfaces/cpu.interface"; 10 | import { OS } from "./common/interfaces/os.interface"; 11 | import { Options } from "./common/interfaces/options.interface"; 12 | 13 | /** 14 | * Declare global namespace for Express module. 15 | * 16 | * @namespace Express 17 | * 18 | * @typedef {object} Request - Represents the incoming HTTP request object in Express. 19 | * 20 | * @property {RequestDetail} info - Custom property added to the Express Request object for storing request details. 21 | * 22 | * @memberof Express 23 | * @alias Express.Request 24 | */ 25 | declare global { 26 | namespace Express { 27 | interface Request { 28 | info: RequestDetail; 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * Represents a class for handling request details in an Express application. 35 | * @class 36 | */ 37 | class RequestDetail { 38 | protected ip: string; 39 | private options: Options; 40 | 41 | /** 42 | * Creates an instance of the `RequestDetails` class. 43 | * 44 | * @constructor 45 | * @param {Request} req - The Express request object. 46 | * @param {Options} options - The options to configure the `RequestDetails` instance. 47 | */ 48 | constructor(private req: Request, options: Options = {}) { 49 | this.ip = req.ip; 50 | this.options = options; 51 | } 52 | 53 | /** 54 | * Get information related to user's IP 55 | * @returns {Promise} - A Promise that returns the information related to user's IP upon success 56 | * 57 | * @example 58 | * 59 | * const ipInfo = await requestDetail.getIpInfo(); 60 | * console.log(ipInfo); // { ip: '127.0.0.1', country: 'US', city: 'New York', ... } 61 | * 62 | */ 63 | getIpInfo(): Promise { 64 | const ip = this.req.ip; 65 | return new Iplocate(this.options.iPlocateToken).getIpInfo(ip); 66 | } 67 | 68 | /** 69 | * Sets the options for the `RequestDetails` instance. 70 | * 71 | * @method 72 | * @param {Options} options - The options to set. 73 | * @returns {void} 74 | */ 75 | setOptions(options: Options): void { 76 | this.options = options; 77 | } 78 | 79 | /** 80 | * Get the operating system (OS) information from the user agent. 81 | * @returns {OS | null} - An object representing the OS information, or null if not available. 82 | * @example 83 | * const os = requestDetail.getOs(); 84 | * console.log(os); // { name: 'Windows', version: '10' } 85 | */ 86 | getOs(): OS | null { 87 | return ua(this.req.headers["user-agent"])?.os || null; 88 | } 89 | 90 | /** 91 | * Get the browser information from the user agent 92 | * @returns {Browser | null} - An object representing the browser information, or null if not available 93 | * 94 | * @example 95 | * 96 | * const browser = requestDetail.getBrowser(); 97 | * console.log(browser); // { name: 'Chrome', version: '58.0.3029.110' } 98 | * 99 | */ 100 | getBrowser(): Browser | null { 101 | // @ts-ignore 102 | return ua(this.req.headers["user-agent"])?.browser || null; 103 | } 104 | 105 | /** 106 | * Get the device information from the user agent 107 | * @returns {Device | null} - An object representing the device information, or null if not available 108 | * 109 | * @example 110 | * 111 | * const device = requestDetail.getDevice(); 112 | * console.log(device); // { type: 'desktop', vendor: 'Unknown', model: 'Unknown' } 113 | * 114 | */ 115 | getDevice(): Device | null { 116 | return ua(this.req.headers["user-agent"])?.device || null; 117 | } 118 | 119 | /** 120 | * Get the CPU information from the user agent 121 | * @returns {CPU | null} - An object representing the CPU information, or null if not available 122 | * 123 | * @example 124 | * 125 | * const cpu = requestDetail.getCPU(); 126 | * console.log(cpu); // { architecture: 'amd64' } 127 | * 128 | */ 129 | getCPU(): CPU | null { 130 | return ua(this.req.headers["user-agent"])?.cpu || null; 131 | } 132 | 133 | /** 134 | * Get user agent. 135 | * @param {string} userAgent - The user agent string. 136 | * @example 137 | * fetchUserAgent(req.headers['user-agent']) 138 | */ 139 | static get fetchUserAgent() { 140 | return ua; 141 | } 142 | 143 | /** 144 | * Get IP information for a specific IP address 145 | * @param {string} ip - The IP address 146 | * @param {string | undefined} token - The [iplocate token](https://www.iplocate.io) 147 | * @returns {Promise} - A Promise that returns the information related to the specific IP address upon success 148 | * 149 | * @example 150 | * 151 | * const ipInfo = await RequestDetail.getIpInfo('192.168.0.1'); 152 | * console.log(ipInfo); // { ip: '192.168.0.1', country: 'US', city: 'New York', ... } 153 | * 154 | */ 155 | static getIpInfoByIp(ip: string, token?: string): Promise { 156 | return new Iplocate(token).getIpInfo(ip); 157 | } 158 | 159 | /** 160 | * Middleware function to attach `RequestDetail` instance to the `req` object 161 | * @param {Request} req - The Express request object 162 | * @param {Response} res - The Express response object 163 | * @param {NextFunction} next - The Express next function 164 | * 165 | * @example 166 | * 167 | * app.use(RequestDetail.middleware); 168 | * 169 | */ 170 | static middleware(req: Request, res: Response, next: NextFunction) { 171 | req.info = new RequestDetail(req, {}); 172 | next(); 173 | } 174 | } 175 | 176 | export default RequestDetail; 177 | --------------------------------------------------------------------------------