search.0t.rocks/dataapp/index.ts

1745 lines
57 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import mustache from 'mustache'
import express, { response } from 'express'
import axios from 'axios'
import { SolrRecord } from './types/solrrecord'
import _ from 'lodash'
import bodyParser from 'body-parser'
import { v4 as uuidv4, v4 } from 'uuid'
import ss from 'simple-statistics'
import ip from 'ip'
import crypto from 'crypto'
import { spawn } from 'child_process'
// import fetch from 'node-fetch';
const stateMapping: Record<string, string> = {
"Alabama": "AL",
"Kentucky": "KY",
"Ohio": "OH",
"Alaska": "AK",
"Louisiana": "LA",
"Oklahoma": "OK",
"Arizona": "AZ",
"Maine": "ME",
"Oregon": "OR",
"Arkansas": "AR",
"Maryland": "MD",
"Pennsylvania": "PA",
"American Samoa": "AS",
"Massachusetts": "MA",
"Puerto Rico": "PR",
"California": "CA",
"Michigan": "MI",
"Rhode Island": "RI",
"Colorado": "CO",
"Minnesota": "MN",
"South Carolina": "SC",
"Connecticut": "CT",
"Mississippi": "MS",
"South Dakota": "SD",
"Delaware": "DE",
"Missouri": "MO",
"Tennessee": "TN",
"District of Columbia": "DC",
"Montana": "MT",
"Texas": "TX",
"Florida": "FL",
"Nebraska": "NE",
"Trust Territories": "TT",
"Georgia": "GA",
"Nevada": "NV",
"Utah": "UT",
"Guam": "GU",
"New Hampshire": "NH",
"Vermont": "VT",
"Hawaii": "HI",
"New Jersey": "NJ",
"Virginia": "VA",
"Idaho": "ID",
"New Mexico": "NM",
"Virgin Islands": "VI",
"Illinois": "IL",
"New York": "NY",
"Washington": "WA",
"Indiana": "IN",
"North Carolina": "NC",
"West Virginia": "WV",
"Iowa": "IA",
"North Dakota": "ND",
"Wisconsin": "WI",
"Kansas": "KS",
"Northern Mariana Islands": "MP",
"Wyoming": "WY"
}
const streetSuffixMapping: Record<string, string> = {
"Alley": "aly",
"Alleyway": "alyw",
"Anex": "anx",
"Annex": "anx",
"Arcade": "arc",
"Avenue": "ave",
"Bayou": "byu",
"Beach": "bch",
"Bend": "bnd",
"Bluff": "blf",
"Bottom": "btm",
"Boulevard": "blvd",
"Branch": "br",
"Bridge": "brg",
"Brook": "brk",
"Bypass": "byp",
"Camp": "cp",
"Canyon": "cyn",
"Cape": "cpe",
"Causeway": "cswy",
"Center": "ctr",
"Circle": "cir",
"Cliff": "clf",
"Club": "clb",
"Common": "cmn",
"Corner": "cor",
"Course": "crse",
"Court": "ct",
"Courts": "cts",
"Cove": "cv",
"Creek": "crk",
"Crescent": "cres",
"Crest": "crst",
"Crossing": "xing",
"Crossroad": "xrd",
"Curve": "curv",
"Dale": "dl",
"Dam": "dm",
"Drive": "dr",
"Estate": "est",
"Expressway": "expy",
"Extension": "ext",
"Falls": "fls",
"Ferry": "fry",
"Field": "fld",
"Flat": "flt",
"Ford": "frd",
"Forest": "frst",
"Forge": "frg",
"Fork": "frk",
"Fort": "ft",
"Freeway": "fwy",
"Garden": "gdn",
"Gateway": "gtwy",
"Glen": "gln",
"Green": "grn",
"Grove": "grv",
"Harbor": "hbr",
"Haven": "hvn",
"Heights": "hts",
"Highway": "hwy",
"Hill": "hl",
"Hollow": "holw",
"Inlet": "inlt",
"Island": "is",
"Junction": "jct",
"Key": "ky",
"Knoll": "knl",
"Lake": "lk",
"Landing": "lndg",
"Lane": "ln",
"Light": "lgt",
"Loaf": "lf",
"Lock": "lck",
"Locks": "lcks",
"Lodge": "ldg",
"Loop": "loop",
"Mall": "mall",
"Manor": "mnr",
"Meadow": "mdw",
"Mews": "mews",
"Mill": "ml",
"Mission": "msn",
"Motorway": "mtwy",
"Mount": "mt",
"Mountain": "mtn",
"Neck": "nck",
"Orchard": "orch",
"Overpass": "opas",
"Park": "park",
"Parkway": "pkwy",
"Pass": "pass",
"Path": "path",
"Pike": "pike",
"Pine": "pne",
"Place": "pl",
"Plain": "pln",
"Plaza": "plz",
"Point": "pt",
"Port": "prt",
"Prairie": "pr",
"Radial": "radl",
"Ramp": "ramp",
"Ranch": "rnch",
"Rapid": "rpd",
"Rest": "rst",
"Ridge": "rdg",
"River": "riv",
"Road": "rd",
"Route": "rte",
"Row": "row",
"Rue": "rue",
"Run": "run",
"Shore": "shr",
"Skyway": "skwy",
"Spring": "spg",
"Springs": "spgs",
"Spur": "spur",
"Square": "sq",
"Station": "sta",
"Stravenue": "stra",
"Stream": "strm",
"Street": "st",
"Summit": "smt",
"Terrace": "ter",
"Throughway": "trwy",
"Trace": "trce",
"Track": "trak",
"Trafficway": "trfy",
"Trail": "trl",
"Trailer": "trlr",
"Tunnel": "tunl",
"Turnpike": "tpke",
"Underpass": "upas",
"Union": "un",
"Valley": "vly",
"Viaduct": "via",
"View": "vw",
"Village": "vlg",
"Ville": "vl",
"Vista": "vis",
"Walk": "walk",
"Way": "way",
"Well": "wl",
"Wells": "wls",
"Wye": "wye"
}
const app = express()
const port = 3000
const hlrToken = ''
const hlrSecret = ''
const basic = crypto.createHash('sha256').update(hlrToken + ':' + hlrSecret).digest('hex');
const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
let handledCharges: string[] = []
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
const fs = require('fs')
const donations = JSON.parse(fs.readFileSync('templates/pages/donations.json', 'utf-8'))
// const mapTemplate = fs.readFileSync('templates/pages/map.html', 'utf-8')
const indexTemplate = fs.readFileSync('templates/pages/home.mustache', 'utf-8')
const recordByIdTemplate = fs.readFileSync('templates/pages/recordById.mustache', 'utf-8')
const recordsListingTemplate = fs.readFileSync('templates/pages/recordsListing.mustache', 'utf-8')
const donationsTemplate = fs.readFileSync('templates/pages/donations.html', 'utf-8')
const exportsTemplate = fs.readFileSync('templates/pages/exports.html', 'utf-8')
const visualizerTemplate = fs.readFileSync('templates/pages/visualizer.html', 'utf-8')
const servers = ["http://solr1:8983/solr/BigData/select"]
const blacklistedAutomatedIps: string[] = []
const whitelistedUserAgents: string[] = [
// API Keys
]
// Stores that last time a query was made to a server for an IP address
const lastQueryTime: Record<string, number> = {}
const lastQueryTimes: Record<string, number[]> = {}
const numLookupsPerIP: Record<string, number> = {}
let hasRequestedWallet: string[] = []
function getRandServer() {
var index = Math.floor(Math.random() * servers.length);
return servers[index];
}
function getRandWalletsServer() {
var index = Math.floor(Math.random() * servers.length);
return servers[index].replace('BigData', 'Wallets');
}
async function queryForDocsSpatial(latLong: string, distanceKm: number) {
const records: SolrRecord[] = []
let numDocs = 0
await axios.get(getRandServer(), {
params: {
q: "latLong:*",
rows: 500,
fq: "{!geofilt sfield=latLong}",
pt: latLong,
d: distanceKm
}
}).then(({ data }) => {
records.push(...data.response.docs)
numDocs += data.response.numFound
})
console.log(`Spatial query for ${latLong} returned ${numDocs} records.`)
return {
numDocs: numDocs,
records: records
}
}
async function getWalletBalance(wallet: string): Promise<number> {
return axios.get(getRandWalletsServer(), {
params: {
q: `id:"${wallet}"`
}
})
.then(({ data }) => {
console.log(data.response.docs[0])
if (data.response.docs[0]) {
return data.response.docs[0].credits
} else {
return 0
}
}
)
.catch((err) => { console.log(err); return 0 })
}
async function addWalletBalance(wallet: string, credits: number) {
if (typeof credits !== 'number') {
console.log(`Credits is not a number: ${credits}`)
throw new Error('Credits is not a number')
}
// Get the old balance
const oldBalance = await getWalletBalance(wallet)
console.log('old balance: ' + oldBalance + ' credits: ' + credits + ' wallet: ' + wallet + ' new balance: ' + (oldBalance + credits))
// Add the new balance
credits += oldBalance
// Update the wallet
return axios.post(getRandWalletsServer().replace('select', 'update'), {
"add": {
"doc": {
// uuid v4
"id": wallet,
"credits": credits
}
}
}, {
params: {
commit: true
}
}).then(() => {
console.log(`Added ${credits} credits to wallet ${wallet}`)
return true
}) .catch ((err) => {
console.log(err)
return false
})
}
async function removeWalletBalance(wallet: string, credits: number) {
if (typeof credits !== 'number') {
console.log(`Credits is not a number: ${credits}`)
throw new Error('Credits is not a number')
}
// Get the old balance
const oldBalance = await getWalletBalance(wallet)
// Remove the new balance
const newBalance = oldBalance - credits
// Update the wallet
return axios.post(getRandWalletsServer().replace('select', 'update'), {
"add": {
"doc": {
// uuid v4
"id": wallet,
"credits": newBalance
}
}
}, {
params: {
commit: true
}
}).then(() => {
console.log(`Removed ${credits} credits to wallet ${wallet}`)
return true
})
}
async function queryForDocs(query: string, limit?: number, start?: number, sort?: string) {
const records: SolrRecord[] = []
let numDocs = 0
await axios.get(getRandServer(), {
params: {
q: query,
rows: limit || 100,
sort: sort,
start: start || 0
}
}).then(({ data }) => {
records.push(...data.response.docs)
numDocs += data.response.numFound
})
return {
numDocs: numDocs,
records: records
}
}
async function queryForExportDocs(query: string, limit?: number, start?: number, sort?: string) {
const records: SolrRecord[] = []
let numDocs = 0
await axios.get(getRandServer().replace('BigData', 'Exports'), {
params: {
q: query,
rows: limit || 100,
sort: sort,
start: start || 0
}
}).then(({ data }) => {
records.push(...data.response.docs)
numDocs += data.response.numFound
})
return {
numDocs: numDocs,
records: records
}
}
function buildQuery(requestedQuery: Record<string, string | string[]>, res: express.Response) {
let query: string[] = []
let orQuery: string[] = []
let notQuery: string[] = []
let additionalQuery: string[] = []
let doAdditionalQuery = false
if (requestedQuery.firstName != undefined) {
if (typeof requestedQuery.firstName == 'string') {
query.push(`firstName:${requestedQuery.firstName.replace(' ', '?')}`)
}
}
if (requestedQuery.notfirstName != undefined) {
if (typeof requestedQuery.notfirstName === 'string') {
notQuery.push(`firstName:${requestedQuery.notfirstName.replace(' ', '?')}`)
} else if (Array.isArray(requestedQuery.notfirstName)) {
requestedQuery.notfirstName.forEach((notItem: any) => {
notQuery.push(`firstName:${notItem.replace(' ', '?')}`)
})
}
}
if (requestedQuery.lastName != undefined) {
if (typeof requestedQuery.lastName == 'string') {
query.push(`lastName:${requestedQuery.lastName.replace(' ', '?')}`)
}
}
if (requestedQuery.notlastName != undefined) {
if (typeof requestedQuery.notlastName === 'string') {
notQuery.push(`lastName:${requestedQuery.notlastName.replace(' ', '?')}`)
} else if (Array.isArray(requestedQuery.notlastName)) {
requestedQuery.notlastName.forEach((notItem: any) => {
notQuery.push(`lastName:${notItem.replace(' ', '?')}`)
})
}
}
if (requestedQuery.birthYear != undefined) {
if (typeof requestedQuery.birthYear == 'string') {
query.push(`birthYear:${requestedQuery.birthYear}`)
}
}
if (requestedQuery.notbirthYear != undefined) {
if (typeof requestedQuery.notbirthYear === 'string') {
notQuery.push(`birthYear:${requestedQuery.notbirthYear.replace(' ', '?')}`)
} else if (Array.isArray(requestedQuery.notbirthYear)) {
requestedQuery.notbirthYear.forEach((notItem: any) => {
notQuery.push(`birthYear:${notItem.replace(' ', '?')}`)
})
}
}
if (requestedQuery.ips != undefined) {
if (typeof requestedQuery.ips == 'string') {
query.push(`ips:WRfKdFVogXnk82${requestedQuery.ips}WRfKdFVogXnk82`)
}
}
if (requestedQuery.notips != undefined) {
if (typeof requestedQuery.notips === 'string') {
notQuery.push(`ips:WRfKdFVogXnk82${requestedQuery.notips}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notips)) {
requestedQuery.notips.forEach((notItem: any) => {
notQuery.push(`ips:WRfKdFVogXnk82${notItem}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.asn != undefined) {
if (typeof requestedQuery.asn == 'string') {
query.push(`asn:${requestedQuery.asn}`)
}
}
if (requestedQuery.notasn != undefined) {
if (typeof requestedQuery.notasn === 'string') {
notQuery.push(`asn:${requestedQuery.notasn.replace(' ', '?')}`)
} else if (Array.isArray(requestedQuery.notasn)) {
requestedQuery.notasn.forEach((notItem: any) => {
notQuery.push(`asn:${notItem.replace(' ', '?')}`)
})
}
}
if (requestedQuery.domain != undefined) {
if (requestedQuery.exact) {
if (typeof requestedQuery.domain == 'string') {
query.push(`domain:WRfKdFVogXnk82${requestedQuery.domain.replace(' ', '?')}WRfKdFVogXnk82`)
}
} else {
if (typeof requestedQuery.domain == 'string') {
query.push(`domain:${requestedQuery.domain.replace(' ', '?')}`)
}
}
}
if (requestedQuery.notdomain != undefined) {
if (typeof requestedQuery.notdomain === 'string') {
notQuery.push(`domain:WRfKdFVogXnk82${requestedQuery.notdomain.replace(' ', '?')}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notdomain)) {
requestedQuery.notdomain.forEach((notItem: any) => {
notQuery.push(`domain:WRfKdFVogXnk82${notItem.replace(' ', '?')}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.asnOrg != undefined) {
if (typeof requestedQuery.asnOrg == 'string') {
query.push(`asnOrg:WRfKdFVogXnk82${requestedQuery.asnOrg}WRfKdFVogXnk82`)
}
}
if (requestedQuery.notasnOrg != undefined) {
if (typeof requestedQuery.notasnOrg === 'string') {
notQuery.push(`asnOrg:${requestedQuery.notasnOrg.replace(' ', '?')}`)
} else if (Array.isArray(requestedQuery.notasnOrg)) {
requestedQuery.notasnOrg.forEach((notItem: any) => {
notQuery.push(`asnOrg:WRfKdFVogXnk82${notItem.replace(' ', '?')}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.country != undefined) {
if (typeof requestedQuery.country == 'string') {
query.push(`country:WRfKdFVogXnk82${requestedQuery.country}WRfKdFVogXnk82`)
}
}
if (requestedQuery.notcountry != undefined) {
if (typeof requestedQuery.notcountry === 'string') {
notQuery.push(`country:WRfKdFVogXnk82${requestedQuery.notcountry.replace(' ', '?')}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notcountry)) {
requestedQuery.notcountry.forEach((notItem: any) => {
notQuery.push(`country:WRfKdFVogXnk82${notItem.replace(' ', '?')}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.continent != undefined) {
if (typeof requestedQuery.continent == 'string') {
query.push(`continent:WRfKdFVogXnk82${requestedQuery.continent}WRfKdFVogXnk82`)
}
}
if (requestedQuery.notcontinent != undefined) {
if (typeof requestedQuery.notcontinent === 'string') {
notQuery.push(`continent:WRfKdFVogXnk82${requestedQuery.notcontinent.replace(' ', '?')}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notcontinent)) {
requestedQuery.notcontinent.forEach((notItem: any) => {
notQuery.push(`continent:WRfKdFVogXnk82${notItem.replace(' ', '?')}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.firstName != undefined && requestedQuery.lastName != undefined && typeof requestedQuery.firstName == 'string' && typeof requestedQuery.lastName == 'string') {
const firstName = requestedQuery.firstName.replace(' ', '?')
const lastName = requestedQuery.lastName.replace(' ', '?')
if (!requestedQuery.exact) {
additionalQuery.push(`emails:${firstName}?${lastName}`)
doAdditionalQuery = true
}
}
if (requestedQuery.source != undefined) {
if (typeof requestedQuery.source == 'string') {
query.push(`source:WRfKdFVogXnk82${requestedQuery.source}WRfKdFVogXnk82`)
}
}
if (requestedQuery.notsource != undefined) {
if (typeof requestedQuery.notsource === 'string') {
notQuery.push(`source:WRfKdFVogXnk82${requestedQuery.notsource.replace(' ', '?')}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notsource)) {
requestedQuery.notsource.forEach((notItem: any) => {
notQuery.push(`source:WRfKdFVogXnk82${notItem.replace(' ', '?')}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.emails != undefined) {
if (typeof requestedQuery.emails == 'string') {
// check if there's a * in the email
if (requestedQuery.emails.indexOf('*') != -1) {
query.push(`emails:${requestedQuery.emails.replace('@', '?')}`)
}
else {
if (requestedQuery.exact) {
query.push(`emails:WRfKdFVogXnk82${requestedQuery.emails.replace('@', '?')}WRfKdFVogXnk82`)
} else {
query.push(`emails:${requestedQuery.emails.replace('@', '?')}`)
}
}
const mostCommonEmailDomains = [/\@gmail\..*/gi, /\@yahoo\..*/gi, /\@hotmail\..*/gi, /\@outlook\..*/gi, /\@aol\..*/gi, /\@icloud\..*/gi, /\@mail\..*/gi, /\@protonmail\..*/gi, /\@zoho\..*/gi, /\@msn\..*/gi, /\@yandex\..*/gi, /\@gmx\..*/gi, /\@live\..*/gi, /\@mail\.ru\..*/gi, /\@inbox\..*/gi, /\@ymail\..*/gi, /\@comcast\..*/gi, /\@verizon\..*/gi, /\@att\..*/gi, /\@sbcglobal\..*/gi, /\@cox\..*/gi, /\@earthlink\..*/gi, /\@charter\..*/gi, /\@optonline\..*/gi, /\@frontier\..*/gi, /\@windstream\..*/gi, /\@q\.com\..*/gi, /\@btinternet\..*/gi, /\@btconnect\..*/gi, /\@ntlworld\..*/gi, /\@bt\..*/gi, /\@virginmedia\..*/gi, /\@btopenworld\..*/gi, /\@talktalk\..*/gi, /\@sky\..*/gi, /\@orange\..*/gi, /\@bt\..*/gi, /\@virgin\..*/gi, /\@ntl\..*/gi, /\@freeserve\..*/gi, /\@blueyonder\..*/gi, /\@btinternet\..*/gi, /\@tiscali\..*/gi, /\@virgin\..*/gi, /\@tesco\..*/gi, /\@onetel\..*/gi, /\@bt\..*/gi, /\@virgin\..*/gi, /\@ntl\..*/gi, /\@freeserve\..*/gi, /\@blueyonder\..*/gi, /\@btinternet\..*/gi, /\@tiscali\..*/gi, /\@virgin\..*/gi, /\@tesco\..*/gi, /\@onetel\..*/gi, /\@bt\..*/gi, /\@virgin\..*/]
const emailSplit = requestedQuery.emails.split('@')
if (requestedQuery.emails.indexOf('*') == -1) {
additionalQuery.push(`emails:WRfKdFVogXnk82${emailSplit[0]}WRfKdFVogXnk82`)
}
doAdditionalQuery = true;
let isCommonDomain = false;
for (const domain of mostCommonEmailDomains) {
if (domain.test(requestedQuery.emails as string)) {
isCommonDomain = true
}
}
if (!isCommonDomain) {
if (emailSplit.length === 2) {
const domain = emailSplit[emailSplit.length - 1]
additionalQuery.push(`emails:${domain}`)
doAdditionalQuery = true
}
}
}
}
if (requestedQuery.notemails != undefined) {
if (typeof requestedQuery.notemails === 'string') {
notQuery.push(`emails:WRfKdFVogXnk82${requestedQuery.notemails.replace('@', '?')}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notemails)) {
requestedQuery.notemails.forEach((notItem: any) => {
notQuery.push(`emails:WRfKdFVogXnk82${notItem.replace('@', '?')}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.VRN != undefined) {
if (typeof requestedQuery.VRN == 'string') {
query.push(`VRN:${requestedQuery.VRN.toLowerCase()}`)
}
}
if (requestedQuery.notVRN != undefined) {
if (typeof requestedQuery.notVRN === 'string') {
notQuery.push(`VRN:${requestedQuery.notVRN.toLowerCase()}`)
} else if (Array.isArray(requestedQuery.notVRN)) {
requestedQuery.notVRN.forEach((notItem: any) => {
notQuery.push(`VRN:${notItem.toLowerCase()}`)
})
}
}
if (requestedQuery.usernames != undefined) {
if (requestedQuery.exact) {
query.push(`usernames:WRfKdFVogXnk82${requestedQuery.usernames}WRfKdFVogXnk82`)
} else {
query.push(`usernames:${requestedQuery.usernames}`)
}
}
if (requestedQuery.notusernames != undefined) {
if (typeof requestedQuery.notusernames === 'string') {
notQuery.push(`usernames:${requestedQuery.notusernames}`)
} else if (Array.isArray(requestedQuery.notusernames)) {
requestedQuery.notusernames.forEach((notItem: any) => {
notQuery.push(`usernames:${notItem.toLowerCase()}`)
})
}
}
if (requestedQuery.address != undefined) {
query.push(`address:WRfKdFVogXnk82${requestedQuery.address}WRfKdFVogXnk82`)
}
if (requestedQuery.notaddress != undefined) {
if (typeof requestedQuery.notaddress === 'string') {
notQuery.push(`address:WRfKdFVogXnk82${requestedQuery.notaddress}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notaddress)) {
requestedQuery.notaddress.forEach((notItem: any) => {
notQuery.push(`address:WRfKdFVogXnk82${notItem}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.city != undefined) {
query.push(`city:${requestedQuery.city}`)
}
if (requestedQuery.notcity != undefined) {
if (typeof requestedQuery.notcity === 'string') {
notQuery.push(`city:WRfKdFVogXnk82${requestedQuery.notcity}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notcity)) {
requestedQuery.notcity.forEach((notItem: any) => {
notQuery.push(`city:WRfKdFVogXnk82${notItem}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.zipCode != undefined) {
query.push(`zipCode:${requestedQuery.zipCode}`)
}
if (requestedQuery.notzipCode != undefined) {
if (typeof requestedQuery.notzipCode === 'string') {
notQuery.push(`address:WRfKdFVogXnk82${requestedQuery.notzipCode}WRfKdFVogXnk82`)
} else if (Array.isArray(requestedQuery.notzipCode)) {
requestedQuery.notzipCode.forEach((notItem: any) => {
notQuery.push(`address:WRfKdFVogXnk82${notItem}WRfKdFVogXnk82`)
})
}
}
if (requestedQuery.state != undefined) {
query.push(`state:${requestedQuery.state}`)
}
if (requestedQuery.notstate != undefined) {
if (typeof requestedQuery.notstate === 'string') {
notQuery.push(`state:${requestedQuery.notstate}`)
} else if (Array.isArray(requestedQuery.notstate)) {
requestedQuery.notstate.forEach((notItem: any) => {
notQuery.push(`state:${notItem}`)
})
}
}
if (requestedQuery.phoneNumbers != undefined && typeof requestedQuery.phoneNumbers == 'string') {
if (!requestedQuery.exact) {
query.push(`phoneNumbers:WRfKdFVogXnk82${requestedQuery.phoneNumbers.replace(/\D+/g, "")}WRfKdFVogXnk82`)
} else {
// Replace all non-digits or non-question marks with
query.push(`phoneNumbers:WRfKdFVogXnk82${requestedQuery.phoneNumbers.replace(/\D+/g, "")}WRfKdFVogXnk82`)
additionalQuery.push(`phoneNumbers:1${requestedQuery.phoneNumbers.replace(/\D+/g, "")}`)
additionalQuery.push(`phoneNumbers:7${requestedQuery.phoneNumbers.replace(/\D+/g, "")}`)
doAdditionalQuery = true;
}
}
if (requestedQuery.notphoneNumbers != undefined) {
if (typeof requestedQuery.notphoneNumbers === 'string') {
notQuery.push(`phoneNumbers:${requestedQuery.notphoneNumbers}`)
} else if (Array.isArray(requestedQuery.notphoneNumbers)) {
requestedQuery.notphoneNumbers.forEach((notItem: any) => {
notQuery.push(`phoneNumbers:${notItem}`)
})
}
}
if (requestedQuery.passwords != undefined && typeof requestedQuery.passwords === 'string') {
query.push(`passwords:${requestedQuery.passwords}`)
} else if (requestedQuery.passwords != undefined && Array.isArray(requestedQuery.passwords)) {
requestedQuery.passwords.forEach((password) => {
orQuery.push(`passwords:WRfKdFVogXnk82${password}WRfKdFVogXnk82`)
})
}
if (requestedQuery.notpasswords != undefined) {
if (typeof requestedQuery.notpasswords === 'string') {
notQuery.push(`passwords:${requestedQuery.notpasswords}`)
} else if (Array.isArray(requestedQuery.notpasswords)) {
requestedQuery.notpasswords.forEach((notItem: any) => {
notQuery.push(`passwords:${notItem}`)
})
}
}
if (requestedQuery.vin != undefined && typeof requestedQuery.vin === 'string') {
query.push(`vin:${requestedQuery.vin}`)
} else if (requestedQuery.vin != undefined && Array.isArray(requestedQuery.vin)) {
requestedQuery.vin.forEach((vin) => {
orQuery.push(`vin:WRfKdFVogXnk82${vin}WRfKdFVogXnk82`)
})
}
let queryBuilt = sanitizeQuery(query.join(' AND '))
if (orQuery.length > 0) {
if (query.length > 0) {
queryBuilt += ' OR ' + sanitizeQuery(orQuery.join(' OR '))
} else {
queryBuilt += sanitizeQuery(orQuery.join(' OR '))
}
}
if (notQuery.length > 0) {
if (query.length > 0) {
queryBuilt += ' NOT ' + sanitizeQuery(notQuery.join(' NOT '))
} else {
return res.status(400).send("Error: you sent an negative query without a regular query! Silly goose.")
}
}
return {
query: queryBuilt,
additionalQuery: additionalQuery,
doAdditionalQuery: doAdditionalQuery
}
}
async function getSimilarRecords(record: SolrRecord) {
let responses: any[] = []
const scoresList: Record<string, number> = {}
const relatedDocIDs: string[] = []
await Promise.all([
...(record.emails || []).map((email) => {
return axios.get(getRandServer(), {
params: {
q: `emails:"${email.replace(/\"/gi, '').replace(/\//gi, '')}"`,
rows: 20,
fl: 'id'
}
}).then(({ data }) => {
data.response.docs.forEach((doc: SolrRecord) => {
if (doc.id === record.id) {
return
}
if (!(doc.id in scoresList)) {
scoresList[doc.id] = 15
} else {
scoresList[doc.id] += 5
}
relatedDocIDs.push(doc.id)
})
})
}),
...(record.usernames || []).map((email) => {
return axios.get(getRandServer(), {
params: {
q: `usernames:"${email.replace(/\"/gi, '').replace(/\//gi, '')}"`,
rows: 20,
fl: 'id'
}
}).then(({ data }) => {
data.response.docs.forEach((doc: SolrRecord) => {
if (doc.id === record.id) {
return
}
if (!(doc.id in scoresList)) {
scoresList[doc.id] = 15
} else {
scoresList[doc.id] += 5
}
relatedDocIDs.push(doc.id)
})
})
}),
axios.get(getRandServer(), {
params: {
q: `id:${record.id}`,
mlt: true,
'mlt.fl': 'emails',
'mlt.mindf': 2,
'mlt.mintf': 2,
}
}).then(({ data }) => {
data.moreLikeThis.forEach((doc: any) => {
if (typeof doc == 'object') {
doc.docs.forEach((relatedDoc: any) => {
if (scoresList[relatedDoc.id]) {
scoresList[relatedDoc.id] += relatedDoc.score
} else {
scoresList[relatedDoc.id] = relatedDoc.score
}
})
}
})
responses.push(...data.moreLikeThis)
}),
axios.get(getRandServer(), {
params: {
q: `id:${record.id}`,
mlt: true,
'mlt.fl': 'usernames',
'mlt.mindf': 1,
'mlt.mintf': 1,
'mlt.match.include': true,
}
}).then(({ data }) => {
data.moreLikeThis.forEach((doc: any) => {
if (typeof doc == 'object') {
doc.docs.forEach((relatedDoc: any) => {
if (scoresList[relatedDoc.id]) {
scoresList[relatedDoc.id] += relatedDoc.score
} else {
scoresList[relatedDoc.id] = relatedDoc.score
}
})
}
})
responses.push(...data.moreLikeThis)
}),
axios.get(getRandServer(), {
params: {
q: `id:${record.id}`,
mlt: true,
'mlt.fl': 'phoneNumbers',
'mlt.mindf': 1,
'mlt.mintf': 1,
'mlt.match.include': true,
}
}).then(({ data }) => {
data.moreLikeThis.forEach((doc: any) => {
if (typeof doc == 'object') {
doc.docs.forEach((relatedDoc: any) => {
if (scoresList[relatedDoc.id]) {
scoresList[relatedDoc.id] += relatedDoc.score
} else {
scoresList[relatedDoc.id] = relatedDoc.score
}
})
}
})
responses.push(...data.moreLikeThis)
}),
axios.get(getRandServer(), {
params: {
q: `id:${record.id}`,
mlt: true,
'mlt.fl': 'address_search',
'mlt.mindf': 1,
'mlt.mintf': 1,
'mlt.match.include': true,
}
}).then(({ data }) => {
data.moreLikeThis.forEach((doc: any) => {
if (typeof doc == 'object') {
doc.docs.forEach((relatedDoc: any) => {
if (scoresList[relatedDoc.id]) {
scoresList[relatedDoc.id] += relatedDoc.score
} else {
scoresList[relatedDoc.id] = relatedDoc.score
}
})
}
})
responses.push(...data.moreLikeThis)
}),
axios.get(getRandServer(), {
params: {
q: `id:${record.id}`,
mlt: true,
'mlt.fl': 'firstName,lastName',
'mlt.mindf': 1,
'mlt.mintf': 1,
'mlt.match.include': true,
}
}).then(({ data }) => {
data.moreLikeThis.forEach((doc: any) => {
if (typeof doc == 'object') {
doc.docs.forEach((relatedDoc: any) => {
if (scoresList[relatedDoc.id]) {
scoresList[relatedDoc.id] += relatedDoc.score
} else {
scoresList[relatedDoc.id] = relatedDoc.score
}
})
}
})
responses.push(...data.moreLikeThis)
}),
])
responses.forEach((response) => {
if (typeof response == 'object') {
relatedDocIDs.push(...(response.docs.map((doc: any) => doc.id)))
}
})
let docs: SolrRecord[] = []
// Get unique doc IDs
const uniqueDocIDs = [...new Set(relatedDocIDs)]
await Promise.all(uniqueDocIDs.map((doc: any) => queryForDocs(`id:${doc}`))).then((results) => {
results.forEach((result) => {
docs.push(...result.records.map((result) => {
return {
...result,
'similarity score': scoresList[result.id] || 0,
}
}))
})
})
// Sort the docs by score
docs = docs.sort((a, b) => {
if (scoresList[a.id] > scoresList[b.id]) {
return -1
} else if (scoresList[a.id] < scoresList[b.id]) {
return 1
} else {
return 0
}
})
return docs
}
function sanitizeQuery(query: string) {
//return query;
return query.replace(/[^\w\s\$\:\.\@\-\*\а-яА-ЯёЁ]/gi, '?').replace(/WRfKdFVogXnk82/g, '"')
}
app.get('/exports', async (req, res) => {
const rendered = mustache.render(exportsTemplate, {})
return res.send(rendered)
})
app.get('/exports/:walletid', async (req, res) => {
try {
const walletid = req.params.walletid
// Ensure the wallet ID is valid
if (!uuidRegex.test(walletid)) {
return res.status(400).json({
error: 'Invalid wallet ID.'
})
}
const records = await queryForExportDocs(`wallet:"${walletid}"`)
return res.json(records.records.map((record: any) => {
return {
...record,
jobid: record.id,
exportCount: record.count,
}
}))
} catch (e) {
return res.status(500).json([
{
id: 'internal error',
query: 'sorry bout that... try again?'
}
])
}
})
app.get('/', async (req, res) => {
const rendered = mustache.render(indexTemplate, { count: "14,491,682,918" })
return res.send(rendered)
})
app.get('/visualize', async (req, res) => {
const rendered = mustache.render(visualizerTemplate, {})
return res.send(rendered)
})
app.get('/map', async (req, res) => {
res.sendFile(__dirname + '/static/map.html')
})
app.get('/favicon.ico', (req, res) => {
// Read the favicon file and send it to the client
res.sendFile(__dirname + '/static/favicon.ico')
})
app.get('/robots.txt', (req, res) => {
// Read robots.txt and send it to the client
res.sendFile(__dirname + '/static/robots.txt')
})
app.get('/dear_peter', (req, res) => {
res.sendFile(__dirname + '/static/dear_peter.html')
})
app.get('/terms', (req, res) => {
res.sendFile(__dirname + '/static/terms.html')
})
app.get('/faq', (req, res) => {
res.sendFile(__dirname + '/static/faq.html')
})
app.get('/canary', (req, res) => {
res.sendFile(__dirname + '/static/canary.html')
})
app.get('/donations', (req, res) => {
// Render out the donations page tempate
const rendered = mustache.render(donationsTemplate, {
donations: donations.sort((a: any, b: any) => b.amount - a.amount)
})
return res.send(rendered)
})
app.post('/export', async (req, res) => {
try {
// Read body
let body = req.body
// Check if the body is valid
if (!body || !body.walletId || !body.exportCount) {
return res.status(400).send('Invalid body')
}
// Make sure export count is > 0
if (typeof body.exportCount !== 'number') {
// Set it to be a number
body.exportCount = parseInt(body.exportCount, 10)
}
if (body.exportCount <= 0) {
return res.status(400).send('Invalid export count')
}
// If the wallet ID is invalid uuid4
if (!uuidRegex.test(body.walletId)) {
return res.status(400).send('Invalid wallet ID')
}
// Get credits for the wallet
const credits = await getWalletBalance(body.walletId)
const cost = (body.exportCount - 100) / 10
if (cost > credits) {
return res.status(400).send('Insufficient credits')
}
// if cost < 0
if (cost < 0) {
return res.status(400).send('Invalid export count')
}
let requestedQuery: Record<string, any> = {}
for (const [key, value] of Object.entries(req.query)) {
if (key !== 'wt') {
if (typeof value === 'string' || Array.isArray(value)) {
requestedQuery[key] = value
} else {
console.log(`Invalid query: ${key} is not a string or array. ${value}`)
}
}
}
const queryBuilt = buildQuery(requestedQuery, res as any) as any
const jobid = uuidv4()
if (!queryBuilt.query || !queryBuilt.additionalQuery || typeof queryBuilt.doAdditionalQuery !== 'boolean') {
return res.status(400).send('Invalid query')
}
let finalQuery = queryBuilt.query
if (queryBuilt.additionalQuery.length > 0) {
finalQuery = `(${finalQuery}) OR ${sanitizeQuery(queryBuilt.additionalQuery.join(' OR '))}`
}
const payload = {
"status": "started",
"jobid": jobid,
"cost": cost,
"query": finalQuery,
"additionalQuery": queryBuilt.additionalQuery,
"doAdditionalQuery": queryBuilt.doAdditionalQuery,
"exportCount": body.exportCount,
"success": true,
"walletId": body.walletId
}
// Return to client that the job has been started
res.json(payload)
// End the response
res.end()
// Deduct clients from wallet
await removeWalletBalance(body.walletId, cost)
// Post to wallet DB
await axios.post(getRandServer().replace('BigData', 'Exports').replace('select', 'update'), {
"add": {
"doc": {
// uuid v4
"id": payload.jobid,
"cost": payload.cost,
"wallet": payload.walletId,
"query": payload.query,
"status": payload.status,
'count': payload.exportCount,
}
}
}, {
params: {
commit: true
}
})
// Base64 encode payload
const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64')
// asyncronously run python3 exports/doExport.py <encodedPayload>
const pythonProcess = spawn('python3', ['exports/doExport.py', encodedPayload])
// Listen for data from the python process
pythonProcess.stdout.on('data', (data) => {
console.log(`Export Job ${jobid}: ${data}`);
})
// Listen for errors from the python process
pythonProcess.stderr.on('data', (data) => {
console.error(`Export Job ${jobid}: ${data}`);
})
} catch (e) {
console.error(e)
res.status(400).send('stoooop')
}
})
app.post('/exports/callbacks/a-unique-id/exportCb', async (req, res) => {
// Read body
const body = req.body
if (body.status === 'failed') {
// Re-credit wallet
await addWalletBalance(body.walletId, body.cost)
}
// Post to db
await axios.post(getRandServer().replace('BigData', 'Exports').replace('select', 'update'), {
"add": {
"doc": {
// uuid v4
"id": body.jobid,
"cost": body.cost,
"wallet": body.walletId,
"query": body.query,
"status": body.status,
'count': body.exportCount,
"link": body.link
}
}
}, {
params: {
commit: true
}
})
return res.status(200)
})
app.get('/wallet/:walletId', async (req, res) => {
try {
const walletId = req.params.walletId
if (!uuidRegex.test(walletId)) {
console.log('bad wallet id regex')
return res.status(400).json({ "error": "bad wallet id" })
}
if (typeof walletId !== 'string') {
return res.status(400).json({ "error": "bad request" })
}
if (walletId.length !== 36) {
console.log('bad wallet id length')
return res.status(400).json({ "error": "bad wallet id" })
}
let balance = await getWalletBalance(walletId)
return res.json({ "credits": balance })
} catch {
return res.status(400).json({ "error": "stooppp" })
}
})
function isAutomated(req: any) {
// Check if ip is in blacklist
const connectingIp = req.headers['cf-connecting-ip'] || req.connection.remoteAddress;
if (blacklistedAutomatedIps.includes(connectingIp)) {
console.log(`BLACKLISTED IP ${connectingIp}`)
return true
}
// Check if user agent has axios in it
const userAgent = req.headers['user-agent']
if (whitelistedUserAgents.some((whitelistedAgent) => userAgent.includes(whitelistedAgent))) {
return false
}
if (userAgent != undefined) {
if (typeof userAgent == 'string') {
if (Object.keys(req.query).length === 1 && req.query.emails) {
// Check if the user has gotten their wallet
const connectingIp = req.headers['cf-connecting-ip']
if (typeof connectingIp === 'string') {
if (hasRequestedWallet.includes(connectingIp)) {
return false
}
}
}
}
}
return false
}
async function checkIPAutomatedSTDDEV(req: any) {
const ip = req.headers['cf-connecting-ip'] || req.connection.remoteAddress;
const userAgent = req.headers['user-agent']
if (whitelistedUserAgents.some((whitelistedAgent) => userAgent.includes(whitelistedAgent))) {
return false
}
if (ip === '0.0.0.0' || !ip) {
// Hello, KT :3c
console.log('wat r u doin')
return true
} else if (typeof ip === 'string') {
if (numLookupsPerIP[ip] === undefined) {
numLookupsPerIP[ip] = 0
}
// If no last query time
if (lastQueryTime[ip] === undefined) {
lastQueryTime[ip] = Date.now()
return false
} else {
// Get the time between the last query and now
const timeSinceLastQuery = (Date.now() - lastQueryTime[ip])
console.log(`Time since last query for ${ip}: ${timeSinceLastQuery}`)
numLookupsPerIP[ip] += 1
console.log(ip + ' has ' + numLookupsPerIP[ip] + ' lookups.')
if (numLookupsPerIP[ip] > 200) {
blacklistedAutomatedIps.push(ip)
return true
}
// Add current request to lookup table
if (lastQueryTimes[ip] === undefined) {
lastQueryTimes[ip] = []
}
// If there's less than 40 values in the array, add the current time
if (lastQueryTimes[ip].length < 40) {
lastQueryTimes[ip].push(timeSinceLastQuery)
}
// If there's more than 40 values in the array, remove the first value and add the current time
else if (lastQueryTimes[ip].length >= 40) {
lastQueryTimes[ip].shift()
lastQueryTimes[ip].push(timeSinceLastQuery)
}
// If there's more than 20 values in the array, calculate the standard deviation between the values
if (lastQueryTimes[ip].length >= 20) {
const standardDeviation = ss.standardDeviation(lastQueryTimes[ip])
console.log(`Standard deviation for ${ip}: ${standardDeviation}`)
// If the standard deviation is less than 1000, the user is probably a bot
if (standardDeviation < 1800) {
console.log('erm haht the duence')
blacklistedAutomatedIps.push(ip)
return true
}
}
// If all of the requests have less than 2000 ms of delay:
if (lastQueryTimes[ip].length >= 20) {
if (lastQueryTimes[ip].every((request) => request < 5000)) {
blacklistedAutomatedIps.push(ip)
return true
}
}
lastQueryTime[ip] = Date.now()
return false;
}
} else {
console.log('erm haht the duence')
return true
}
return false
}
async function doAutomatedRes(res: any, json?: boolean) {
if (json) {
return res.json({
resultCount: 69420,
count: 69420,
records: [
{
id: uuidv4(),
firstName: '[[EXTREMELY LOUD INCORRECT BUZZER]]',
lastName: 'Automated scraping detected. Please contact miyakoyakota@riseup.com to be whitelisted.',
email: 'This data comes from https://search.illicit.services and it is free to use. Do not pay for access to this data.',
}
]
})
}
else {
const rendered = mustache.render(recordsListingTemplate, {
resultCount: 69420,
count: 69420,
records: [
{
id: uuidv4(),
firstName: '[[EXTREMELY LOUD INCORRECT BUZZER]]',
fields: [
"firstName: [[EXTREMELY LOUD INCORRECT BUZZER]]",
"lastName: Automated scraping detected. Please contact miyakoyakota@riseup.com to be whitelisted. If you would like to use this site as an API, you may add ?wt=json to return JSON.",
"email: This data comes from https://search.illicit.services and it is free to use. Do not pay for access to this data.",
]
}
]
})
return res.send(rendered)
}
}
app.get('/documents/by_id/:id', async (req, res) => {
if (isAutomated(req)) {
return doAutomatedRes(res, req.query.wt === 'json')
}
if (await checkIPAutomatedSTDDEV(req)) {
return doAutomatedRes(res, req.query.wt === 'json')
}
const records = await queryForDocs(`id:${req.params.id}`)
const record = records.records[0]
if (record === undefined) {
return res.status(404).send('No record found.')
}
// If wt=json
if (req.query.wt == 'json') {
// check moreLikeThis
if (req.query.moreLikeThis == 'true') {
const similarRecords = await getSimilarRecords(record).catch((err) => {
console.log(err)
return []
})
return res.json({
record: record,
related: similarRecords
})
} else {
return res.json({
record: record
})
}
}
const similarRecords = await getSimilarRecords(record).catch((err) => {
return []
})
const rendered = mustache.render(recordByIdTemplate, {
id: record.id,
record: Object.entries(_.omit(record, ['id', '_version_'])).map(([key, value]) => {
return {
key: key,
value: value
}
}),
related: similarRecords.map((record) => {
return {
id: record.id,
record: Object.entries(_.omit(record, ['id', '_version_'])).map(([key, value]) => {
return {
key: key,
value: value
}
})
}
})
})
return res.send(rendered)
})
function makeid(length: number) {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let counter = 0;
while (counter < length) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}
return result;
}
app.get('/spatial', async (req, res) => {
if (req.query.latLong != undefined && typeof req.query.latLong === 'string') {
// Validate latLong using regex
const latLongRegex = /^-?\d{1,3}(?:\.\d{1,20})?,-?\d{1,3}(?:\.\d{1,20})?$/;
// Validate d within 0.1 and 1000
if (typeof req.query.d !== 'string' && req.query.d !== undefined) {
return res.send({
error: true,
errorMessage: 'Please provide a valid d between 0.1 and 1000'
})
}
const d = parseFloat(req.query.d || '0.2')
if (d < 0.1 || d > 1000) {
return res.send({
error: true,
errorMessage: 'Please provide a valid d between 0.1 and 1000'
})
}
if (latLongRegex.test(req.query.latLong)) {
let recordsResponse = await queryForDocsSpatial(req.query.latLong, d).catch((err) => {
return {
numDocs: 0,
records: [] as SolrRecord[]
}
})
res.json(recordsResponse)
} else {
// latLong is not valid
return res.send({
error: true,
errorMessage: 'Please only send coordinates as lat,long'
})
}
} else {
res.json({
error: true,
errorMessage: "Provide a latLong! Dumbo."
})
}
})
app.get('/records', async (req, res) => {
try {
const userAgent = req.headers['user-agent']
if (!(userAgent === 'yeayeyayaeyayeayeyaeyeayyaeyeyaeyae')) {
if (isAutomated(req)) {
return doAutomatedRes(res, req.query.wt === 'json')
}
}
if (req.query.wt === 'json') {
// Check if the user-agent is in the list of allowed user-agents
const apiKey = req.query.apikey || req.headers['user-agent']
if (typeof apiKey === 'string') {
if (whitelistedUserAgents.includes(apiKey)) {
// User agent is allowed
// Continue
} else {
console.log(`IP ${req.headers['cf-connecting-ip'] || req.connection.remoteAddress} tried to access the API with user-agent ${apiKey}`)
// User agent is not allowed
res.status(403)
return res.json({
error: true,
message: 'API Access requires a free API token. Please contact miyakoyakota@riseup.com to get one.'
})
}
} else {
return res.json({ error: true, message: 'API Access requires a free API token. Please contact miyakoyakota@riseup.com to get one.' })
}
}
let requestedQuery: Record<string, any> = {}
for (const [key, value] of Object.entries(req.query)) {
if (key !== 'wt') {
if (typeof value === 'string' || Array.isArray(value)) {
requestedQuery[key] = value
} else {
console.log(`Invalid query: ${key} is not a string or array. ${value}`)
}
}
}
const queryBuilt = buildQuery(requestedQuery, res as any) as any
if (!Boolean(queryBuilt.query)) {
return res.status(400).send('no query!!!')
}
// Check if type is express.Response
if (queryBuilt === undefined) {
return res.status(400).send('no query!!!')
}
if (!queryBuilt.query || !queryBuilt.additionalQuery || typeof queryBuilt.doAdditionalQuery !== 'boolean') {
return res.status(400).send('no query!!!')
}
let totalRecordCount = 0
let recordsResponse: any = null
recordsResponse = await queryForDocs(queryBuilt.query).catch((err) => {
console.log(err)
return {
numDocs: 0,
records: [] as SolrRecord[]
}
})
totalRecordCount += recordsResponse.numDocs
let records = recordsResponse.records
if (queryBuilt.doAdditionalQuery && !req.query.exact && req.query.exact != 'true' && req.query.exact != '1' && req.query.exact != 'yes' && req.query.exact != 'y' && req.query.exact != 'on' && req.query.exact != 't' && req.query.exact != 'sure' && req.query.exact != 'please' && req.query.exact != 'True') {
const additionalQueryBuilt = sanitizeQuery(queryBuilt.additionalQuery.join(' OR '))
let additionalRecordsResponse: any = null
if (typeof req.query.sofreshandsoclean === 'string') {
additionalRecordsResponse = await queryForDocs(additionalQueryBuilt, 10000).catch((err) => {
return {
numDocs: 0,
records: [] as SolrRecord[]
}
})
} else {
additionalRecordsResponse = await queryForDocs(additionalQueryBuilt).catch((err) => {
return {
numDocs: 0,
records: [] as SolrRecord[]
}
})
}
totalRecordCount += additionalRecordsResponse.numDocs
const additionalRecords = additionalRecordsResponse.records
records.push(...additionalRecords)
}
let recordsFinal = records.map((record: any) => {
return {
...record,
canMap: Boolean(record.address) || Boolean(record.latLong),
fields: Object.keys(_.omit(record, ['id', '_version_'])).map((key) => {
return `${key}: ${(record as any)[key]}`
})
}
})
// Remove duplicates
recordsFinal = _.uniqBy(recordsFinal, 'id')
// If wt=json is set return as json
if (req.query.wt == 'json') {
return res.json({
resultCount: totalRecordCount,
count: records.length,
records: recordsFinal
})
} else {
const rendered = mustache.render(recordsListingTemplate, {
resultCount: totalRecordCount,
count: records.length,
records: recordsFinal,
finalquery: queryBuilt.query
})
return res.send(rendered)
}
} catch (e) {
res.status(500)
}
})
app.listen(port, '0.0.0.0', () => {
console.log(`Example app listening on port ${port}`)
})