Skip to content

faisal-akbar/local-guide-server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

76 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ—ΊοΈ Local Guide Platform API

A comprehensive backend API for connecting travelers with passionate local experts who offer authentic, personalized experiences. This platform empowers individuals to share their city's hidden gems, culture, and stories, allowing travelers to explore destinations like a local.


Postman Collection: Local Guide API Collection

🧱 Features

  • πŸ” Authentication: JWT-based authentication with refresh tokens, password reset, and OTP verification
  • πŸ‘₯ Role-based Access Control: TOURIST, GUIDE, and ADMIN roles with appropriate permissions
  • πŸ“ User Management: Complete profile management with role-specific fields (expertise, daily rate for guides, travel preferences for tourists)
  • 🎯 Tour Listings: Create, update, delete, and manage tour listings with images, pricing, and availability
  • πŸ“… Availability Management: Guides can set available dates/times for their tours
  • πŸ“– Booking System: Complete booking workflow with status management (Pending, Confirmed, Completed, Cancelled)
  • πŸ’³ Payment Integration: Stripe integration for secure payment processing
  • ⭐ Review & Rating: Post-tour review and rating system for guides
  • ❀️ Wishlist: Tourists can save favorite listings
  • πŸ… Badge System: Automated badge assignment for guides (Super Guide, Newcomer, Foodie Expert, etc.)
  • πŸ“Š Analytics & Stats: Comprehensive statistics for admins and guides
  • πŸ“§ Email Notifications: OTP verification and password reset via email
  • πŸ“Έ File Uploads: Cloudinary integration for image uploads
  • πŸ” Advanced Search: Filter listings by city, category, price range, and more
  • ⚠️ Global Error Handling: Comprehensive error management and validation
  • 🧱 Modular Architecture: Scalable and maintainable codebase structure
  • πŸ”’ Security: Input validation, authentication middleware, and secure file handling

🧩 Tech Stack

  • Node.js + Express β€” Backend framework
  • TypeScript β€” Type-safe development
  • PostgreSQL + Prisma β€” Database with ORM
  • Zod β€” Schema validation
  • JWT β€” Authentication and authorization
  • Stripe β€” Payment processing
  • Cloudinary β€” Image storage and management
  • Multer β€” File upload handling
  • Node-cron β€” Scheduled tasks
  • dotenv β€” Environment configuration
  • ESLint β€” Code quality and linting
  • Render β€” Deployment

πŸ› οΈ Getting Started

# 1. Clone the repository
git clone <repository-url>
cd b5a8-server

# 2. Install dependencies
npm install

# 3. Configure environment variables
cp .env.example .env

# 4. Update .env with your configuration
PORT=5000
DATABASE_URL="postgresql://username:password@localhost:5432/local_guide_db"
NODE_ENV=development

# JWT
JWT_ACCESS_SECRET=your_access_secret
JWT_ACCESS_EXPIRES=3d
JWT_REFRESH_SECRET=your_refresh_secret
JWT_REFRESH_EXPIRES=10d

# BCRYPT
BCRYPT_SALT_ROUND=10

# SUPER ADMIN
SUPER_ADMIN_EMAIL=super@localguide.com
SUPER_ADMIN_PASSWORD=your_super_admin_password

# Express Session
EXPRESS_SESSION_SECRET=your_session_secret

# Frontend URL
FRONTEND_URL=http://localhost:3000

# CLOUDINARY
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret

# STRIPE
STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret
STRIPE_APPLICATION_FEE_PERCENTAGE=10
STRIPE_CURRENCY=usd


# 5. Prisma migration
npx prisma migrate dev

# 6. Generate Prisma client
npx prisma generate

# 7. Seed database (optional)
npm run db:seed

# 8. Start development server
npm run dev

πŸ‘€ Dummy Users for Testing

Email and password for some dummy users:
// ADMIN
email: super@next.com
password: ph@123456

// TOURIST
email: john.tourist@localguide.com
password: tourist123

