-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #215 from NoshonNetworks/backend_issue
Update landver backend
- Loading branch information
Showing
19 changed files
with
568 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
PORT=3000 | ||
DATABASE_URL= | ||
NODE_ENV=development | ||
CORS_ORIGINS= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "land-registry-backend", | ||
"version": "1.0.0", | ||
"description": "Backend service for Land Registry dApp", | ||
"main": "dist/index.js", | ||
"scripts": { | ||
"build": "tsc", | ||
"start": "node dist/index.js", | ||
"dev": "ts-node-dev src/index.ts", | ||
"lint": "eslint . --ext .ts" | ||
}, | ||
"dependencies": { | ||
"express": "^4.18.2", | ||
"pg": "^8.11.0", | ||
"dotenv": "^16.0.3", | ||
"cors": "^2.8.5", | ||
"helmet": "^7.0.0", | ||
"express-validator": "^7.0.1", | ||
"winston": "^3.9.0" | ||
}, | ||
"devDependencies": { | ||
"@types/express": "^4.17.17", | ||
"@types/pg": "^8.10.1", | ||
"@types/cors": "^2.8.13", | ||
"@types/node": "^20.2.5", | ||
"typescript": "^5.0.4", | ||
"ts-node-dev": "^2.0.0", | ||
"@typescript-eslint/eslint-plugin": "^5.59.8", | ||
"@typescript-eslint/parser": "^5.59.8", | ||
"eslint": "^8.41.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import dotenv from 'dotenv'; | ||
dotenv.config(); | ||
|
||
export const config = { | ||
port: process.env.PORT || 3000, | ||
databaseUrl: process.env.DATABASE_URL || '', | ||
environment: process.env.NODE_ENV || 'development', | ||
corsOrigins: process.env.CORS_ORIGINS?.split(',') || ['http://localhost:3000'], | ||
}; |
27 changes: 27 additions & 0 deletions
27
land-registry-backend/src/controllers/inspectorController.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* Controller functions for handling inspector data | ||
* | ||
* These functions manage the retrieval and processing of inspector data, | ||
* including all inspectors and their assignments. | ||
*/ | ||
|
||
import { Request, Response, NextFunction } from 'express'; | ||
import * as inspectorService from '../services/inspectorService'; | ||
|
||
export async function getAllInspectors(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const inspectors = await inspectorService.getAllInspectors(); | ||
res.json(inspectors); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getInspectorAssignments(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const assignments = await inspectorService.getInspectorAssignments(req.params.address); | ||
res.json(assignments); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* Controller functions for handling land registry data | ||
* | ||
* These functions manage the retrieval and processing of land registry data, | ||
* including all lands, individual land details, and related transfers and verifications. | ||
*/ | ||
|
||
import { Request, Response, NextFunction } from 'express'; | ||
import * as landService from '../services/landService'; | ||
import { AppError } from '../middleware/errorHandler'; | ||
|
||
export async function getAllLands(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const lands = await landService.getAllLands(); | ||
res.json(lands); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getLandById(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const land = await landService.getLandById(req.params.landId); | ||
if (!land) { | ||
throw new AppError(404, 'Land not found', 'LAND_NOT_FOUND'); | ||
} | ||
res.json(land); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getLandTransfers(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const transfers = await landService.getLandTransfers(req.params.landId); | ||
res.json(transfers); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getLandVerifications(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const verifications = await landService.getLandVerifications(req.params.landId); | ||
res.json(verifications); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getLandsByOwner(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const lands = await landService.getLandsByOwner(req.params.address); | ||
res.json(lands); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
land-registry-backend/src/controllers/listingController.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/** | ||
* Controller functions for handling marketplace listings | ||
* | ||
* These functions manage the retrieval and processing of marketplace listings, | ||
* including active listings and individual listing details. | ||
*/ | ||
|
||
import { Request, Response, NextFunction } from 'express'; | ||
import * as listingService from '../services/listingService'; | ||
import { AppError } from '../middleware/errorHandler'; | ||
|
||
export async function getActiveListings(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const listings = await listingService.getActiveListings(); | ||
res.json(listings); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getListingById(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const listing = await listingService.getListingById(req.params.listingId); | ||
if (!listing) { | ||
throw new AppError(404, 'Listing not found', 'LISTING_NOT_FOUND'); | ||
} | ||
res.json(listing); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getListingPriceHistory(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const priceHistory = await listingService.getListingPriceHistory(req.params.listingId); | ||
res.json(priceHistory); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
export async function getLandSales(req: Request, res: Response, next: NextFunction) { | ||
try { | ||
const sales = await listingService.getLandSales(); | ||
res.json(sales); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import express from 'express'; | ||
import cors from 'cors'; | ||
import helmet from 'helmet'; | ||
import { landRoutes } from './routes/landRoutes'; | ||
import { inspectorRoutes } from './routes/inspectorRoutes'; | ||
import { listingRoutes } from './routes/listingRoutes'; | ||
import { errorHandler } from './middleware/errorHandler'; | ||
import { config } from './config'; | ||
import { logger } from './utils/logger'; | ||
|
||
const app = express(); | ||
|
||
// Middleware | ||
app.use(helmet()); | ||
app.use(cors()); | ||
app.use(express.json()); | ||
|
||
// // Routes | ||
// app.use('/api/lands', landRoutes); | ||
// app.use('/api/inspectors', inspectorRoutes); | ||
// app.use('/api/listings', listingRoutes); | ||
|
||
// Error handling | ||
app.use(errorHandler); | ||
|
||
app.listen(config.port, () => { | ||
logger.info(`Server running on port ${config.port}`); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Request, Response, NextFunction } from 'express'; | ||
import { logger } from '../utils/logger'; | ||
|
||
export class AppError extends Error { | ||
constructor( | ||
public statusCode: number, | ||
public message: string, | ||
public code?: string | ||
) { | ||
super(message); | ||
} | ||
} | ||
|
||
export const errorHandler = ( | ||
err: Error, | ||
req: Request, | ||
res: Response, | ||
next: NextFunction | ||
) => { | ||
logger.error('Error:', { | ||
message: err.message, | ||
stack: err.stack, | ||
path: req.path, | ||
method: req.method, | ||
}); | ||
|
||
if (err instanceof AppError) { | ||
return res.status(err.statusCode).json({ | ||
status: 'error', | ||
code: err.code, | ||
message: err.message, | ||
}); | ||
} | ||
|
||
return res.status(500).json({ | ||
status: 'error', | ||
message: 'Internal server error', | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Request, Response, NextFunction } from 'express'; | ||
import { validationResult } from 'express-validator'; | ||
import { AppError } from './errorHandler'; | ||
|
||
export const validate = (req: Request, res: Response, next: NextFunction) => { | ||
const errors = validationResult(req); | ||
if (!errors.isEmpty()) { | ||
throw new AppError(400, 'Validation error', 'VALIDATION_ERROR'); | ||
} | ||
next(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Router } from 'express'; | ||
import { param } from 'express-validator'; | ||
import { validate } from '../middleware/validate'; | ||
import * as inspectorController from '../controllers/inspectorController'; | ||
|
||
export const inspectorRoutes = Router(); | ||
|
||
inspectorRoutes.get('/', inspectorController.getAllInspectors); | ||
|
||
inspectorRoutes.get( | ||
'/:address/assignments', | ||
[param('address').isString()], | ||
validate, | ||
inspectorController.getInspectorAssignments | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { Router } from 'express'; | ||
import { param, query } from 'express-validator'; | ||
import { validate } from '../middleware/validate'; | ||
import * as landController from '../controllers/landController'; | ||
|
||
export const landRoutes = Router(); | ||
|
||
landRoutes.get('/', landController.getAllLands); | ||
|
||
landRoutes.get( | ||
'/:landId', | ||
[param('landId').isString()], | ||
validate, | ||
landController.getLandById | ||
); | ||
|
||
landRoutes.get( | ||
'/:landId/transfers', | ||
[param('landId').isString()], | ||
validate, | ||
landController.getLandTransfers | ||
); | ||
|
||
landRoutes.get( | ||
'/:landId/verifications', | ||
[param('landId').isString()], | ||
validate, | ||
landController.getLandVerifications | ||
); | ||
|
||
landRoutes.get( | ||
'/owner/:address', | ||
[param('address').isString()], | ||
validate, | ||
landController.getLandsByOwner | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Router } from 'express'; | ||
import { param } from 'express-validator'; | ||
import { validate } from '../middleware/validate'; | ||
import * as listingController from '../controllers/listingController'; | ||
|
||
export const listingRoutes = Router(); | ||
|
||
listingRoutes.get('/active', listingController.getActiveListings); | ||
|
||
listingRoutes.get( | ||
'/:listingId', | ||
[param('listingId').isString()], | ||
validate, | ||
listingController.getListingById | ||
); | ||
|
||
listingRoutes.get( | ||
'/:listingId/price-history', | ||
[param('listingId').isString()], | ||
validate, | ||
listingController.getListingPriceHistory | ||
); | ||
|
||
listingRoutes.get('/sales', listingController.getLandSales); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Pool } from 'pg'; | ||
import { config } from '../config'; | ||
|
||
export const pool = new Pool({ | ||
connectionString: config.databaseUrl, | ||
}); | ||
|
||
export async function query(text: string, params?: any[]) { | ||
const start = Date.now(); | ||
const res = await pool.query(text, params); | ||
const duration = Date.now() - start; | ||
console.log('Executed query', { text, duration, rows: res.rowCount }); | ||
return res; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { query } from './db'; | ||
import { Inspector, InspectorAssignment } from '../types'; | ||
|
||
export async function getAllInspectors(): Promise<Inspector[]> { | ||
const result = await query(` | ||
SELECT * FROM inspectors | ||
WHERE is_active = true | ||
`); | ||
return result.rows; | ||
} | ||
|
||
export async function getInspectorAssignments(inspectorAddress: string): Promise<InspectorAssignment[]> { | ||
const result = await query(` | ||
SELECT ia.*, l.* | ||
FROM inspector_assignments ia | ||
JOIN lands l ON ia.land_id = l.land_id | ||
WHERE ia.inspector_address = $1 | ||
ORDER BY ia.timestamp DESC | ||
`, [inspectorAddress]); | ||
return result.rows; | ||
} |
Oops, something went wrong.