Conversation
create backend of catagory and services
add category data
migrate entire codebase from JavaScript to TypeScript
refactor: update Dockerfile to install TypeScript and run transpiled …
PR from main
…, and retrieval functionalities
…r admin creation endpoints
…on Prisma client pattern and enhancing connection pool configuration docs: add troubleshooting guide for database connection issues and optimizations implemented perf: optimize backend performance by implementing caching, response compression, and database indexing feat: create comprehensive monitoring for connection pool and performance metrics chore: add tests for connection pool and database performance to ensure stability and efficiency
…th checks - Added @types/aws-sdk to dependencies for TypeScript support. - Removed unused quick-db-test.js, performance.middleware.ts, connection-monitor.ts, and related test files. - Simplified health route by removing unnecessary database stats and connection time logging. - Updated user.service.ts to improve performance by counting services instead of fetching them. - Cleaned up database connection logic in database.ts and removed retry logic. - Removed various test scripts related to admin and connection testing. - Created a new empty dev.db file for Prisma.
…Socket connection
… reviews by rating, and show rating distribution.
- Created Admin model in Prisma schema with fields: id, username, password, firstName, lastName. - Implemented admin authentication middleware to protect routes and validate JWT tokens. - Developed AdminController with methods for registration, login, profile retrieval, and updates. - Added validation schemas for admin registration, login, and updates using Joi. - Updated routes to include admin-specific endpoints for registration, login, profile management, and admin listing. - Created admin service for handling database operations related to admin users. - Added SQL migration for creating the Admin table in the database.
There was a problem hiding this comment.
Pull Request Overview
This pull request introduces comprehensive geolocation search functionality to the platform, enabling location-based service discovery. The changes migrate the codebase from JavaScript to TypeScript for better type safety and include extensive location-aware features.
- Added geolocation search capabilities with Google Maps integration for address geocoding and distance calculations
- Migrated core services and validators from JavaScript to TypeScript with proper type definitions
- Enhanced service validation with location fields and geospatial search parameters
Reviewed Changes
Copilot reviewed 98 out of 124 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/validators/services.validator.ts | New TypeScript validator with location fields and geospatial search schemas |
| src/services/services.service.ts | Enhanced service layer with location processing and PostGIS spatial queries |
| src/services/googleMaps.service.ts | New Google Maps integration for geocoding and reverse geocoding |
| src/controllers/services.controller.ts | Updated controllers with hybrid search combining semantic and location-based filtering |
| src/routes/services.route.ts | Expanded API routes for location-based search endpoints |
| src/utils/database.ts | New singleton database manager for improved connection handling |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| lat: Joi.number().min(-90).max(90).optional().messages({ | ||
| 'number.min': 'Latitude must be between -90 and 90', | ||
| 'number.max': 'Latitude must be between -90 and 90' | ||
| }), | ||
| lng: Joi.number().min(-180).max(180).optional().messages({ | ||
| 'number.min': 'Longitude must be between -180 and 180', | ||
| 'number.max': 'Longitude must be between -180 and 180' | ||
| }), | ||
| latitude: Joi.number().min(-90).max(90).optional().messages({ | ||
| 'number.min': 'Latitude must be between -90 and 90', | ||
| 'number.max': 'Latitude must be between -90 and 90' | ||
| }), | ||
| longitude: Joi.number().min(-180).max(180).optional().messages({ | ||
| 'number.min': 'Longitude must be between -180 and 180', | ||
| 'number.max': 'Longitude must be between -180 and 180' | ||
| }) |
There was a problem hiding this comment.
The reverseGeocodeSchema duplicates latitude/longitude validation by supporting both 'lat/lng' and 'latitude/longitude' formats. Consider creating a reusable coordinate validation helper to reduce code duplication and ensure consistency across all coordinate validations.
| export const reverseGeocodeSchema = Joi.object({ | ||
| lat: Joi.number().min(-90).max(90).optional().messages({ | ||
| 'number.min': 'Latitude must be between -90 and 90', | ||
| 'number.max': 'Latitude must be between -90 and 90' | ||
| }), | ||
| lng: Joi.number().min(-180).max(180).optional().messages({ | ||
| 'number.min': 'Longitude must be between -180 and 180', | ||
| 'number.max': 'Longitude must be between -180 and 180' | ||
| }), | ||
| latitude: Joi.number().min(-90).max(90).optional().messages({ | ||
| 'number.min': 'Latitude must be between -90 and 90', | ||
| 'number.max': 'Latitude must be between -90 and 90' | ||
| }), | ||
| longitude: Joi.number().min(-180).max(180).optional().messages({ | ||
| 'number.min': 'Longitude must be between -180 and 180', | ||
| 'number.max': 'Longitude must be between -180 and 180' | ||
| }) | ||
| }).or('lat', 'latitude').or('lng', 'longitude'); |
There was a problem hiding this comment.
The .or() chain requires either 'lat' OR 'latitude' and either 'lng' OR 'longitude', but doesn't enforce that coordinates come in matching pairs (lat+lng or latitude+longitude). This could allow invalid combinations like 'lat' with 'longitude'. Consider using .and() with conditional validation to ensure coordinate pairs are consistent.
| export const reverseGeocodeSchema = Joi.object({ | |
| lat: Joi.number().min(-90).max(90).optional().messages({ | |
| 'number.min': 'Latitude must be between -90 and 90', | |
| 'number.max': 'Latitude must be between -90 and 90' | |
| }), | |
| lng: Joi.number().min(-180).max(180).optional().messages({ | |
| 'number.min': 'Longitude must be between -180 and 180', | |
| 'number.max': 'Longitude must be between -180 and 180' | |
| }), | |
| latitude: Joi.number().min(-90).max(90).optional().messages({ | |
| 'number.min': 'Latitude must be between -90 and 90', | |
| 'number.max': 'Latitude must be between -90 and 90' | |
| }), | |
| longitude: Joi.number().min(-180).max(180).optional().messages({ | |
| 'number.min': 'Longitude must be between -180 and 180', | |
| 'number.max': 'Longitude must be between -180 and 180' | |
| }) | |
| }).or('lat', 'latitude').or('lng', 'longitude'); | |
| export const reverseGeocodeSchema = Joi.alternatives().try( | |
| Joi.object({ | |
| lat: Joi.number().min(-90).max(90).required().messages({ | |
| 'number.min': 'Latitude must be between -90 and 90', | |
| 'number.max': 'Latitude must be between -90 and 90', | |
| 'any.required': 'Latitude is required' | |
| }), | |
| lng: Joi.number().min(-180).max(180).required().messages({ | |
| 'number.min': 'Longitude must be between -180 and 180', | |
| 'number.max': 'Longitude must be between -180 and 180', | |
| 'any.required': 'Longitude is required' | |
| }) | |
| }), | |
| Joi.object({ | |
| latitude: Joi.number().min(-90).max(90).required().messages({ | |
| 'number.min': 'Latitude must be between -90 and 90', | |
| 'number.max': 'Latitude must be between -90 and 90', | |
| 'any.required': 'Latitude is required' | |
| }), | |
| longitude: Joi.number().min(-180).max(180).required().messages({ | |
| 'number.min': 'Longitude must be between -180 and 180', | |
| 'number.max': 'Longitude must be between -180 and 180', | |
| 'any.required': 'Longitude is required' | |
| }) | |
| }) | |
| ); |
| // Execute queries | ||
| const [servicesResult, countResult] = await Promise.all([ | ||
| prisma.$queryRawUnsafe(servicesQuery, ...queryParams), | ||
| prisma.$queryRawUnsafe(countQuery, longitude, latitude, radius * 1000, ...queryParams.slice(5, paramIndex - 1)) |
There was a problem hiding this comment.
The parameter slicing logic queryParams.slice(5, paramIndex - 1) may pass incorrect parameters to the count query. The count query expects the same base parameters (longitude, latitude, radius) plus additional filters, but the slice range doesn't account for the paramIndex offset correctly. This could cause parameter mismatch errors.
| prisma.$queryRawUnsafe(countQuery, longitude, latitude, radius * 1000, ...queryParams.slice(5, paramIndex - 1)) | |
| prisma.$queryRawUnsafe(countQuery, longitude, latitude, radius * 1000, ...queryParams.slice(6, -2)) |
| const clientIP = req.ip || req.connection.remoteAddress || req.headers['x-forwarded-for'] as string; | ||
|
|
There was a problem hiding this comment.
The IP extraction logic has potential type safety issues. req.connection.remoteAddress is deprecated, and req.headers['x-forwarded-for'] can be an array. Consider using a more robust IP extraction approach that handles arrays and uses the standard req.socket.remoteAddress instead of the deprecated property.
| const clientIP = req.ip || req.connection.remoteAddress || req.headers['x-forwarded-for'] as string; | |
| // Robust client IP extraction | |
| let clientIP: string | undefined; | |
| if (req.ip) { | |
| clientIP = req.ip; | |
| } else if (req.headers['x-forwarded-for']) { | |
| const xff = req.headers['x-forwarded-for']; | |
| if (Array.isArray(xff)) { | |
| clientIP = xff[0]; | |
| } else if (typeof xff === 'string') { | |
| clientIP = xff.split(',')[0].trim(); | |
| } | |
| } else if (req.socket && req.socket.remoteAddress) { | |
| clientIP = req.socket.remoteAddress; | |
| } | |
| if (!clientIP) { | |
| clientIP = ''; | |
| } |
geolocation searching