// GUIDE
email: ahmed.guide@localguide.com
password: guide123


πŸ“ Project Structure

b5a8-server/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”‚   β”œβ”€β”€ cloudinary.config.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ db.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ env.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ multer.config.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ redis.config.ts
β”‚   β”‚   β”‚   └── stripe.config.ts
β”‚   β”‚   β”œβ”€β”€ interfaces/
β”‚   β”‚   β”‚   β”œβ”€β”€ error.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ error.types.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ index.d.ts
β”‚   β”‚   β”‚   └── pagination.ts
β”‚   β”‚   β”œβ”€β”€ middlewares/
β”‚   β”‚   β”‚   β”œβ”€β”€ checkAuth.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ dbConnection.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ globalErrorHandler.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ notFound.ts
β”‚   β”‚   β”‚   └── validateRequest.ts
β”‚   β”‚   β”œβ”€β”€ modules/
β”‚   β”‚   β”‚   β”œβ”€β”€ auth/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ auth.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ auth.routes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ auth.service.ts
β”‚   β”‚   β”‚   β”‚   └── auth.validation.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ user/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ user.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ user.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ user.routes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ user.service.ts
β”‚   β”‚   β”‚   β”‚   └── user.validation.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ listing/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ listing.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ listing.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ listing.routes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ listing.service.ts
β”‚   β”‚   β”‚   β”‚   └── listing.validation.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ availability/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ availability.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ availability.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ availability.routes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ availability.service.ts
β”‚   β”‚   β”‚   β”‚   └── availability.validation.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ booking/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ booking.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ booking.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ booking.routes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ booking.service.ts
β”‚   β”‚   β”‚   β”‚   └── booking.validation.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ payment/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ payment.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ payment.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ payment.cron.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ payment.routes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ payment.service.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ payment.validation.ts
β”‚   β”‚   β”‚   β”‚   └── payment.webhook.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ review/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ review.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ review.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ review.routes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ review.service.ts
β”‚   β”‚   β”‚   β”‚   └── review.validation.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ wishlist/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ wishlist.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ wishlist.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ wishlist.routes.ts
β”‚   β”‚   β”‚   β”‚   └── wishlist.service.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ badge/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ badge.constant.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ badge.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ badge.routes.ts
β”‚   β”‚   β”‚   β”‚   └── badge.service.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ stats/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ stats.controller.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ stats.routes.ts
β”‚   β”‚   β”‚   β”‚   └── stats.service.ts
β”‚   β”‚   β”‚   └── otp/
β”‚   β”‚   β”‚       β”œβ”€β”€ otp.controller.ts
β”‚   β”‚   β”‚       β”œβ”€β”€ otp.routes.ts
β”‚   β”‚   β”‚       β”œβ”€β”€ otp.service.ts
β”‚   β”‚   β”‚       └── otp.validation.ts
β”‚   β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”‚   └── index.ts
β”‚   β”‚   └── utils/
β”‚   β”‚       β”œβ”€β”€ catchAsync.ts
β”‚   β”‚       β”œβ”€β”€ errorHelpers/
β”‚   β”‚       β”‚   β”œβ”€β”€ AppError.ts
β”‚   β”‚       β”‚   β”œβ”€β”€ handleClientError.ts
β”‚   β”‚       β”‚   β”œβ”€β”€ handleValidationError.ts
β”‚   β”‚       β”‚   └── handleZodError.ts
β”‚   β”‚       β”œβ”€β”€ jwt/
β”‚   β”‚       β”‚   β”œβ”€β”€ jwt.ts
β”‚   β”‚       β”‚   β”œβ”€β”€ setCookie.ts
β”‚   β”‚       β”‚   └── userTokens.ts
β”‚   β”‚       β”œβ”€β”€ paginationHelper.ts
β”‚   β”‚       β”œβ”€β”€ pick.ts
β”‚   β”‚       β”œβ”€β”€ seedDummyUsers.ts
β”‚   β”‚       β”œβ”€β”€ seedSuperAdmin.ts
β”‚   β”‚       β”œβ”€β”€ sendEmail.ts
β”‚   β”‚       └── sendResponse.ts
β”‚   β”œβ”€β”€ app.ts
β”‚   └── server.ts
β”œβ”€β”€ prisma/
β”‚   β”œβ”€β”€ migrations/
β”‚   β”œβ”€β”€ schema.prisma
β”‚   └── prisma.config.ts
β”œβ”€β”€ sample-data/
β”‚   β”œβ”€β”€ availabilities-bookings-payments-reviews.ts
β”‚   β”œβ”€β”€ cleanAndSeed.ts
β”‚   β”œβ”€β”€ listings.ts
β”‚   β”œβ”€β”€ seedAll.ts
β”‚   └── wishlists.ts
β”œβ”€β”€ postman/
β”‚   └── Local_Guide_API.postman_collection.json
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ LocalGuide.md
β”‚   β”œβ”€β”€ README.md
β”‚   β”œβ”€β”€ STRIPE_ENV_SETUP.md
β”‚   └── STRIPE_INTEGRATION.md
β”œβ”€β”€ package.json
└── tsconfig.json

