diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index 97f8a87..ad5d384 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -32,6 +32,7 @@ export interface ApiResponse { export interface TokenResponse { token: string | null; + iucnToken: string | null; } export interface ApiStatusResponse { diff --git a/server/routes.ts b/server/routes.ts index 7777006..9a0fbd6 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -307,7 +307,7 @@ export async function registerRoutes(app: Express): Promise { apiVersion: "v4", message: "IUCN API v4 is connected and responding" }); - } catch (error) { + } catch (error: any) { // If V4 fails, fall back to checking V3 with environment API key console.log("IUCN V4 API check failed, falling back to V3:", error.message); } @@ -371,6 +371,39 @@ export async function registerRoutes(app: Express): Promise { }); } + // To avoid 414 errors, we'll limit the name parameter to just the genus and species + // This will extract first two parts of scientific name (genus and species) + const nameParts = String(name).split(' '); + const simplifiedName = nameParts.slice(0, 2).join(' '); + const [genusName, speciesName] = nameParts; + + // Try with v4 API first if we have a token + const activeToken = await storage.getActiveToken(); + if (activeToken?.iucnToken) { + try { + // Use the v4 API with scientific name endpoint + const response = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, { + headers: { + "Authorization": `Bearer ${activeToken.iucnToken}` + }, + params: { + genus_name: genusName, + species_name: speciesName || "" + } + }); + + return res.json({ + success: true, + data: response.data, + apiVersion: "v4" + }); + } catch (error: any) { + console.log("IUCN V4 API species lookup failed, falling back to V3:", error.message); + // Continue to v3 fallback + } + } + + // Fallback to v3 API const apiKey = process.env.IUCN_API_KEY; if (!apiKey) { return res.status(401).json({ @@ -380,11 +413,6 @@ export async function registerRoutes(app: Express): Promise { }); } - // To avoid 414 errors, we'll limit the name parameter to just the genus and species - // This will extract first two parts of scientific name (genus and species) - const nameParts = String(name).split(' '); - const simplifiedName = nameParts.slice(0, 2).join(' '); - try { const response = await axios.get(`${IUCN_V3_BASE_URL}/species/name/${encodeURIComponent(simplifiedName)}`, { params: { token: apiKey } @@ -392,7 +420,8 @@ export async function registerRoutes(app: Express): Promise { res.json({ success: true, - data: response.data + data: response.data, + apiVersion: "v3" }); } catch (error) { if (axios.isAxiosError(error) && error.response) { @@ -423,6 +452,52 @@ export async function registerRoutes(app: Express): Promise { }); } + // To avoid 414 errors, we'll limit the name parameter to just the genus and species + const nameParts = String(name).split(' '); + const simplifiedName = nameParts.slice(0, 2).join(' '); + const [genusName, speciesName] = nameParts; + + // Try with v4 API first if we have a token + const activeToken = await storage.getActiveToken(); + if (activeToken?.iucnToken) { + try { + // First, we need to find the species taxon ID using scientific name lookup + const taxaResponse = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, { + headers: { + "Authorization": `Bearer ${activeToken.iucnToken}` + }, + params: { + genus_name: genusName, + species_name: speciesName || "" + } + }); + + // Check if we found the species + if (taxaResponse.data && taxaResponse.data.result && taxaResponse.data.result.length > 0) { + const taxonId = taxaResponse.data.result[0].taxonid; + + // Now retrieve the threats using the taxon ID + const threatsResponse = await axios.get(`${IUCN_V4_BASE_URL}/threats/species/id/${taxonId}`, { + headers: { + "Authorization": `Bearer ${activeToken.iucnToken}` + } + }); + + return res.json({ + success: true, + data: threatsResponse.data, + apiVersion: "v4" + }); + } + // If no species found, fall back to v3 + console.log("IUCN V4 API: No species found with the given scientific name, falling back to V3"); + } catch (error: any) { + console.log("IUCN V4 API threats lookup failed, falling back to V3:", error.message); + // Continue to v3 fallback + } + } + + // Fallback to v3 API const apiKey = process.env.IUCN_API_KEY; if (!apiKey) { return res.status(401).json({ @@ -431,10 +506,6 @@ export async function registerRoutes(app: Express): Promise { message: "IUCN API key is not configured" }); } - - // To avoid 414 errors, we'll limit the name parameter to just the genus and species - const nameParts = String(name).split(' '); - const simplifiedName = nameParts.slice(0, 2).join(' '); try { const response = await axios.get(`${IUCN_V3_BASE_URL}/threats/species/name/${encodeURIComponent(simplifiedName)}`, { @@ -443,7 +514,8 @@ export async function registerRoutes(app: Express): Promise { res.json({ success: true, - data: response.data + data: response.data, + apiVersion: "v3" }); } catch (error) { if (axios.isAxiosError(error) && error.response) { @@ -474,6 +546,52 @@ export async function registerRoutes(app: Express): Promise { }); } + // To avoid 414 errors, we'll limit the name parameter to just the genus and species + const nameParts = String(name).split(' '); + const simplifiedName = nameParts.slice(0, 2).join(' '); + const [genusName, speciesName] = nameParts; + + // Try with v4 API first if we have a token + const activeToken = await storage.getActiveToken(); + if (activeToken?.iucnToken) { + try { + // First, we need to find the species taxon ID using scientific name lookup + const taxaResponse = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, { + headers: { + "Authorization": `Bearer ${activeToken.iucnToken}` + }, + params: { + genus_name: genusName, + species_name: speciesName || "" + } + }); + + // Check if we found the species + if (taxaResponse.data && taxaResponse.data.result && taxaResponse.data.result.length > 0) { + const taxonId = taxaResponse.data.result[0].taxonid; + + // Now retrieve the habitats using the taxon ID + const habitatsResponse = await axios.get(`${IUCN_V4_BASE_URL}/habitats/species/id/${taxonId}`, { + headers: { + "Authorization": `Bearer ${activeToken.iucnToken}` + } + }); + + return res.json({ + success: true, + data: habitatsResponse.data, + apiVersion: "v4" + }); + } + // If no species found, fall back to v3 + console.log("IUCN V4 API: No species found with the given scientific name, falling back to V3"); + } catch (error: any) { + console.log("IUCN V4 API habitats lookup failed, falling back to V3:", error.message); + // Continue to v3 fallback + } + } + + // Fallback to v3 API const apiKey = process.env.IUCN_API_KEY; if (!apiKey) { return res.status(401).json({ @@ -482,10 +600,6 @@ export async function registerRoutes(app: Express): Promise { message: "IUCN API key is not configured" }); } - - // To avoid 414 errors, we'll limit the name parameter to just the genus and species - const nameParts = String(name).split(' '); - const simplifiedName = nameParts.slice(0, 2).join(' '); try { const response = await axios.get(`${IUCN_V3_BASE_URL}/habitats/species/name/${encodeURIComponent(simplifiedName)}`, { @@ -494,7 +608,8 @@ export async function registerRoutes(app: Express): Promise { res.json({ success: true, - data: response.data + data: response.data, + apiVersion: "v3" }); } catch (error) { if (axios.isAxiosError(error) && error.response) { @@ -525,6 +640,52 @@ export async function registerRoutes(app: Express): Promise { }); } + // To avoid 414 errors, we'll limit the name parameter to just the genus and species + const nameParts = String(name).split(' '); + const simplifiedName = nameParts.slice(0, 2).join(' '); + const [genusName, speciesName] = nameParts; + + // Try with v4 API first if we have a token + const activeToken = await storage.getActiveToken(); + if (activeToken?.iucnToken) { + try { + // First, we need to find the species taxon ID using scientific name lookup + const taxaResponse = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, { + headers: { + "Authorization": `Bearer ${activeToken.iucnToken}` + }, + params: { + genus_name: genusName, + species_name: speciesName || "" + } + }); + + // Check if we found the species + if (taxaResponse.data && taxaResponse.data.result && taxaResponse.data.result.length > 0) { + const taxonId = taxaResponse.data.result[0].taxonid; + + // Now retrieve the conservation measures using the taxon ID + const measuresResponse = await axios.get(`${IUCN_V4_BASE_URL}/measures/species/id/${taxonId}`, { + headers: { + "Authorization": `Bearer ${activeToken.iucnToken}` + } + }); + + return res.json({ + success: true, + data: measuresResponse.data, + apiVersion: "v4" + }); + } + // If no species found, fall back to v3 + console.log("IUCN V4 API: No species found with the given scientific name, falling back to V3"); + } catch (error: any) { + console.log("IUCN V4 API conservation measures lookup failed, falling back to V3:", error.message); + // Continue to v3 fallback + } + } + + // Fallback to v3 API const apiKey = process.env.IUCN_API_KEY; if (!apiKey) { return res.status(401).json({ @@ -533,10 +694,6 @@ export async function registerRoutes(app: Express): Promise { message: "IUCN API key is not configured" }); } - - // To avoid 414 errors, we'll limit the name parameter to just the genus and species - const nameParts = String(name).split(' '); - const simplifiedName = nameParts.slice(0, 2).join(' '); try { const response = await axios.get(`${IUCN_V3_BASE_URL}/measures/species/name/${encodeURIComponent(simplifiedName)}`, { @@ -545,7 +702,8 @@ export async function registerRoutes(app: Express): Promise { res.json({ success: true, - data: response.data + data: response.data, + apiVersion: "v3" }); } catch (error) { if (axios.isAxiosError(error) && error.response) {