2025-03-20 21:48:06 +00:00

218 lines
6.2 KiB
TypeScript

import type { Express, Request, Response } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import axios from "axios";
import { insertSpeciesSchema, insertSearchSchema, insertApiTokenSchema } from "@shared/schema";
import { ZodError } from "zod";
import { fromZodError } from "zod-validation-error";
const CITES_BASE_URL = "https://api.speciesplus.net/api/v1";
export async function registerRoutes(app: Express): Promise<Server> {
// API Token routes
app.post("/api/token", async (req: Request, res: Response) => {
try {
const tokenData = insertApiTokenSchema.parse(req.body);
const savedToken = await storage.saveApiToken(tokenData);
// Validate the token by making a test request to CITES API
try {
await axios.get(`${CITES_BASE_URL}/taxon_concepts`, {
headers: {
"X-Authentication-Token": tokenData.token
}
});
res.json({ success: true, token: savedToken });
} catch (error) {
if (axios.isAxiosError(error) && error.response) {
return res.status(400).json({
success: false,
message: "Invalid API token. Please check and try again."
});
}
throw error;
}
} catch (error) {
if (error instanceof ZodError) {
return res.status(400).json({
success: false,
message: fromZodError(error).message
});
}
res.status(500).json({
success: false,
message: "Failed to save API token"
});
}
});
app.get("/api/token", async (req: Request, res: Response) => {
try {
const token = await storage.getActiveToken();
res.json({ token: token?.token || null });
} catch (error) {
res.status(500).json({
success: false,
message: "Failed to retrieve API token"
});
}
});
// Species search routes
app.get("/api/species/search", async (req: Request, res: Response) => {
try {
const { query, format = "json" } = req.query;
if (!query) {
return res.status(400).json({
success: false,
message: "Search query is required"
});
}
// Get the API token
const tokenData = await storage.getActiveToken();
if (!tokenData) {
return res.status(401).json({
success: false,
message: "API token is not configured. Please set your CITES+ API token."
});
}
// Record the search query
await storage.addSearch({ query: query.toString() });
// Make request to CITES API
try {
const response = await axios.get(`${CITES_BASE_URL}/taxon_concepts.${format}`, {
params: { name: query },
headers: {
"X-Authentication-Token": tokenData.token
}
});
res.json({
success: true,
data: response.data
});
} catch (error) {
if (axios.isAxiosError(error) && error.response) {
return res.status(error.response.status).json({
success: false,
message: error.response.data?.message || "Error from CITES+ API",
status: error.response.status
});
}
throw error;
}
} catch (error) {
res.status(500).json({
success: false,
message: "Failed to search species"
});
}
});
app.get("/api/species/:id", async (req: Request, res: Response) => {
try {
const { id } = req.params;
const { endpoint, format = "json" } = req.query;
const tokenData = await storage.getActiveToken();
if (!tokenData) {
return res.status(401).json({
success: false,
message: "API token is not configured"
});
}
// Make request to CITES API for specific data about the taxon
try {
let apiUrl = `${CITES_BASE_URL}/taxon_concepts/${id}`;
if (endpoint) {
apiUrl += `/${endpoint}`;
}
apiUrl += `.${format}`;
const response = await axios.get(apiUrl, {
headers: {
"X-Authentication-Token": tokenData.token
}
});
res.json({
success: true,
data: response.data
});
} catch (error) {
if (axios.isAxiosError(error) && error.response) {
return res.status(error.response.status).json({
success: false,
message: error.response.data?.message || "Error from CITES+ API",
status: error.response.status
});
}
throw error;
}
} catch (error) {
res.status(500).json({
success: false,
message: "Failed to get species details"
});
}
});
// Save species to database
app.post("/api/species", async (req: Request, res: Response) => {
try {
const speciesData = insertSpeciesSchema.parse(req.body);
const savedSpecies = await storage.saveSpecies(speciesData);
res.json({ success: true, species: savedSpecies });
} catch (error) {
if (error instanceof ZodError) {
return res.status(400).json({
success: false,
message: fromZodError(error).message
});
}
res.status(500).json({
success: false,
message: "Failed to save species data"
});
}
});
// Get all saved species
app.get("/api/species", async (req: Request, res: Response) => {
try {
const allSpecies = await storage.getAllSpecies();
res.json({ success: true, species: allSpecies });
} catch (error) {
res.status(500).json({
success: false,
message: "Failed to retrieve saved species"
});
}
});
// Get recent searches
app.get("/api/searches", async (req: Request, res: Response) => {
try {
const { limit = 10 } = req.query;
const recentSearches = await storage.getRecentSearches(Number(limit));
res.json({ success: true, searches: recentSearches });
} catch (error) {
res.status(500).json({
success: false,
message: "Failed to retrieve recent searches"
});
}
});
const httpServer = createServer(app);
return httpServer;
}