πŸ‘€ User Roles

Role Responsibilities
TOURIST Search tours, book guides, write reviews, manage wishlist
GUIDE Create and manage tour listings, set availability, accept bookings
ADMIN Manage users, listings, bookings, view statistics, manage badges

πŸ“‘ API Endpoints

Base URL: /api/v1

πŸ” Authentication

Method Endpoint Description Auth Required
POST /auth/register Register user (Tourist/Guide) No
POST /auth/login Login user No
POST /auth/refresh-token Refresh JWT token No
POST /auth/logout Logout user Yes
POST /auth/forgot-password Request password reset No
POST /auth/change-password Change password Yes
POST /auth/reset-password Reset password with token Yes

πŸ“§ OTP (Optional)

Method Endpoint Description Auth Required
POST /otp/send Send OTP to email No
POST /otp/verify Verify OTP No

πŸ‘€ Users

Method Endpoint Description Auth Required Role
GET /user/me Get logged-in user profile Yes Any
GET /user/:id Get user by ID No -
GET /user/top-rated-guides Get top rated guides No -
GET /user/all-users Get all users (paginated) Yes ADMIN
PATCH /user/:id Update user profile Yes Owner/Any
PATCH /user/:id/block-user Block/unblock user Yes ADMIN
POST /user/create-admin Create new admin Yes ADMIN

🎯 Listings

Method Endpoint Description Auth Required Role
GET /listings Get all listings (with filters) No -
GET /listings/:id Get listing by ID No -
GET /listings/featured-cities Get featured cities No -
GET /listings/categories/distinct Get distinct categories with counts No -
GET /listings/my/listings Get my listings (paginated) Yes GUIDE
POST /listings Create new listing Yes GUIDE
PATCH /listings/:id Update listing Yes GUIDE
DELETE /listings/:id Delete listing Yes GUIDE

Query Parameters for GET /listings:

  • page - Page number (default: 1)
  • limit - Items per page (default: 10)
  • city - Filter by city
  • category - Filter by category (CULTURE, FOOD, ADVENTURE, etc.)
  • minPrice - Minimum price filter
  • maxPrice - Maximum price filter
  • search - Search in title/description

πŸ“… Availabilities

Method Endpoint Description Auth Required Role
GET /availabilities Get all availabilities No -
GET /availabilities/:id Get availability by ID No -
GET /availabilities/my/availabilities Get my availabilities Yes GUIDE
POST /availabilities Create availability Yes GUIDE
POST /availabilities/bulk Create bulk availabilities Yes GUIDE
PATCH /availabilities/:id Update availability Yes GUIDE
DELETE /availabilities/:id Delete availability Yes GUIDE

