localgreenchain/lib/services/plantsnet.ts
Claude 1e14a700c7
Implement LocalGreenChain: Plant Cloning Blockchain System
This commit implements a complete blockchain-based plant tracking system
that preserves lineage across clones, seeds, and all plant offspring while
connecting growers through geographic proximity.

Features implemented:
- Custom blockchain with proof-of-work consensus
- Plant registration and cloning with lineage tracking
- Geographic discovery to find nearby plants and growers
- Integration with plants.net API for plant identification
- Comprehensive web UI for plant management
- RESTful API endpoints for all operations
- Network statistics and visualization

Core Components:
- lib/blockchain/: PlantBlock, PlantChain, and blockchain manager
- lib/services/: plants.net API and geolocation services
- pages/api/plants/: REST API endpoints for all operations
- pages/: Frontend UI pages for registration, exploration, and lineage

Technical Details:
- TypeScript for type safety
- Next.js for server-side rendering
- Tailwind CSS for responsive design
- JSON file-based blockchain storage
- Haversine distance calculations for geolocation
- OpenStreetMap integration for geocoding

This system enables large-scale adoption by:
- Making plant lineage tracking accessible to everyone
- Connecting local communities through plant sharing
- Providing immutable proof of plant provenance
- Supporting unlimited generations of plant propagation
- Scaling from individual growers to global networks

Documentation includes comprehensive README with:
- Quick start guide
- API reference
- Architecture details
- Scaling recommendations
- Use cases for various audiences
- Roadmap for future enhancements
2025-11-16 05:11:55 +00:00

225 lines
5.7 KiB
TypeScript

/**
* Plants.net API Integration Service
* Provides connectivity to the plants.net API for plant identification,
* data enrichment, and community features
*/
export interface PlantsNetSearchResult {
id: string;
commonName: string;
scientificName: string;
genus?: string;
family?: string;
imageUrl?: string;
description?: string;
careInstructions?: string;
}
export interface PlantsNetCommunity {
nearbyGrowers: {
userId: string;
username: string;
location: {
city?: string;
country?: string;
distance?: number;
};
plantsOwned: string[];
}[];
}
export class PlantsNetService {
private apiKey: string;
private baseUrl: string;
constructor(apiKey?: string) {
this.apiKey = apiKey || process.env.PLANTS_NET_API_KEY || '';
this.baseUrl = 'https://api.plants.net/v1';
}
/**
* Search for plant by common or scientific name
*/
async searchPlant(query: string): Promise<PlantsNetSearchResult[]> {
try {
const response = await fetch(
`${this.baseUrl}/search?q=${encodeURIComponent(query)}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
console.error('Plants.net API error:', response.statusText);
return [];
}
const data = await response.json();
return data.results || [];
} catch (error) {
console.error('Error searching plants.net:', error);
return [];
}
}
/**
* Get detailed plant information by ID
*/
async getPlantDetails(plantId: string): Promise<PlantsNetSearchResult | null> {
try {
const response = await fetch(`${this.baseUrl}/plants/${plantId}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
console.error('Plants.net API error:', response.statusText);
return null;
}
return await response.json();
} catch (error) {
console.error('Error fetching plant details:', error);
return null;
}
}
/**
* Find nearby growers who have similar plants
*/
async findNearbyGrowers(
plantSpecies: string,
latitude: number,
longitude: number,
radiusKm: number = 50
): Promise<PlantsNetCommunity> {
try {
const response = await fetch(
`${this.baseUrl}/community/nearby?species=${encodeURIComponent(plantSpecies)}&lat=${latitude}&lon=${longitude}&radius=${radiusKm}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
console.error('Plants.net API error:', response.statusText);
return { nearbyGrowers: [] };
}
return await response.json();
} catch (error) {
console.error('Error finding nearby growers:', error);
return { nearbyGrowers: [] };
}
}
/**
* Identify plant from image (if API supports it)
*/
async identifyPlantFromImage(imageUrl: string): Promise<PlantsNetSearchResult[]> {
try {
const response = await fetch(`${this.baseUrl}/identify`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ imageUrl }),
});
if (!response.ok) {
console.error('Plants.net API error:', response.statusText);
return [];
}
const data = await response.json();
return data.suggestions || [];
} catch (error) {
console.error('Error identifying plant:', error);
return [];
}
}
/**
* Get care instructions for a plant
*/
async getCareInstructions(scientificName: string): Promise<string | null> {
try {
const response = await fetch(
`${this.baseUrl}/care/${encodeURIComponent(scientificName)}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
console.error('Plants.net API error:', response.statusText);
return null;
}
const data = await response.json();
return data.careInstructions || null;
} catch (error) {
console.error('Error fetching care instructions:', error);
return null;
}
}
/**
* Report a plant to the plants.net network
* This allows integration with their global plant tracking
*/
async reportPlantToNetwork(plantData: {
commonName: string;
scientificName?: string;
location: { latitude: number; longitude: number };
ownerId: string;
propagationType?: string;
}): Promise<{ success: boolean; plantsNetId?: string }> {
try {
const response = await fetch(`${this.baseUrl}/reports`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(plantData),
});
if (!response.ok) {
console.error('Plants.net API error:', response.statusText);
return { success: false };
}
const data = await response.json();
return {
success: true,
plantsNetId: data.id,
};
} catch (error) {
console.error('Error reporting plant to network:', error);
return { success: false };
}
}
}
// Singleton instance
let plantsNetService: PlantsNetService | null = null;
export function getPlantsNetService(): PlantsNetService {
if (!plantsNetService) {
plantsNetService = new PlantsNetService();
}
return plantsNetService;
}