import { species, type Species, type InsertSpecies, searches, type Search, type InsertSearch, apiTokens, type ApiToken, type InsertApiToken } from "@shared/schema"; export interface IStorage { // Species methods getSpecies(id: number): Promise; getSpeciesByName(scientificName: string): Promise; getAllSpecies(): Promise; saveSpecies(speciesData: InsertSpecies): Promise; // Search methods addSearch(search: InsertSearch): Promise; getRecentSearches(limit?: number): Promise; // API Token methods saveApiToken(token: InsertApiToken): Promise; getActiveToken(): Promise; updateToken(id: number, token: string, iucnToken?: string): Promise; } export class MemStorage implements IStorage { private speciesStore: Map; private searchesStore: Map; private tokensStore: Map; private speciesId: number; private searchId: number; private tokenId: number; constructor() { this.speciesStore = new Map(); this.searchesStore = new Map(); this.tokensStore = new Map(); this.speciesId = 1; this.searchId = 1; this.tokenId = 1; } // Species methods async getSpecies(id: number): Promise { return this.speciesStore.get(id); } async getSpeciesByName(scientificName: string): Promise { return Array.from(this.speciesStore.values()).find( (species) => species.scientificName.toLowerCase() === scientificName.toLowerCase() ); } async getAllSpecies(): Promise { return Array.from(this.speciesStore.values()).sort( (a, b) => new Date(b.searchedAt!).getTime() - new Date(a.searchedAt!).getTime() ); } async saveSpecies(speciesData: InsertSpecies): Promise { const existing = await this.getSpeciesByName(speciesData.scientificName); if (existing) { // Update existing species with new data and timestamp const updated: Species = { ...existing, ...speciesData, searchedAt: new Date(), }; this.speciesStore.set(existing.id, updated); return updated; } const id = this.speciesId++; const timestamp = new Date(); const species: Species = { ...(speciesData as any), // Cast to any to avoid TypeScript errors id, searchedAt: timestamp }; this.speciesStore.set(id, species); return species; } // Search methods async addSearch(searchData: InsertSearch): Promise { const id = this.searchId++; const search: Search = { ...searchData, id, timestamp: new Date() }; this.searchesStore.set(id, search); return search; } async getRecentSearches(limit: number = 10): Promise { return Array.from(this.searchesStore.values()) .sort((a, b) => { const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0; const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0; return dateB - dateA; }) .slice(0, limit); } // API Token methods async saveApiToken(tokenData: InsertApiToken): Promise { // Deactivate all existing tokens this.tokensStore.forEach((token) => { token.isActive = false; }); const id = this.tokenId++; // Ensure all required fields are present const token: ApiToken = { id, token: tokenData.token, iucnToken: tokenData.iucnToken || null, isActive: tokenData.isActive || true, createdAt: new Date() }; this.tokensStore.set(id, token); return token; } async getActiveToken(): Promise { return Array.from(this.tokensStore.values()).find(token => token.isActive); } async updateToken(id: number, token: string, iucnToken?: string): Promise { const existingToken = this.tokensStore.get(id); if (!existingToken) return undefined; const updatedToken = { ...existingToken, token, ...(iucnToken !== undefined ? { iucnToken } : {}) }; this.tokensStore.set(id, updatedToken); return updatedToken; } } export const storage = new MemStorage();