diff --git a/Dockerfile b/Dockerfile index 74869e0..fdb7895 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts as runner +FROM node:lts-alpine as builder WORKDIR /search ENV NODE_ENV production ARG COMMIT_ID @@ -9,5 +9,12 @@ COPY . . RUN npm ci RUN npm i -g typescript RUN npm run build:ts +COPY ./src/suggestions/icons /search/dist/suggestions/icons +FROM node:lts-alpine +WORKDIR /search +COPY --from=builder /search/dist /search/dist +COPY --from=builder /search/package.json /search/package.json +COPY --from=builder /search/src/GeoLite2-City.mmdb /search/src/GeoLite2-City.mmdb +RUN npm install --omit=dev EXPOSE 8080 -CMD ["npm", "run", "production"] \ No newline at end of file +CMD ["npm", "run", "production"] diff --git a/package-lock.json b/package-lock.json index 043d8ab..b8c0148 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@opentelemetry/sdk-trace-node": "^1.8.0", "@opentelemetry/semantic-conventions": "^1.8.0", "axios": "^1.1.3", + "cross-env": "^7.0.3", "dohjs": "^0.3.2", "fastify": "^4.9.2", "fastify-cli": "^5.5.1", @@ -36,13 +37,13 @@ "maxmind": "^4.3.2", "node-cache": "^5.1.2", "node-dig-dns": "^0.3.0", + "openai": "^3.1.0", "ping": "^0.4.1" }, "devDependencies": { "@types/node": "^15.0.0", "@types/tap": "^15.0.0", "concurrently": "^6.0.0", - "cross-env": "^7.0.3", "fastify-tsconfig": "^1.0.1", "tap": "^15.0.9", "ts-node": "^10.0.0", @@ -5200,7 +5201,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.1" }, @@ -5218,7 +5218,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6778,8 +6777,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "node_modules/isstream": { "version": "0.1.2", @@ -7844,6 +7842,36 @@ "wrappy": "1" } }, + "node_modules/openai": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.1.0.tgz", + "integrity": "sha512-v5kKFH5o+8ld+t0arudj833Mgm3GcgBnbyN9946bj6u7bvel4Yg6YFz2A4HLIYDzmMjIo0s6vSG9x73kOwvdCg==", + "dependencies": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, + "node_modules/openai/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/openai/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -8009,7 +8037,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -8942,7 +8969,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -8954,7 +8980,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -11819,7 +11844,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -15988,7 +16012,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, "requires": { "cross-spawn": "^7.0.1" } @@ -15997,7 +16020,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -17207,8 +17229,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isstream": { "version": "0.1.2", @@ -18076,6 +18097,35 @@ "wrappy": "1" } }, + "openai": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.1.0.tgz", + "integrity": "sha512-v5kKFH5o+8ld+t0arudj833Mgm3GcgBnbyN9946bj6u7bvel4Yg6YFz2A4HLIYDzmMjIo0s6vSG9x73kOwvdCg==", + "requires": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + }, + "dependencies": { + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -18204,8 +18254,7 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", @@ -18939,7 +18988,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -18947,8 +18995,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "shell-quote": { "version": "1.7.2", @@ -21057,7 +21104,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } diff --git a/package.json b/package.json index 9a44999..984860e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "predeploy": "npm run build:ts", "production": "cross-env NODE_ENV=production fastify start -l info dist/app.js -p 8080", "build:ts": "tsc", - "dev": "tsc && concurrently -k -p \"[{name}]\" -n \"search.emu.sh\" -c \"yellow.bold,cyan.bold\" \"tsc -w\" \"fastify start --ignore-watch=.ts$ -p 8080 -w -l info -P dist/app.js\"" + "dev": "tsc && concurrently -k -p \"[{name}]\" -n \"search.emu.sh\" -c \"yellow.bold,cyan.bold\" \"tsc -w\" \"fastify start --ignore-watch=.ts$ -p 3006 -w -l info -P dist/app.js\"" }, "keywords": [], "author": "", @@ -46,6 +46,7 @@ "maxmind": "^4.3.2", "node-cache": "^5.1.2", "node-dig-dns": "^0.3.0", + "openai": "^3.1.0", "ping": "^0.4.1" }, "devDependencies": { diff --git a/src/routes/suggest.ts b/src/routes/suggest.ts index 129939e..c253aaf 100644 --- a/src/routes/suggest.ts +++ b/src/routes/suggest.ts @@ -1,28 +1,28 @@ -import axios, { AxiosRequestConfig } from 'axios'; -import { FastifyPluginAsync } from 'fastify'; +import axios, { AxiosRequestConfig } from "axios"; +import { FastifyPluginAsync } from "fastify"; // import zlib from "zlib"; -import { Span, Tracer } from '@opentelemetry/api'; -import doh from 'dohjs'; -import { AbortEvent } from 'fastify-racing'; -import http from 'http'; -import https from 'https'; -import maxmind, { CityResponse } from 'maxmind'; -import NodeCache from 'node-cache'; -import suggestions from '../suggestions'; -import cryptoAssets from '../suggestions/cryptoAssets.json'; -const resolver = new doh.DohResolver('https://1.1.1.1/dns-query'); +import { Span, Tracer } from "@opentelemetry/api"; +import doh from "dohjs"; +import { AbortEvent } from "fastify-racing"; +import http from "http"; +import https from "https"; +import maxmind, { CityResponse } from "maxmind"; +import NodeCache from "node-cache"; +import suggestions from "../suggestions"; +import cryptoAssets from "../suggestions/cryptoAssets.json"; +const resolver = new doh.DohResolver("https://1.1.1.1/dns-query"); const cache = new NodeCache(); const httpAgent = new http.Agent({ keepAlive: true }); const httpsAgent = new https.Agent({ keepAlive: true }); const sendReply = (request, results, reply) => { - const suggestType = request?.query?.type === 'json' ? 'suggestType' : 'google:suggesttype'; - const suggestSubtypes = request?.query?.type === 'json' ? 'suggestSubtypes' : 'google:suggestsubtypes'; - const suggestDetail = request?.query?.type === 'json' ? 'suggestDetail' : 'google:suggestdetail'; - const suggestRelevance = request?.query?.type === 'json' ? 'suggestRelevance' : 'google:suggestrelevance'; - const verbatimrelevance = request?.query?.type === 'json' ? 'verbatimrelevance' : 'google:verbatimrelevance'; - const headerTexts = request?.query?.type === 'json' ? 'headerTexts' : 'google:headertexts'; - const clientData = request?.query?.type === 'json' ? 'clientData' : 'google:clientdata'; + const suggestType = request?.query?.type === "json" ? "suggestType" : "google:suggesttype"; + const suggestSubtypes = request?.query?.type === "json" ? "suggestSubtypes" : "google:suggestsubtypes"; + const suggestDetail = request?.query?.type === "json" ? "suggestDetail" : "google:suggestdetail"; + const suggestRelevance = request?.query?.type === "json" ? "suggestRelevance" : "google:suggestrelevance"; + const verbatimrelevance = request?.query?.type === "json" ? "verbatimrelevance" : "google:verbatimrelevance"; + const headerTexts = request?.query?.type === "json" ? "headerTexts" : "google:headertexts"; + const clientData = request?.query?.type === "json" ? "clientData" : "google:clientdata"; const googleRes = { [`${suggestType}`]: [], [`${headerTexts}`]: [], @@ -33,28 +33,28 @@ const sendReply = (request, results, reply) => { }; results.sort((a, b) => (a.relevance > b.relevance ? -1 : b.relevance > a.relevance ? 1 : 0)); results.sort((a, b) => (a.relevance > b.relevance ? -1 : b.relevance > a.relevance ? 1 : 0)); - if (request?.query?.type === 'json' || request?.query?.format === 'json') return results; - const searchFormat: Array = ['', [], [], []]; + if (request?.query?.type === "json" || request?.query?.format === "json") return results; + const searchFormat: Array = ["", [], [], []]; searchFormat[0] = request.query.q; results.forEach((res) => { // const converted = request?.query?.format === 'json' ? res : convert(res); Object.entries(res).forEach(([k, v], i) => { try { - k === 'suggestion' ? searchFormat[1].push(v) && searchFormat[2].push('') : googleRes[k].push(v); + k === "suggestion" ? searchFormat[1].push(v) && searchFormat[2].push("") : googleRes[k].push(v); } catch (e) { - console.log('ERROR AT', k); + console.log("ERROR AT", k); } }); }); - googleRes[verbatimrelevance] = typeof results[0] !== 'undefined' ? results[0][`${suggestRelevance}`] : 555; + googleRes[verbatimrelevance] = typeof results[0] !== "undefined" ? results[0][`${suggestRelevance}`] : 555; // console.log(googleResType); searchFormat.push(googleRes); console.log(JSON.stringify(searchFormat)); return reply .code(200) - .header('Content-disposition', 'attachment; filename=xd.txt') - .type('text/javascript; charset=UTF-8') + .header("Content-disposition", "attachment; filename=xd.txt") + .type("text/javascript; charset=UTF-8") .send(`)]}'\n${JSON.stringify(searchFormat)}`); }; const fetchResult = async (signal, request, reply) => { @@ -63,13 +63,13 @@ const fetchResult = async (signal, request, reply) => { httpsAgent, signal, }); - const suggestType = request?.query?.type === 'json' ? 'suggestType' : 'google:suggesttype'; - const suggestSubtypes = request?.query?.type === 'json' ? 'suggestSubtypes' : 'google:suggestsubtypes'; - const suggestDetail = request?.query?.type === 'json' ? 'suggestDetail' : 'google:suggestdetail'; - const suggestRelevance = request?.query?.type === 'json' ? 'suggestRelevance' : 'google:suggestrelevance'; - const verbatimrelevance = request?.query?.type === 'json' ? 'verbatimrelevance' : 'google:verbatimrelevance'; - const headerTexts = request?.query?.type === 'json' ? 'headerTexts' : 'google:headertexts'; - const clientData = request?.query?.type === 'json' ? 'clientData' : 'google:clientdata'; + const suggestType = request?.query?.type === "json" ? "suggestType" : "google:suggesttype"; + const suggestSubtypes = request?.query?.type === "json" ? "suggestSubtypes" : "google:suggestsubtypes"; + const suggestDetail = request?.query?.type === "json" ? "suggestDetail" : "google:suggestdetail"; + const suggestRelevance = request?.query?.type === "json" ? "suggestRelevance" : "google:suggestrelevance"; + const verbatimrelevance = request?.query?.type === "json" ? "verbatimrelevance" : "google:verbatimrelevance"; + const headerTexts = request?.query?.type === "json" ? "headerTexts" : "google:headertexts"; + const clientData = request?.query?.type === "json" ? "clientData" : "google:clientdata"; const { activeSpan, tracer, @@ -80,16 +80,11 @@ const fetchResult = async (signal, request, reply) => { const t: Tracer = tracer; const s: Span = activeSpan; s.updateName(`[${request.method}]: /suggest`); - s.setAttribute('query', request.query.q); + s.setAttribute("query", request.query.q); const childSpan = tracer.startSpan(`${activeSpan.name} - child process`); - // doSomeWork() - - // Pipe the buffer to the response stream. - // Spans started in a wrapped route will automatically be children of the activeSpan. - const lookup = await maxmind.open('./src/GeoLite2-City.mmdb'); let useApiKeys: boolean = false; // query param to actually utilize the API keys specified in .env - if (request.query?.useApiKeys === 'true') { + if (request.query?.useApiKeys === "true") { useApiKeys = true; } // console.log(request.headers); @@ -99,7 +94,7 @@ const fetchResult = async (signal, request, reply) => { const q = request.query?.q; reply.code(200); const bangSlug = searchRegex?.groups.bang; - const searchingBang = typeof bangSlug === 'string' && typeof search !== 'undefined'; + const searchingBang = typeof bangSlug === "string" && typeof search !== "undefined"; const googleQuery = searchingBang ? { ...request.query, q: search } : request.query; delete googleQuery.useApiKeys; // used internally, don't pass this to google let results = []; @@ -111,14 +106,14 @@ const fetchResult = async (signal, request, reply) => { }); if (shouldPush) { results.push({ - suggestion: `!${s.name}${typeof search !== 'undefined' ? ` ${search}` : ''}`, - [`${suggestType}`]: 'ENTITY', - [`${suggestSubtypes}`]: ['Custom Bang', 'BANG', s.url.replace(`~QUERYHERE~`, search)], + suggestion: `!${s.name}${typeof search !== "undefined" ? ` ${search}` : ""}`, + [`${suggestType}`]: "ENTITY", + [`${suggestSubtypes}`]: ["Custom Bang", "BANG", s.url.replace(`~QUERYHERE~`, search)], [`${suggestDetail}`]: { a: s.url.replace(`~QUERYHERE~`, search), - dc: '#DE5833', + dc: "#DE5833", i: `https://search.emu.sh/icons/${s.favicon}`, - q: ' ', + q: " ", t: `!${s?.name} - ${s?.description}: ${search}`, }, [`${suggestRelevance}`]: 444, @@ -142,7 +137,7 @@ const fetchResult = async (signal, request, reply) => { const cryptocompareKey = process?.env?.CRYPTOCOMPARE_KEY || false; if (assets.length > 0 && useApiKeys && cryptocompareKey) { let opt: AxiosRequestConfig = { - method: 'GET', + method: "GET", url: `https://min-api.cryptocompare.com/data/price?fsym=${assets[0].assetSymbol}&tsyms=USD&api_key=${cryptocompareKey}`, httpsAgent, httpAgent, @@ -150,34 +145,34 @@ const fetchResult = async (signal, request, reply) => { const cachedPrice = cache.get(assets[0].assetSymbol); let cryptoData: any = { data: { USD: cachedPrice } }; - if (typeof cachedPrice === 'undefined') { - console.log('price not cached'); + if (typeof cachedPrice === "undefined") { + console.log("price not cached"); cryptoData = await ax.request(opt); cache.set(assets[0].assetSymbol, cryptoData.data.USD, 300); } else { - console.log('price cached'); + console.log("price cached"); } const coef = cryptoSearch?.groups?.coef ? cryptoSearch?.groups?.coef : 1; console.log(cryptoData?.data?.USD); results.push({ - suggestion: `!cmc ${assets[0].assetName.toLowerCase().replace(' ', '-')}`, - [`${suggestType}`]: 'ENTITY', - [`${suggestSubtypes}`]: ['crypto'], + suggestion: `!cmc ${assets[0].assetName.toLowerCase().replace(" ", "-")}`, + [`${suggestType}`]: "ENTITY", + [`${suggestSubtypes}`]: ["crypto"], [`${suggestDetail}`]: { a: `${assets[0].assetName} - ${assets[0].description}`, - dc: '#DE5833', + dc: "#DE5833", i: assets[0].image, - q: '', + q: "", t: `${coef} ${assets[0].assetSymbol} = $${ coef * cryptoData.data.USD > 0.99 ? (coef * cryptoData.data.USD) .toFixed(2) .toString() - .replace(/\B(?=(\d{3})+(?!\d))/g, ',') + .replace(/\B(?=(\d{3})+(?!\d))/g, ",") : `${(coef * cryptoData.data.USD) .toString() - .split('.')[0] - .replace(/\B(?=(\d{3})+(?!\d))/g, ',')}.${(coef * cryptoData.data.USD).toString().split('.')[1]}` + .split(".")[0] + .replace(/\B(?=(\d{3})+(?!\d))/g, ",")}.${(coef * cryptoData.data.USD).toString().split(".")[1]}` } USD`, }, [`${suggestRelevance}`]: 1999, @@ -197,38 +192,39 @@ const fetchResult = async (signal, request, reply) => { /(dig\s)?(?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9])\s(?A|AAAA|ALIAS|CNAME|MX|NS|PTR|SOA|SRV|TXT)(\s@)?(?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9])?/i ); if (isDig?.length) { - console.log('MATCHED'); + console.log("MATCHED"); const response = await resolver.query(isDig.groups.domain, isDig.groups.DNS); await Promise.all( response.answers.map(async (ans, i) => { let answer = ans?.data?.exchange || ans?.data; console.log(`answer: ${JSON.stringify(ans)}`); - let ip = ''; + let ip = ""; // IP if ( - typeof answer === 'string' && + typeof answer === "string" && answer?.match(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/) ) { ip = answer; } let geolocation: any = {}; if (await maxmind.validate(ip)) { + const lookup = await maxmind.open("./src/GeoLite2-City.mmdb"); geolocation = await lookup.get(ip); } - console.log('hmm'); + console.log("hmm"); results.push({ suggestion: `${`!redirect https://toolbox.googleapps.com/apps/dig/?cache=${i}#${isDig.groups.DNS}/${isDig.groups.domain}`}`, - [`${suggestType}`]: 'ENTITY', + [`${suggestType}`]: "ENTITY", [`${suggestSubtypes}`]: [512, 433], [`${suggestDetail}`]: { a: `${ans.name} ${ans.ttl} ${ans.class} ${ans.type} ${ans.data?.exchange || ans.data}`, - dc: '#DE5833', + dc: "#DE5833", i: - typeof geolocation?.country?.iso_code !== 'undefined' + typeof geolocation?.country?.iso_code !== "undefined" ? `https://cdn.ip2location.com/assets/img/flags/${geolocation?.country?.iso_code?.toLowerCase()}.png?cache=${i}` : `https://emu.bz/gay.png`, - q: '', + q: "", t: `${ans.name} ${ans.ttl} ${ans.class} ${ans.type} ${ans.data?.exchange || ans.data}`, }, [`${suggestRelevance}`]: 512 * (response.answers.length - i), @@ -240,21 +236,21 @@ const fetchResult = async (signal, request, reply) => { console.log(`results: ${JSON.stringify(results)}`); console.log(`results.length: ${results.length}`); } else { - if (typeof search === 'string' && maxmind.validate(search?.trim())) { - const lookup = await maxmind.open('./src/GeoLite2-City.mmdb'); + if (typeof search === "string" && maxmind.validate(search?.trim())) { + const lookup = await maxmind.open("./src/GeoLite2-City.mmdb"); const geolocation = await lookup.get(search.trim()); console.log(`geolocation: ${JSON.stringify(geolocation)}`); // inferred type maxmind.CityResponse results.push({ suggestion: search, - [`${suggestType}`]: 'ENTITY', - [`${suggestSubtypes}`]: ['Country'], + [`${suggestType}`]: "ENTITY", + [`${suggestSubtypes}`]: ["Country"], [`${suggestDetail}`]: { - a: `${geolocation?.city?.names?.en ? `${geolocation?.city?.names?.en}, ` : ''}${ - geolocation?.subdivisions?.length ? `${geolocation?.subdivisions[0]?.iso_code}, ` : '' + a: `${geolocation?.city?.names?.en ? `${geolocation?.city?.names?.en}, ` : ""}${ + geolocation?.subdivisions?.length ? `${geolocation?.subdivisions[0]?.iso_code}, ` : "" } ${geolocation?.country?.names?.en} ${(geolocation as any)?.location?.latitude} ${(geolocation as any)?.location?.longitude}`, - dc: '#DE5833', + dc: "#DE5833", i: `https://cdn.ip2location.com/assets/img/flags/${geolocation?.country?.iso_code?.toLowerCase()}.png`, - q: 'test', + q: "test", t: `${search}`, }, [`${suggestRelevance}`]: 9999, @@ -262,12 +258,12 @@ const fetchResult = async (signal, request, reply) => { } } if (results.length > 0) resolve(results); - else reject('no results'); + else reject("no results"); }), //DuckDuckGo bangs new Promise>(async (resolve, reject) => { - if (typeof bangSlug !== 'string' || request.query.q.trim() === '') { - console.log('rejecting'); + if (typeof bangSlug !== "string" || request.query.q.trim() === "") { + console.log("rejecting"); reject(); } else { let results = []; @@ -283,18 +279,18 @@ const fetchResult = async (signal, request, reply) => { .then((res: any) => { const [{ data: bangs }, { data: bangss }] = res; if (bangs?.Redirect) { - console.log('redirect'); + console.log("redirect"); if (bangs?.Image) results.push({ suggestion: `${request.query.q}`, - [`${suggestType}`]: 'ENTITY', + [`${suggestType}`]: "ENTITY", [`${suggestSubtypes}`]: [512, 199, 175], [`${suggestDetail}`]: { a: `${bangs?.Redirect}`, - dc: '#DE5833', - i: `${bangs?.Image.startsWith('http') ? bangs?.Image : `https://duckduckgo.com/${bangs?.Image}`}`, - q: 'redirect', - zae: '/g/test', + dc: "#DE5833", + i: `${bangs?.Image.startsWith("http") ? bangs?.Image : `https://duckduckgo.com/${bangs?.Image}`}`, + q: "redirect", + zae: "/g/test", t: `${request.query.q.trim()}`, }, [`${suggestRelevance}`]: 900 - results.length, @@ -306,18 +302,18 @@ const fetchResult = async (signal, request, reply) => { if (!searchingBang) { if (b?.image?.length > 0) { // b.image = b.image.substring(0, b.image.indexOf('?')); - b.image = b.image += '?cache=' + (Math.random() + 1).toString(36).substring(7); + b.image = b.image += "?cache=" + (Math.random() + 1).toString(36).substring(7); results.push({ - suggestion: `${b.phrase} ${search ? `${search}` : ''}`, - [`${suggestType}`]: 'ENTITY', - [`${suggestSubtypes}`]: ['DuckDuckGo'], + suggestion: `${b.phrase} ${search ? `${search}` : ""}`, + [`${suggestType}`]: "ENTITY", + [`${suggestSubtypes}`]: ["DuckDuckGo"], [`${suggestDetail}`]: { a: b.snippet, - dc: '#DE5833', + dc: "#DE5833", i: b.image, - q: 'bang', - zae: '/g/test', - t: `${b.phrase} - ${typeof b.snippet !== 'undefined' ? ` ${b.snippet}` : ''}`, + q: "bang", + zae: "/g/test", + t: `${b.phrase} - ${typeof b.snippet !== "undefined" ? ` ${b.snippet}` : ""}`, }, [`${suggestRelevance}`]: 6000 - results.length, }); @@ -326,15 +322,15 @@ const fetchResult = async (signal, request, reply) => { if (b?.phrase === `!${bangSlug}`) { results.push({ suggestion: `${request.query.q}`, - [`${suggestType}`]: 'ENTITY', - [`${suggestSubtypes}`]: ['DuckDuckGo'], + [`${suggestType}`]: "ENTITY", + [`${suggestSubtypes}`]: ["DuckDuckGo"], [`${suggestDetail}`]: { a: `${bangs?.Redirect}`, - dc: '#DE5833', + dc: "#DE5833", i: b.image, - q: 'alice=true', - zae: '/g/test', - t: `${b.phrase} - ${typeof b.snippet !== 'undefined' ? ` ${b.snippet}` : ''}`, + q: "alice=true", + zae: "/g/test", + t: `${b.phrase} - ${typeof b.snippet !== "undefined" ? ` ${b.snippet}` : ""}`, }, [`${suggestRelevance}`]: 800 - results.length, }); @@ -346,7 +342,7 @@ const fetchResult = async (signal, request, reply) => { .catch((e) => console.log(e.code)); } if (results.length > 0) resolve(results); - else reject('no results'); + else reject("no results"); }), ]); console.log(`results::: ${results}`); @@ -356,32 +352,32 @@ const fetchResult = async (signal, request, reply) => { if (results.length === 0) { results = await new Promise>((resolve, reject) => { const options = { - Method: 'GET', - url: 'https://www.google.com/complete/search', + Method: "GET", + url: "https://www.google.com/complete/search", params: googleQuery, headers: { - 'x-client-data': 'CJS2yQEIpbbJAQjEtskBCKmdygEIvY7LAQjQmssBCKCgywEI5/HLAQis8ssBCN3yywEI6fLLAQjv98sBCJn4ywEItPjLAQie+csBGI6eywEYuvLLARjf+csB', - 'sec-fetch-site': 'none', - 'sec-fetch-mode': 'no-cors', - 'sec-fetch-dest': 'empty', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', - 'accept-language': 'en-US,en;q=0.9', - cookie: 'cgic=IgMqLyo', + "x-client-data": "CJS2yQEIpbbJAQjEtskBCKmdygEIvY7LAQjQmssBCKCgywEI5/HLAQis8ssBCN3yywEI6fLLAQjv98sBCJn4ywEItPjLAQie+csBGI6eywEYuvLLARjf+csB", + "sec-fetch-site": "none", + "sec-fetch-mode": "no-cors", + "sec-fetch-dest": "empty", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", + "accept-language": "en-US,en;q=0.9", + cookie: "cgic=IgMqLyo", }, }; ax.request(options) .then((res) => { let results = []; - const data = res.data.replace(/.*/, '').substr(1); + const data = res.data.replace(/.*/, "").substr(1); const response = JSON.parse(data); response[1].forEach((key, i) => { results.push({ - suggestion: searchingBang ? key.replace(new RegExp(`(^)(${bangSlug}\\s)?`, 'g'), `!${bangSlug} ${'$1'}`) : key, + suggestion: searchingBang ? key.replace(new RegExp(`(^)(${bangSlug}\\s)?`, "g"), `!${bangSlug} ${"$1"}`) : key, [`${suggestType}`]: response[4][`google:suggesttype`]?.[i], [`${suggestSubtypes}`]: response[4]?.[`google:suggestsubtypes`]?.[i], [`${suggestDetail}`]: - typeof response[4]?.[`google:suggestdetail`]?.[i] !== 'undefined' && searchingBang - ? { ...response[4]?.[`google:suggestdetail`]?.[i], a: `${response[4]?.[`google:suggestdetail`]?.[i]['a']} via !${bangSlug}` } + typeof response[4]?.[`google:suggestdetail`]?.[i] !== "undefined" && searchingBang + ? { ...response[4]?.[`google:suggestdetail`]?.[i], a: `${response[4]?.[`google:suggestdetail`]?.[i]["a"]} via !${bangSlug}` } : response[4]?.[`google:suggestdetail`]?.[i] || {}, [`${suggestRelevance}`]: response[4][`google:suggestrelevance`]?.[i], }); @@ -389,7 +385,7 @@ const fetchResult = async (signal, request, reply) => { if (results.length > 0) { resolve(results); } else { - console.log('rejecting google promise'); + console.log("rejecting google promise"); reject(); } }) @@ -405,12 +401,12 @@ const fetchResult = async (signal, request, reply) => { return sendReply(request, results, reply); }; const suggest: FastifyPluginAsync = async (fastify, opts): Promise => { - fastify.get('/suggest', async function (request: any, reply) { + fastify.get("/suggest", async function (request: any, reply) { const signal = request.race(); const result: AbortEvent | unknown = await Promise.race([ signal, new Promise(async (resolve, reject) => { - let result = ''; + let result = ""; try { result = await fetchResult(signal, request, reply); resolve(result); @@ -421,7 +417,7 @@ const suggest: FastifyPluginAsync = async (fastify, opts): Promise => { } }).catch((e) => console.log(e.code)), ]); - if ((result)?.type === 'aborted') return 'aborted'; + if ((result)?.type === "aborted") return "aborted"; else return result; }); }; diff --git a/src/suggestions.ts b/src/suggestions.ts index 5db9efc..90eefb2 100644 --- a/src/suggestions.ts +++ b/src/suggestions.ts @@ -9,15 +9,24 @@ function Suggestion(name: string, aliases: string[], url: string, favicon: strin const suggestions = []; const search = `~QUERYHERE~`; -suggestions.push(new Suggestion('public', ['pub'], `https://github.com/section/${search}`, 'github.png', 'Section Github Org Search')); -suggestions.push(new Suggestion('redirect', [], `${search}`, 'redirect.png', 'redirect to url')); +suggestions.push(new Suggestion("public", ["pub"], `https://github.com/section/${search}`, "github.png", "Section Github Org Search")); +suggestions.push(new Suggestion("redirect", [], `${search}`, "redirect.png", "redirect to url")); suggestions.push( new Suggestion( - 'orders', - ['o'], + "ebo", + ["ebayorders"], + `https://www.ebay.com/mye/myebay/v2/purchase?page=1&q=${search}&mp=purchase-search-module-v2&type=v2&pg=purchase`, + "ebay.png", + "ebay orders" + ) +); +suggestions.push( + new Suggestion( + "orders", + ["o"], `https://smile.amazon.com/gp/your-account/order-history/ref=ppx_yo_dt_b_search?opt=ab&search=${search}`, - 'amazon-32.jpg', - 'Amazon Orders' + "amazon-32.jpg", + "Amazon Orders" ) );