diff --git a/lib/assembleTypese.ts b/lib/assembleTypese.ts index f0067b43..27a5c5d3 100644 --- a/lib/assembleTypese.ts +++ b/lib/assembleTypese.ts @@ -1,5 +1,5 @@ // ignore ts error all file -// @ts-nocheck +//@ts-nocheck import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter"; diff --git a/pages/api/arbitrum/label.ts b/pages/api/arbitrum/label.ts index f378a30e..83ca0803 100644 --- a/pages/api/arbitrum/label.ts +++ b/pages/api/arbitrum/label.ts @@ -23,7 +23,7 @@ export default async function handler(req, res) { address = req.query.address; } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20; } else { limit = Number(req.query.limit); @@ -42,7 +42,8 @@ export default async function handler(req, res) { try { if (address === "") { - labels = await db.collection(clc_name).find().limit(limit).toArray(); + res.status(200).json({ data: [] }); + return; } else { // Adjust the MongoDB query to search by 'address' const queryAtlas = { diff --git a/pages/api/batch_label.ts b/pages/api/batch_label.ts new file mode 100644 index 00000000..34ef8683 --- /dev/null +++ b/pages/api/batch_label.ts @@ -0,0 +1,146 @@ +import { Db } from 'mongodb' +import { connectToDatabase } from "../../lib/mongodb" +import type { NextApiRequest, NextApiResponse } from 'next' + +let db: Db; +const clc_name = process.env.CLC_NAME_WLBLS; +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + + //only allows GET requests to API + if (req.method !== "GET") { + return res.status(405).json({ message: "Method not allowed" }); + } + let labels: Label[] | null = null; + try { + // asserts if addresses is empty + if (isSanitized(req)) { + // extracts the data from the query + const adr_list: string[] = extractAddress(req.query.addresses) + if (countLabels(adr_list) > 100) return res.status(403).json({ message: "Maximum address limit exceeded" }); + // validate formatting + adr_list.forEach((address) => { + if (!isAddressFormatted(address)) { + return res.status(403).json({ message: "Invalid address detected : " + address }); + } + }) + // db call + const wallets = await getWallet({ limit: countLabels(adr_list), query: adr_list, clc_name: clc_name }); + // decoupling responses + + const response = { + "data": wallets, + }; + + res.status(200).json(response); + + } else return res.status(403).json({ message: "Pass eth/sol argument" }) + } catch (error) { console.log(error); throw new Error("error") } + + + + + +} +//decoupled async search query +const getWallet = async ({ + query, + limit, + clc_name, +}: { + query: string[], + limit: number, + clc_name: string, +}) => { + try { + if (!db) await init() + const queryAtlas = { "address": { "$in": query } }; + const projection = + { + address_name: 1, + label_type: 1, + label_subtype: 1, + address: 1, + label: 1, + } + const collation = { locale: 'en', strength: 1 }; + const index = "address_1"; + const cursor = await db.collection(clc_name) + .find(queryAtlas, { projection }) + .collation(collation) + .hint(index) + .limit(limit); + const labels = await cursor.toArray(); + const result = labels.map((label) => ({ + address: label.address, + address_name: label.address_name, + label_type: label.label_type, + label_subtype: label.label_subtype, + label: label.label, + score: label.score, + })); + return { batch_result: result } + } catch (error) { + return { error } + } +}; + +//connect to db +async function init() { + try { + const database = await connectToDatabase(); + db = database.db; + + } catch (error) { + console.log(error); + throw new Error("Unable to connect to database"); + } + return db; +}; + +//Type check on req (no empty address) +function isSanitized(res: NextApiRequest): Boolean { + try { + return typeof (res.query.addresses) === "string" && res.query.addresses != "" + } catch (error) { + console.log(error); + throw new Error("Unable to assess query blockchain type") + } +}; + + +//return a list from an array of addresses in string format +function extractAddress(addressArray: string | string[]): string[] { + + // Remove square brackets and split the string into an array + const adr_list = String(addressArray).split(','); + return adr_list; + +} + +//detects if addresses are valid +function isAddressFormatted(adr: string): boolean { + + return (adr.length === 42 && adr.startsWith("0x")); + +}; + +//label counter +function countLabels(adr_list: string[]): number { + return adr_list.length; +} + +//response interface ? +type ResponseData = { + [key: string]: any; + +} +type Label = { + address: string, + address_name: string, + label_type: string, + label_subtype: string, + label: string, +} \ No newline at end of file diff --git a/pages/api/contractlabel.ts b/pages/api/contractlabel.ts index 293b7a9f..1cf142fb 100644 --- a/pages/api/contractlabel.ts +++ b/pages/api/contractlabel.ts @@ -26,7 +26,7 @@ export default async function handler(req, res) { address = req.query.address; } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20; } else { limit = Number(req.query.limit); @@ -45,7 +45,8 @@ export default async function handler(req, res) { try { if (address === "") { - labels = await db.collection(clc_name).find().limit(limit).toArray(); + res.status(200).json({ data: [] }); + return; } else { // Adjust the MongoDB query to search by 'address' const queryAtlas = { diff --git a/pages/api/label.ts b/pages/api/label.ts index 67161304..fa5aa818 100644 --- a/pages/api/label.ts +++ b/pages/api/label.ts @@ -26,7 +26,7 @@ export default async function handler(req, res) { address = req.query.address; } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20; } else { limit = Number(req.query.limit); @@ -45,7 +45,8 @@ export default async function handler(req, res) { try { if (address === "") { - labels = await db.collection(clc_name).find().limit(limit).toArray(); + res.status(200).json({ data: [] }); + return; } else { // Adjust the MongoDB query to search by 'address' const queryAtlas = { diff --git a/pages/api/optimism/label.ts b/pages/api/optimism/label.ts index 8a410a7e..a2ddfa0d 100644 --- a/pages/api/optimism/label.ts +++ b/pages/api/optimism/label.ts @@ -24,7 +24,7 @@ export default async function handler(req, res) { address = req.query.address; } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20; } else { limit = Number(req.query.limit); @@ -43,7 +43,8 @@ export default async function handler(req, res) { try { if (address === "") { - labels = await db.collection(clc_name).find().limit(limit).toArray(); + res.status(200).json({ data: [] }); + return; } else { // Adjust the MongoDB query to search by 'address' const queryAtlas = { diff --git a/pages/api/query.ts b/pages/api/query.ts index 13e97e52..3129ec2b 100644 --- a/pages/api/query.ts +++ b/pages/api/query.ts @@ -61,7 +61,7 @@ export default async function handler(req, res) { query = req.query.query } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20 } else { limit = Number(req.query.limit) diff --git a/pages/api/query_q.ts b/pages/api/query_q.ts index ee9b3e62..b01b5df8 100644 --- a/pages/api/query_q.ts +++ b/pages/api/query_q.ts @@ -30,7 +30,7 @@ export default async function handler(req, res) { query = req.query.query } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20 } else { limit = Number(req.query.limit) diff --git a/pages/api/query_socials.ts b/pages/api/query_socials.ts index ff5e0a82..da3932a8 100644 --- a/pages/api/query_socials.ts +++ b/pages/api/query_socials.ts @@ -55,7 +55,7 @@ export default async function handler( query = req.query.query as string } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 40 } else { limit = Number(req.query.limit) diff --git a/pages/api/query_socials_a.ts b/pages/api/query_socials_a.ts index 09073738..bd8769e2 100644 --- a/pages/api/query_socials_a.ts +++ b/pages/api/query_socials_a.ts @@ -55,7 +55,7 @@ export default async function handler( query = req.query.query as string } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 40 } else { limit = Number(req.query.limit) diff --git a/pages/api/sc_names.ts b/pages/api/sc_names.ts index c6002851..d87b48f0 100644 --- a/pages/api/sc_names.ts +++ b/pages/api/sc_names.ts @@ -60,7 +60,7 @@ export default async function handler(req, res) { query = req.query.query } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20 } else { limit = Number(req.query.limit) diff --git a/pages/api/sc_names_sc.ts b/pages/api/sc_names_sc.ts index c9b65a1d..44c99f43 100644 --- a/pages/api/sc_names_sc.ts +++ b/pages/api/sc_names_sc.ts @@ -30,7 +30,7 @@ export default async function handler(req, res) { query = req.query.query } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20 } else { limit = Number(req.query.limit) diff --git a/pages/api/search.ts b/pages/api/search.ts index dc007b94..66e79bd1 100644 --- a/pages/api/search.ts +++ b/pages/api/search.ts @@ -28,15 +28,15 @@ export default async function handler(req, res) { } - if (req.query.limit === "" || req.query.limit === undefined) { + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { limit = 20 } else { limit = Number(req.query.limit) } // max limit is 100 - if (limit > 1000) { - limit = 1000 + if (limit > 100) { + limit = 100 } // limit to only GET method or throw error diff --git a/pages/api/solana/batch_label.ts b/pages/api/solana/batch_label.ts new file mode 100644 index 00000000..d6f7eee4 --- /dev/null +++ b/pages/api/solana/batch_label.ts @@ -0,0 +1,146 @@ +import { Db } from 'mongodb' +import { connectToDatabase } from "../../../lib/mongodb" +import type { NextApiRequest, NextApiResponse } from 'next' + +let db: Db; +const clc_name = process.env.CLC_NAME_WLBLS_SOLANA; +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + + //only allows GET requests to API + if (req.method !== "GET") { + return res.status(405).json({ message: "Method not allowed" }); + } + let labels: Label[] | null = null; + try { + + // asserts if addresses is empty + if (isSanitized(req)) { + // extracts the data from the query + const adr_list: string[] = extractAddress(req.query.addresses); + if (countLabels(adr_list) > 100) return res.status(403).json({ message: "Maximum address limit exceeded" }); + // validate formatting + adr_list.forEach((address) => { + if (!isAddressFormatted(address)) { + return res.status(403).json({ message: "Invalid address detected : " + address }); + } + }) + // db call + const wallets = await getWallet({ limit: countLabels(adr_list), query: adr_list, clc_name: clc_name }); + // decoupling responses + + const response = { + "data": wallets, + }; + + res.status(200).json(response); + + } else return res.status(403).json({ message: "Pass addresses argument" }) + } catch (error) { console.log(error); throw new Error("error") } + + + + + +} +//decoupled async search query +const getWallet = async ({ + query, + limit, + clc_name, +}: { + query: string[], + limit: number, + clc_name: string, +}) => { + try { + if (!db) await init() + const queryAtlas = { "ADDRESS": { "$in": query } }; + const projection = + { + ADDRESS_NAME: 1, + LABEL_TYPE: 1, + LABEL_SUBTYPE: 1, + ADDRESS: 1, + LABEL: 1, + }; + const collation = { locale: 'en', strength: 2, maxVariable: "punct" } + const index = "address_1"; + const cursor = await db.collection(clc_name) + .find(queryAtlas, { projection }) + .collation(collation) + .hint(index) + .limit(limit); + const labels = await cursor.toArray(); + const result = labels.map((label) => ({ + address: label.ADDRESS, + address_name: label.ADDRESS_NAME, + label_type: label.LABEL_TYPE, + label_subtype: label.LABEL_SUBTYPE, + label: label.LABEL, + })); + return { batch_result: result } + } catch (error) { + return { error } + } +}; + +//connect to db +async function init() { + try { + const database = await connectToDatabase(); + db = database.db; + + } catch (error) { + console.log(error); + throw new Error("Unable to connect to database"); + } + return db; +}; + +//Type check on req (no empty address) +function isSanitized(res: NextApiRequest): Boolean { + try { + return typeof (res.query.addresses) === "string" && res.query.addresses != "" + } catch (error) { + console.log(error); + throw new Error("addresses parameter required") + } +}; + + +//return a list from an array of addresses in string format +function extractAddress(addressArray: string | string[]): string[] { + + // Remove square brackets and split the string into an array + const adr_list = String(addressArray).split(','); + return adr_list; + +} + +//detects if addresses are valid +function isAddressFormatted(adr: string): boolean { + + return (adr.length <= 44 || adr.length >= 32); + +}; + +//label counter +function countLabels(adr_list: string[]): number { + return adr_list.length; +} + +//response interface ? +type ResponseData = { + [key: string]: any; + +} +type Label = { + address: string, + address_name: string, + label_type: string, + label_subtype: string, + label: string, +} \ No newline at end of file diff --git a/pages/api/solana/label.ts b/pages/api/solana/label.ts new file mode 100644 index 00000000..972b4d97 --- /dev/null +++ b/pages/api/solana/label.ts @@ -0,0 +1,88 @@ +import middlewares, { middlewares_special } from "@/lib/rateLimits" +import { connectToDatabase } from "../../../lib/mongodb" + +export default async function handler(req, res) { + // Database connection setup + let db; + let address, limit; + try { + const database = await connectToDatabase(); + db = database.db; + } catch (error) { + console.log(error); + throw new Error("Unable to connect to database"); + } + + // Validate 'address' query parameter + if (req.query.address === undefined && req.query.label === undefined) { + return res.status(400).json({ message: "Bad request: 'address' parameter missing" }); + } + + if (req.query.address === "" || req.query.address === undefined) { + address = ""; + } else { + address = req.query.address; + } + + if (req.query.limit === "" || req.query.limit === undefined || Number.isNaN(req.query.limit)) { + limit = 20; + } else { + limit = Number(req.query.limit); + } + + if (limit > 100) { + limit = 100; + } + + if (req.method !== "GET") { + return res.status(405).json({ message: "Method not allowed" }); + } + + const clc_name = process.env.CLC_NAME_WLBLS_SOLANA; + let labels = null; + + try { + if (address === "") { + res.status(200).json({ data: [] }); + return; + } else if (!!address) { + // Adjust the MongoDB query to search by 'address' + const queryAtlas = { + ADDRESS: address, + }; + + const projection = { + ADDRESS_NAME: 1, + LABEL_TYPE: 1, + LABEL_SUBTYPE: 1, + ADDRESS: 1, + LABEL: 1, + }; + + const cursor = await db.collection(clc_name).find(queryAtlas, { projection },).collation( + { locale: 'en', strength: 2, maxVariable: "punct" } + ).hint("ADDRESS_1") + .limit(limit); + labels = await cursor.toArray(); + } + + labels = labels.map((lbl) => ({ + ADDRESS: lbl.ADDRESS, + ADDRESS_NAME: lbl.ADDRESS_NAME, + LABEL_TYPE: lbl.LABEL_TYPE, + LABEL_SUBTYPE: lbl.LABEL_SUBTYPE, + LABEL: lbl.LABEL, + })); + + } catch (error) { + console.log(error); + res.status(500).json({ message: "Internal server error" }); + return; + } + + const response = { + data: labels, + }; + + res.status(200).json(response); +} diff --git a/pages/api/solana/search.ts b/pages/api/solana/search.ts new file mode 100644 index 00000000..fa6c10e2 --- /dev/null +++ b/pages/api/solana/search.ts @@ -0,0 +1,132 @@ +import { Db } from 'mongodb' +import { connectToDatabase } from "../../../lib/mongodb" +import type { NextApiRequest, NextApiResponse } from 'next' + +let db: Db; +const clc_name = process.env.CLC_NAME_WLBLS_SOLANA; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + //only allows GET requests to API + if (req.method !== "GET") { + return res.status(405).json({ message: "Method not allowed" }); + } + let labels: Label[] | null = null; + const skip = + typeof req.query.skip === 'string' ? Number(req.query.skip) : 0; + const limit = + typeof req.query.limit === 'string' && !Number.isNaN(req.query.limit) ? Math.max(Math.min(Number(req.query.limit), 100), 1) : 20; + + const searchtext = + typeof req.query.searchtext === 'string' ? req.query.searchtext : undefined; + + const { solana_wallets } = await getSolana({ skip, limit, query: searchtext }); + console.log("Searched element : " + searchtext); + try { + labels = solana_wallets.map((lbl) => ({ + address: lbl.ADDRESS, + address_name: lbl.ADDRESS_NAME, + label_type: lbl.LABEL_TYPE, + label_subtype: lbl.LABEL_SUBTYPE, + label: lbl.LABEL, + })); + const response = { + data: labels, + }; + + res.status(200).json(response); + } catch (error) { console.log(error); res.status(500); throw new Error("Bad request: 'searchtext' parameter missing"); }; + + + + + + + +} +//decoupled async search query +const getSolana = async ({ + query, + skip, + limit +}: { + query?: string + skip: number + limit: number +}) => { + try { + if (!db) await init() + + const pipeline: PipelineStage[] = [{ $skip: skip }, { $limit: limit }] + if (query) { + pipeline.unshift({ + $search: { + index: 'dynamic', + text: { + query, + fuzzy: { + maxEdits: 1, + prefixLength: 3, + maxExpansions: 50 + }, + path: { + wildcard: '*' + } + } + } + }) + } + const result = await db.collection(clc_name).aggregate(pipeline).toArray() + return { solana_wallets: result } + } catch (error) { + return { error } + } +} +//connect to db +async function init() { + try { + const database = await connectToDatabase(); + db = database.db; + + } catch (error) { + console.log(error); + throw new Error("Unable to connect to database"); + } + return db; +} + + +//mongosh query interface +type PipelineStage = + | { + $search: { + index: string + text: { + query: string + fuzzy: {} + path: { + wildcard: string + } + } + } + } + | { + $skip: number + } + | { + $limit: number + } +//response interface ? +type ResponseData = { + [key: string]: string | Label[]; + +} +type Label = { + address: string, + address_name: string, + label_type: string, + label_subtype: string, + label: string, +} \ No newline at end of file