Query Parameters for GET /availabilities:

  • listingId - Filter by listing ID
  • page - Page number
  • limit - Items per page

πŸ“– Bookings

Method Endpoint Description Auth Required Role
POST /bookings Create booking request Yes TOURIST
GET /bookings/my-bookings Get my bookings Yes Any
GET /bookings Get all bookings (admin) Yes ADMIN
GET /bookings/:id Get booking by ID Yes Any
PATCH /bookings/:id/status Update booking status Yes GUIDE

Query Parameters for GET /bookings/my-bookings:

  • page - Page number
  • limit - Items per page
  • type - Filter by type (upcoming, past)
  • status - Filter by status (PENDING, CONFIRMED, COMPLETED, CANCELLED)

Booking Status Values:

  • PENDING - Awaiting guide confirmation
  • CONFIRMED - Guide accepted the booking
  • COMPLETED - Tour completed
  • CANCELLED - Booking cancelled

πŸ’³ Payments

Method Endpoint Description Auth Required Role
POST /payments/confirm Confirm payment with Stripe Yes TOURIST
GET /payments Get my payments Yes Any
GET /payments/booking/:bookingId Get payment by booking ID Yes Any
GET /payments/:id Get payment by ID Yes Any
POST /payments/:id/release Release payment to guide Yes GUIDE

Query Parameters for GET /payments:

  • page - Page number
  • limit - Items per page

Note: Payment processing is handled via Stripe. The webhook endpoint /webhook handles Stripe events.


⭐ Reviews

Method Endpoint Description Auth Required Role
GET /reviews Get all reviews No -
GET /reviews/:id Get review by ID No -
GET /reviews/my/reviews Get my reviews (as guide) Yes GUIDE
GET /reviews/reviewable-bookings Get reviewable bookings Yes TOURIST
GET /reviews/booking/:bookingId Get review by booking ID No -
GET /reviews/guide/:guideId Get reviews by guide ID No -
POST /reviews Create review Yes TOURIST
PATCH /reviews/:id Update review Yes TOURIST
DELETE /reviews/:id Delete review Yes TOURIST

Query Parameters for GET /reviews:

  • listingId - Filter by listing ID
  • page - Page number
  • limit - Items per page

Query Parameters for GET /reviews/my/reviews:

  • page - Page number
  • limit - Items per page
  • sortBy - Sort field (createdAt, rating)
  • sortOrder - Sort order (asc, desc)

❀️ Wishlist

Method Endpoint Description Auth Required Role
POST /wishlist Add listing to wishlist Yes TOURIST
GET /wishlist Get my wishlist Yes TOURIST
GET /wishlist/check/:listingId Check if listing in wishlist Yes TOURIST
DELETE /wishlist/:listingId Remove from wishlist Yes TOURIST

Query Parameters for GET /wishlist:

  • page - Page number
  • limit - Items per page

πŸ… Badges

Method Endpoint Description Auth Required Role
GET /badges/guide/:guideId Get guide badges No -
POST /badges/guide/:guideId/recalculate Recalculate guide badges Yes ADMIN
POST /badges/recalculate-all Recalculate all badges Yes ADMIN

Badge Types:

  • SUPER_GUIDE - High ratings and many bookings
  • NEWCOMER - New guide on the platform
  • FOODIE_EXPERT - Expertise in food tours
  • CULTURE_MASTER - Expertise in cultural tours
  • ADVENTURE_SEEKER - Expertise in adventure tours

πŸ“Š Stats

Method Endpoint Description Auth Required Role
GET /stats/overview Get overview statistics Yes ADMIN
GET /stats/users Get user statistics Yes ADMIN
GET /stats/tourists Get tourist statistics Yes ADMIN
GET /stats/guides Get guide statistics Yes ADMIN
GET /stats/listings Get listing statistics Yes ADMIN
GET /stats/bookings Get booking statistics Yes ADMIN
GET /stats/revenue Get revenue statistics Yes ADMIN
GET /stats/profit Get profit statistics Yes ADMIN
GET /stats/guide-info Get guide info and stats Yes GUIDE

