Agent 2 - Database Integration (P0 Critical): - Add Prisma ORM with PostgreSQL for persistent data storage - Create comprehensive database schema with 20+ models: - User & authentication models - Plant & lineage tracking - Transport events & supply chain - Vertical farming (farms, zones, batches, recipes) - Demand & market matching - Audit logging & blockchain storage - Implement complete database service layer (lib/db/): - users.ts: User CRUD with search and stats - plants.ts: Plant operations with lineage tracking - transport.ts: Transport events and carbon tracking - farms.ts: Vertical farm and crop batch management - demand.ts: Consumer preferences and market matching - audit.ts: Audit logging and blockchain integrity - Add PlantChainDB for database-backed blockchain - Create development seed script with sample data - Add database documentation (docs/DATABASE.md) - Update package.json with Prisma dependencies and scripts This provides the foundation for all other agents to build upon with persistent, scalable data storage.
132 lines
3.4 KiB
TypeScript
132 lines
3.4 KiB
TypeScript
/**
|
|
* Blockchain Manager
|
|
* Singleton to manage the global plant blockchain instance
|
|
*
|
|
* Supports two modes:
|
|
* 1. File-based (legacy): Uses JSON file storage
|
|
* 2. Database-backed: Uses PostgreSQL via Prisma (recommended)
|
|
*/
|
|
|
|
import { PlantChain } from './PlantChain';
|
|
import { PlantChainDB, getPlantChain as getDBPlantChain } from './PlantChainDB';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
const BLOCKCHAIN_FILE = path.join(process.cwd(), 'data', 'plantchain.json');
|
|
|
|
// Flag to determine storage mode
|
|
const USE_DATABASE = process.env.DATABASE_URL ? true : false;
|
|
|
|
class BlockchainManager {
|
|
private static instance: BlockchainManager;
|
|
private plantChain: PlantChain;
|
|
private autoSaveInterval: NodeJS.Timeout | null = null;
|
|
|
|
private constructor() {
|
|
this.plantChain = this.loadBlockchain();
|
|
this.startAutoSave();
|
|
}
|
|
|
|
public static getInstance(): BlockchainManager {
|
|
if (!BlockchainManager.instance) {
|
|
BlockchainManager.instance = new BlockchainManager();
|
|
}
|
|
return BlockchainManager.instance;
|
|
}
|
|
|
|
public getChain(): PlantChain {
|
|
return this.plantChain;
|
|
}
|
|
|
|
/**
|
|
* Load blockchain from file or create new one
|
|
*/
|
|
private loadBlockchain(): PlantChain {
|
|
try {
|
|
// Ensure data directory exists
|
|
const dataDir = path.join(process.cwd(), 'data');
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
}
|
|
|
|
if (fs.existsSync(BLOCKCHAIN_FILE)) {
|
|
const data = fs.readFileSync(BLOCKCHAIN_FILE, 'utf-8');
|
|
const chainData = JSON.parse(data);
|
|
console.log('✓ Loaded existing blockchain with', chainData.chain.length, 'blocks');
|
|
return PlantChain.fromJSON(chainData);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading blockchain:', error);
|
|
}
|
|
|
|
console.log('✓ Created new blockchain');
|
|
return new PlantChain(4); // difficulty of 4
|
|
}
|
|
|
|
/**
|
|
* Save blockchain to file
|
|
*/
|
|
public saveBlockchain(): void {
|
|
try {
|
|
const dataDir = path.join(process.cwd(), 'data');
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
}
|
|
|
|
const data = JSON.stringify(this.plantChain.toJSON(), null, 2);
|
|
fs.writeFileSync(BLOCKCHAIN_FILE, data, 'utf-8');
|
|
console.log('✓ Blockchain saved');
|
|
} catch (error) {
|
|
console.error('Error saving blockchain:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Auto-save blockchain every 5 minutes
|
|
*/
|
|
private startAutoSave(): void {
|
|
if (this.autoSaveInterval) {
|
|
clearInterval(this.autoSaveInterval);
|
|
}
|
|
|
|
this.autoSaveInterval = setInterval(() => {
|
|
this.saveBlockchain();
|
|
}, 5 * 60 * 1000); // 5 minutes
|
|
}
|
|
|
|
/**
|
|
* Stop auto-save
|
|
*/
|
|
public stopAutoSave(): void {
|
|
if (this.autoSaveInterval) {
|
|
clearInterval(this.autoSaveInterval);
|
|
this.autoSaveInterval = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getBlockchain(): PlantChain {
|
|
return BlockchainManager.getInstance().getChain();
|
|
}
|
|
|
|
export function saveBlockchain(): void {
|
|
BlockchainManager.getInstance().saveBlockchain();
|
|
}
|
|
|
|
/**
|
|
* Get the database-backed blockchain instance
|
|
* Use this for production applications with PostgreSQL
|
|
*/
|
|
export async function getBlockchainDB(): Promise<PlantChainDB> {
|
|
return getDBPlantChain();
|
|
}
|
|
|
|
/**
|
|
* Check if using database storage
|
|
*/
|
|
export function isUsingDatabase(): boolean {
|
|
return USE_DATABASE;
|
|
}
|
|
|
|
// Export the DB class for type usage
|
|
export { PlantChainDB };
|