From 995bec5910d90c68f743520f68be127870b3eebe Mon Sep 17 00:00:00 2001 From: Stephanie Nwankwo Date: Fri, 22 Nov 2024 13:28:47 +0100 Subject: [PATCH] feat: server setup --- app/server/routes/landRoutes.js | 26 +++-- app/server/src/controllers/landController.js | 70 +++++++++++++ app/server/src/services/blockchainService.js | 100 ++++++++++++++++++- 3 files changed, 181 insertions(+), 15 deletions(-) diff --git a/app/server/routes/landRoutes.js b/app/server/routes/landRoutes.js index 471c1151..b0338ba7 100644 --- a/app/server/routes/landRoutes.js +++ b/app/server/routes/landRoutes.js @@ -1,17 +1,15 @@ -// const express = require('express'); -// const router = express.Router(); -// const landController = require('../controllers/landController'); +const express = require('express'); +const router = express.Router(); +const landController = require('../controllers/landController'); -// // Remove any authentication middleware for these routes -// router.post('/register', landController.registerLand); -// router.get('/', (req, res, next) => { -// console.log('Received GET request for all lands'); -// landController.getAllLands(req, res, next); -// }); -// router.get('/:id', landController.getLandById); -// router.get('/:id/verify', landController.verifyLand); +// Base land routes +router.get('/', landController.getAllLands); +router.get('/inspectors', landController.getInspectors); +router.get('/approved', landController.getApprovedLands); +router.get('/transfer-history/:landId', landController.getTransferHistory); -// //Todo: docs, api +// Land verification and details +router.get('/:id', landController.getLandById); +router.get('/:id/verify', landController.verifyLandStatus); - -// module.exports = router; +module.exports = router; diff --git a/app/server/src/controllers/landController.js b/app/server/src/controllers/landController.js index c7c2b4b5..63dc814e 100644 --- a/app/server/src/controllers/landController.js +++ b/app/server/src/controllers/landController.js @@ -141,3 +141,73 @@ exports.verifyLand = async (req, res, next) => { next(new CustomError('Error verifying land', 500)); } }; + +exports.getInspectors = async (req, res, next) => { + try { + console.log('Fetching inspectors from blockchain'); + const inspectors = await blockchainService.getInspectors(); + res.status(200).json(inspectors); + } catch (error) { + console.error('Error fetching inspectors:', error); + next(new CustomError('Error fetching inspectors', 500)); + } +}; + +exports.getApprovedLands = async (req, res, next) => { + try { + console.log('Fetching approved lands'); + const approvedLands = await blockchainService.getApprovedLands(); + + // Enrich blockchain data with database information + const enrichedLands = await Promise.all(approvedLands.map(async (land) => { + const dbLand = await Land.findOne({ landId: land.id }); + return { ...land, ...dbLand?.toObject() }; + })); + + res.status(200).json(enrichedLands); + } catch (error) { + console.error('Error fetching approved lands:', error); + next(new CustomError('Error fetching approved lands', 500)); + } +}; + +exports.getTransferHistory = async (req, res, next) => { + try { + const { landId } = req.params; + console.log('Fetching transfer history for land:', landId); + + if (!landId) { + throw new CustomError('Land ID is required', 400, 'LAND_ID_REQUIRED'); + } + + const transferHistory = await blockchainService.getLandTransferHistory(landId); + res.status(200).json(transferHistory); + } catch (error) { + console.error('Error fetching transfer history:', error); + next(new CustomError('Error fetching transfer history', 500)); + } +}; + +exports.verifyLandStatus = async (req, res, next) => { + try { + const { id } = req.params; + console.log('Verifying land status for ID:', id); + + if (!id) { + throw new CustomError('Land ID is required', 400, 'LAND_ID_REQUIRED'); + } + + const verificationResult = await blockchainService.verifyLandStatus(id); + + res.status(200).json({ + isVerified: verificationResult.isVerified, + message: verificationResult.isVerified + ? 'Land is verified in the blockchain registry' + : 'Land is not verified in the blockchain registry', + details: verificationResult.details + }); + } catch (error) { + console.error('Error verifying land status:', error); + next(new CustomError('Error verifying land status', 500)); + } +}; diff --git a/app/server/src/services/blockchainService.js b/app/server/src/services/blockchainService.js index 5a51cb79..d8240c24 100644 --- a/app/server/src/services/blockchainService.js +++ b/app/server/src/services/blockchainService.js @@ -3,6 +3,7 @@ const LandRegistryABI = require('../abis/LandRegistry.json'); const dotenv = require('dotenv'); const crypto = require('crypto'); + dotenv.config(); const provider = new ethers.JsonRpcProvider(process.env.ETHEREUM_RPC_URL); @@ -99,8 +100,105 @@ async function verifyLand(landId) { } } +const getInspectors = async () => { + try { + console.log('Fetching inspectors from blockchain...'); + // Call the smart contract method to get inspectors + const inspectors = await contract.getInspectors(); + + // Transform the raw data into a more useful format + return inspectors.map(inspector => ({ + address: inspector.addr, + name: inspector.name, + isActive: inspector.isActive, + registrationDate: new Date(inspector.registrationDate * 1000).toISOString(), + totalLandsInspected: inspector.landsInspected.toString() + })); + } catch (error) { + console.error('Error fetching inspectors:', error); + throw new Error('Failed to fetch inspectors from blockchain'); + } +}; + +const getApprovedLands = async () => { + try { + console.log('Fetching approved lands from blockchain...'); + // Get all approved lands from the contract + const approvedLands = await contract.getApprovedLands(); + + // Transform the raw blockchain data + return approvedLands.map(land => ({ + id: land.id.toString(), + owner: land.owner, + location: land.location, + area: ethers.utils.formatUnits(land.area, 'ether'), // If area is stored in wei + landUse: land.landUse, + approvalDate: new Date(land.approvalDate * 1000).toISOString(), + inspectorAddress: land.inspector, + status: land.status + })); + } catch (error) { + console.error('Error fetching approved lands:', error); + throw new Error('Failed to fetch approved lands from blockchain'); + } +}; + +const getLandTransferHistory = async (landId) => { + try { + console.log('Fetching transfer history for landId:', landId); + // Get transfer events for the specific land + const filter = contract.filters.LandTransferred(landId); + const events = await contract.queryFilter(filter, 0, 'latest'); + + // Transform the events into a readable format + return events.map(event => ({ + transactionHash: event.transactionHash, + blockNumber: event.blockNumber, + timestamp: new Date(event.args.timestamp * 1000).toISOString(), + previousOwner: event.args.from, + newOwner: event.args.to, + landId: event.args.landId.toString(), + transferType: event.args.transferType + })); + } catch (error) { + console.error('Error fetching transfer history:', error); + throw new Error('Failed to fetch land transfer history'); + } +}; + +const verifyLandStatus = async (landId) => { + try { + console.log('Verifying land status on blockchain...'); + // Get land details from the contract + const landDetails = await contract.getLand(landId); + + // Verify the land's status + const isVerified = await contract.isLandVerified(landId); + + return { + isVerified, + details: { + owner: landDetails.owner, + status: landDetails.status, + verificationDate: landDetails.verificationDate + ? new Date(landDetails.verificationDate * 1000).toISOString() + : null, + inspector: landDetails.inspector, + lastUpdated: new Date(landDetails.lastUpdated * 1000).toISOString() + } + }; + } catch (error) { + console.error('Error verifying land status:', error); + throw new Error('Failed to verify land status'); + } +}; + module.exports = { registerLand, getLandDetails, - verifyLand + verifyLand, + getInspectors, + getApprovedLands, + getLandTransferHistory, + verifyLandStatus }; \ No newline at end of file