πŸ—„οΈ Database Schema

User Model

  • id: Primary key
  • name: User's full name
  • email: Unique email address
  • password: Hashed password
  • phone: Optional phone number
  • picture: Profile picture URL
  • bio: User biography
  • languages: Array of languages spoken
  • role: User role (TOURIST, GUIDE, ADMIN)
  • isActive: Account status (ACTIVE, INACTIVE, BLOCKED)
  • isVerified: Email verification status
  • isDeleted: Soft delete flag
  • expertise: Array of expertise areas (Guide only)
  • dailyRate: Daily rate for guides
  • travelPreferences: Array of travel preferences (Tourist only)
  • stripeCustomerId: Stripe customer ID (Tourist only)

Listing Model

  • id: Primary key
  • title: Tour listing title
  • description: Tour description
  • itinerary: Detailed itinerary
  • tourFee: Tour fee amount
  • durationDays: Maximum duration in days
  • meetingPoint: Meeting point location
  • maxGroupSize: Maximum group size
  • city: City where tour is located
  • category: Tour category (CULTURE, FOOD, ADVENTURE, etc.)
  • images: Array of image URLs
  • isActive: Listing active status
  • guideId: Reference to Guide User

Availability Model

  • id: Primary key
  • startDateTime: Start date and time
  • endDateTime: End date and time
  • isAvailable: Availability status
  • listingId: Reference to Listing

Booking Model

  • id: Primary key
  • date: Booking date
  • numberOfGuests: Number of guests
  • status: Booking status (PENDING, CONFIRMED, COMPLETED, CANCELLED)
  • touristId: Reference to Tourist User
  • listingId: Reference to Listing
  • availabilityId: Reference to Availability

Payment Model

  • id: Primary key
  • amount: Payment amount
  • currency: Payment currency
  • status: Payment status
  • paymentIntentId: Stripe payment intent ID
  • bookingId: Reference to Booking
  • touristId: Reference to Tourist User
  • guideId: Reference to Guide User

Review Model

  • id: Primary key
  • rating: Rating (1-5)
  • comment: Review comment
  • bookingId: Reference to Booking
  • touristId: Reference to Tourist User
  • guideId: Reference to Guide User
  • listingId: Reference to Listing

Wishlist Model

  • id: Primary key
  • touristId: Reference to Tourist User
  • listingId: Reference to Listing

Badge Model

  • id: Primary key
  • type: Badge type
  • guideId: Reference to Guide User

πŸ”’ Authentication

All protected endpoints require tokens in the cookies:

cookie: accessToken=<access_token>
cookie: refreshToken=<refresh_token>

Access tokens expire after the configured time (default: 3 days). Use the refresh token endpoint to obtain a new access token.


πŸ“ Request/Response Format

Success Response

{
  "success": true,
  "statusCode": 200,
  "message": "Operation successful",
  "data": {
    // Response data
  },
  "meta": {
    // Pagination metadata (if applicable)
    "page": 1,
    "limit": 10,
    "total": 100,
    "totalPages": 10
  }
}

Error Response

{
  "success": false,
  "statusCode": 400,
  "message": "Error message",
  "errorDetails": {
    // Detailed error information
  },
  "stack": "Error stack (development only)"
}

πŸ§ͺ Testing

Use the provided Postman collection to test all endpoints:

  1. Import postman/Local_Guide_API.postman_collection.json into Postman
  2. Set the base_url variable to your API URL (default: http://localhost:5000/api/v1)
  3. Start with authentication endpoints to get access tokens
  4. Tokens are automatically saved to collection variables for subsequent requests

πŸ“š Additional Documentation


πŸš€ Deployment

The application can be deployed to various platforms. Ensure all environment variables are properly configured in your deployment environment.

Build for Production

npm run build

Start Production Server

npm start

πŸ‘¨β€πŸ’» Author

Faisal Akbar

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors