Click here to Skip to main content
15,867,686 members
Articles / Web Development / Node.js

BookCars - Car Rental Platform with Mobile App

Rate me:
Please Sign up or sign in to vote.
4.98/5 (75 votes)
18 Mar 2024MIT58 min read 192.1K   6.6K   186   125
Car Rental Platform with a mobile app
In this article you will learn about BookCars. A car rental platform with a mobile app. You will get a brief overview of the platform introducing the frontend, the backend and the mobile app. You will also learn how it was made, including a description of the key parts of the source code and software architecture, how to build Docker image and the mobile app, and how to run the source code.

Table of Contents

  1. Introduction
  2. Features
  3. Prerequisites
  4. Quick Overview
    1. Frontend
    2. Mobile App
    3. Backend
  5. Background
  6. Installing
  7. Docker
    1. Docker Image
    2. SSL
  8. Change Currency
  9. Add New Language
  10. Demo Database
    1. Windows, Linux, macOS
    2. Docker
  11. Build Mobile App
  12. Run from Source
  13. Run Mobile App
  14. Unit Tests and Coverage
  15. Using the Code
    1. API
    2. Frontend
    3. Mobile App
    4. Backend
  16. Points of Interest
  17. History

Introduction

Image 1

BookCars is an open-source and cross-platform car rental platform, supplier-oriented, with a backend for managing car fleets and bookings, as well as a frontend and a mobile app for renting cars.

BookCars is designed to work with multiple suppliers. Each supplier can manage his car fleet and bookings from the backend. BookCars can also work with only one supplier as well.

From the backend, admins can create and manage suppliers, cars, locations, customers and bookings.

When new suppliers are created, they will receive an email prompting them to create an account in order to access the backend and manage their car fleet and bookings.

Customers can sign up from the frontend or the mobile app, search for available cars based on pickup and drop-off points and time, choose a car and complete the checkout process.

A key design decision was made to use TypeScript instead of JavaScript due to its numerous advantages. TypeScript offers strong typing, tooling, and integration, resulting in high-quality, scalable, more readable and maintainable code that is easy to debug and test.

Image 2

BookCars can run in a Docker container. In this article, you can find how to build BookCars Docker image and run it in a Docker container.

BookCars is user-friendly, straightforward, secure against XSS, XST, CSRF and MITM, and subtly crafted.

In this article, you will learn how BookCars was made including a description of the main parts of the source code and the software architecture, how to deploy it, and how to run the source code. But before we dig in, we'll start with a quick overview of the platform.

Features

  • Supplier management
  • Ready for one or multiple suppliers
  • Car fleet management
  • Booking management
  • Customer management
  • Multiple payment methods (Credit Card, Pay Later)
  • Multiple language support (English, French)
  • Multiple pagination options (Classic pagination with next and previous buttons, infinite scroll)
  • Responsive backend and frontend
  • Native Mobile app for Android and iOS with single codebase
  • Push notifications
  • Secure against XSS, XST, CSRF and MITM

Prerequisites

  • TypeScript
  • Node.js
  • Express
  • MongoDB
  • React
  • MUI
  • React Native
  • Expo
  • JWT
  • MVC
  • Docker
  • NGINX
  • Git

Quick Overview

In this section, you'll see a quick overview of the main pages of the frontend, the backend and the mobile app.

Frontend

From the frontend, customers can search for available cars, choose a car and checkout.

Below is the main page of the frontend where the customer can choose pickup and drop-off points and time, and search for available cars.

Image 3

Below is the search result of the main page where the customer can choose a car for rental.

Image 4

Below is the checkout page where the customer can set rental options and checkout. If the customer is not registered, he can checkout and register at the same time. He will receive a confirmation and activation email prompting him to set his password if he is not registered yet.

Image 5

Below is the sign in page. On production, authentication cookies are httpOnly, signed, secure and strict sameSite. These options prevent XSS, CSRF and MITM attacks. Authentication cookies are protected against XST attacks as well via a custom middleware.

Image 6

Below is the sign up page.

Image 7

Below is the page where the customer can see and manage his bookings.

Image 8

Below is the page where the customer can see a booking in detail.

Image 9

Below is the page where the customer can see and manage his notifications.

Image 10

Below is the page where the customer can manage his settings.

Image 11

Below is the page where the customer can change his password.

Image 12

There are other pages but these are the main pages of the frontend.

Mobile App

From the mobile app, customers can search for available cars, choose a car and checkout.

The customer can also receive push notifications, if the status of his booking is updated from the backend.

Below are the main pages of the mobile app where the customer can choose pickup and drop-off points and time, and search for available cars.

Image 13 Image 14 Image 15

Below is the search result of the main page where the customer can choose a car for rental and checkout.

Image 16 Image 17 Image 18 Image 19

Below are sign in and sign up pages.

Image 20 Image 21 Image 22

Below are the pages where the customer can see and manage his bookings.

Image 23 Image 24 Image 25 Image 26 Image 27

Below are the pages where the customer can update his profile information, change his password and see his notifications.

Image 28 Image 29 Image 30 Image 31

That's it for the main pages of the mobile app.

Backend

BookCars is supplier-oriented. This means that there are three types of users:

  • Admin: He has full access to the backend. He can do everything.
  • Supplier: He has restricted access to the backend. He can only manage his car fleet and bookings.
  • Customer: He has access only to the frontend and the mobile app. He cannot access the backend.

BookCars is designed to work with multiple suppliers. Each supplier can manage his car fleet and bookings from the backend. BookCars can also work with only one supplier as well.

From the backend, admins can create and manage suppliers, cars, locations, customers and bookings.

When new suppliers are created, they will receive an email prompting them to create an account in order to access the backend and manage their car fleet and bookings.

Below is the sign in page of the backend.

Image 32

Below is the dashboard page of the backend where admins and suppliers can see and manage bookings.

Image 33

Below is the page where car fleet is displayed and can be managed.

Image 34

Below is the page where admins and suppliers can create new cars by providing an image and car info. For car options to be included for free, set 0 for the corresponding car option. Otherwise, set the price of the option or leave it empty if you don't want to include it.

Image 35

Below is the page where admins and suppliers can edit cars.

Image 36

Below is the page where admins can manage users.

Image 37

Below is the page where to edit bookings.

Image 38

There are other pages but these are the main pages of the backend.

Background

The basic idea behind BookCars is very simple:

  • A backend: From which admins create new suppliers. Each supplier will receive an automatic email to activate his account and have access to the backend so he can manage his car fleet, bookings and customers.
  • A frontend and a mobile app: From which customers can see available cars depending on pickup and drop-off points and time. Then, they can proceed to checkout in order to book their cars.

Installing

BookCars is cross-platform and can run and be installed on Windows, Linux and macOS.

Below are the installation instructions on Linux.

Prerequisites

  1. Install git, Node.js, NGINX, MongoDB and mongosh.

  2. Configure MongoDB:

    Shell
    mongosh

    Create admin user:

    db = db.getSiblingDB('admin')
    db.createUser({ user: "admin" , pwd: "PASSWORD", 
    roles: ["userAdminAnyDatabase", "dbAdminAnyDatabase", "readWriteAnyDatabase"]})

    Replace PASSWORD with a strong password.

    Secure MongoDB:

    Shell
    sudo nano /etc/mongod.conf

    Change configuration as follow:

    net:
      port: 27017
      bindIp: 0.0.0.0
    
    security:
      authorization: enabled

    Restart MongoDB service:

    Shell
    sudo systemctl restart mongod.service
    sudo systemctl status mongod.service

Instructions

  1. Clone BookCars repo:
    Shell
    cd /opt
    sudo git clone https://github.com/aelassas/bookcars.git
  2. Add permissions:
    Shell
    sudo chown -R $USER:$USER /opt/bookcars
    sudo chmod -R +x /opt/bookcars/__scripts
  3. Create deployment shortcut:
    Shell
    sudo ln -s /opt/bookcars/__scripts/bc-deploy.sh /usr/local/bin/bc-deploy
  4. Create BookCars service:
    Shell
    sudo cp /opt/bookcars/__services/bookcars.service /etc/systemd/system
    sudo systemctl enable bookcars.service
  5. Create /opt/bookcars/api/.env file:
    NODE_ENV=production
    BC_PORT=4002
    BC_HTTPS=false
    BC_PRIVATE_KEY=/etc/ssl/bookcars.com.key
    BC_CERTIFICATE=/etc/ssl/bookcars.com.crt
    BC_DB_URI=mongodb://admin:PASSWORD@127.0.0.1:27017/bookcars?authSource=admin&appName=bookcars
    BC_DB_SSL=false
    BC_DB_SSL_CERT=/etc/ssl/bookcars.com.crt
    BC_DB_SSL_CA=/etc/ssl/bookcars.com.ca.pem
    BC_DB_DEBUG=false
    BC_COOKIE_SECRET=COOKIE_SECRET
    BC_AUTH_COOKIE_DOMAIN=localhost
    BC_JWT_SECRET=JWT_SECRET
    BC_JWT_EXPIRE_AT=86400
    BC_TOKEN_EXPIRE_AT=86400
    BC_SMTP_HOST=in-v3.mailjet.com
    BC_SMTP_PORT=587
    BC_SMTP_USER=USER
    BC_SMTP_PASS=PASSWORD
    BC_SMTP_FROM=admin@bookcars.com
    BC_CDN_USERS=/var/www/cdn/bookcars/users
    BC_CDN_TEMP_USERS=/var/www/cdn/bookcars/temp/users
    BC_CDN_CARS=/var/www/cdn/bookcars/cars
    BC_CDN_TEMP_CARS=/var/www/cdn/temp/bookcars/cars
    BC_DEFAULT_LANGUAGE=en
    BC_BACKEND_HOST=http://localhost:3001/
    BC_FRONTEND_HOST=http://localhost/
    BC_MINIMUM_AGE=21
    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN

    You need to configure the following options:

    BC_DB_URI=mongodb://admin:PASSWORD@127.0.0.1:27017/bookcars?authSource=admin&appName=bookcars
    BC_COOKIE_SECRET=COOKIE_SECRET
    BC_AUTH_COOKIE_DOMAIN=localhost
    BC_JWT_SECRET=JWT_SECRET
    BC_SMTP_HOST=in-v3.mailjet.com
    BC_SMTP_PORT=587
    BC_SMTP_USER=USER
    BC_SMTP_PASS=PASSWORD
    BC_SMTP_FROM=admin@bookcars.com
    BC_BACKEND_HOST=http://localhost:3001/
    BC_FRONTEND_HOST=http://localhost/

    Replace PASSWORD in BC_DB_URI with MongoDB password and JWT_SECRET with a secret token. Finally, set the SMTP options. SMTP options are necessary for sign up. You can use mailjet, brevo, sendgrid or any other transactional email provider.

    COOKIE_SECRET and JWT_SECRET should at least be 32 characters long, but the longer the better. You can use an online password generator and set the password length to 32 or longer.

    Leave localhost if you want to test locally or replace it with an IP, hostname or FQDN.

    If you want to enable push notifications in the mobile app, follow these instructions and set the following option:

    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN

    If you want to enable HTTPS, you need to set the following options:

    BC_HTTPS = true
    BC_PRIVATE_KEY=/etc/ssl/bookcars.com.key
    BC_CERTIFICATE=/etc/ssl/bookcars.com.crt
  6. Create /opt/bookcars/backend/.env file:
    PORT=3001
    REACT_APP_NODE_ENV=production
    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_DEFAULT_LANGUAGE=en
    REACT_APP_BC_PAGE_SIZE=30
    REACT_APP_BC_CARS_PAGE_SIZE=15
    REACT_APP_BC_BOOKINGS_PAGE_SIZE=20
    REACT_APP_BC_BOOKINGS_MOBILE_PAGE_SIZE=10
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_TEMP_USERS=http://localhost/cdn/bookcars/temp/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_CDN_TEMP_CARS=http://localhost/cdn/bookcars/temp/cars
    REACT_APP_BC_COMPANY_IMAGE_WIDTH=60
    REACT_APP_BC_COMPANY_IMAGE_HEIGHT=30
    REACT_APP_BC_CAR_IMAGE_WIDTH=300
    REACT_APP_BC_CAR_IMAGE_HEIGHT=200
    REACT_APP_BC_MINIMUM_AGE=21
    REACT_APP_BC_PAGINATION_MODE=classic

    You need to configure the following options:

    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_TEMP_USERS=http://localhost/cdn/bookcars/temp/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_CDN_TEMP_CARS=http://localhost/cdn/bookcars/temp/cars

    Leave localhost if you want to test locally or replace it with an IP, hostname or FQDN.

    REACT_APP_BC_PAGINATION_MODE: You can choose between classic or infinite_scroll. This option defaults to classic. If you choose classic, you will get a classic pagination with next and previous buttons on desktop and infinite scroll on mobile. If you choose infinite_scroll, you will get infinite scroll on desktop and mobile.

  7. Create /opt/bookcars/frontend/.env file:
    REACT_APP_NODE_ENV=production
    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_RECAPTCHA_ENABLED=false
    REACT_APP_BC_RECAPTCHA_SITE_KEY=GOOGLE_RECAPTCHA_SITE_KEY
    REACT_APP_BC_DEFAULT_LANGUAGE=en
    REACT_APP_BC_PAGE_SIZE=30
    REACT_APP_BC_CARS_PAGE_SIZE=15
    REACT_APP_BC_BOOKINGS_PAGE_SIZE=20
    REACT_APP_BC_BOOKINGS_MOBILE_PAGE_SIZE=10
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_COMPANY_IMAGE_WIDTH=60
    REACT_APP_BC_COMPANY_IMAGE_HEIGHT=30
    REACT_APP_BC_CAR_IMAGE_WIDTH=300
    REACT_APP_BC_CAR_IMAGE_HEIGHT=200
    REACT_APP_BC_MINIMUM_AGE=21
    REACT_APP_BC_PAGINATION_MODE=classic

    You need to configure the following options:

    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars

    Leave localhost if you want to test locally or replace it with an IP, hostname or FQDN.

    reCAPTCHA is by default disabled. If you want to enable it, you need to set REACT_APP_BC_RECAPTCHA_ENABLED to true and REACT_APP_BC_RECAPTCHA_SITE_KEY to Google reCAPTCHA site key.

  8. If you want to run or build the mobile app, you need to create mobile/.env:
    BC_API_HOST=https://bookcars.com:4002
    BC_DEFAULT_LANGUAGE=en
    BC_PAGE_SIZE=20
    BC_CARS_PAGE_SIZE=8
    BC_BOOKINGS_PAGE_SIZE=8
    BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
    BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars
    BC_COMPANY_IMAGE_WIDTH=60
    BC_COMPANY_IMAGE_HEIGHT=30
    BC_CAR_IMAGE_WIDTH=300
    BC_CAR_IMAGE_HEIGHT=200
    BC_MINIMUM_AGE=21

    You need to configure the following options:

    BC_API_HOST=https://bookcars.com:4002
    BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
    BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars

    Replace localhost with an IP, hostname or FQDN.

  9. Configure NGINX:
    Shell
    sudo nano /etc/nginx/sites-available/default

    Change the configuration as follow for the frontend:

    server {
        root /var/www/bookcars/frontend;
        #listen 443 http2 ssl default_server;
        listen 80 default_server;
        server_name _;
        
        #ssl_certificate_key /etc/ssl/bookcars.com.key;
        #ssl_certificate /etc/ssl/bookcars.com.pem;
    
        access_log /var/log/nginx/bookcars.frontend.access.log;
        error_log /var/log/nginx/bookcars.frontend.error.log;
    
        index index.html;
    
        location / {
            # First attempt to serve request as file, then as directory,
            # then as index.html, then fall back to displaying a 404.
            try_files $uri $uri/ /index.html =404;
        }
    
        location /cdn {
          alias /var/www/cdn;
        }
    }

    If you want to enable SSL, uncomment and set these lines:

    #listen 443 http2 ssl default_server
    #ssl_certificate_key /etc/ssl/bookcars.com.key
    #ssl_certificate /etc/ssl/bookcars.com.pem;

    Add the following configuration for the backend:

    server {
        root /var/www/bookcars/backend;
        #listen 3001 http2 ssl default_server;
        listen 3001 default_server;
        server_name _;
    
        #ssl_certificate_key /etc/ssl/bookcars.com.key;
        #ssl_certificate /etc/ssl/bookcars.com.pem;
    
        #error_page 497 301 =307 https://$host:$server_port$request_uri;
    
        access_log /var/log/nginx/bookcars.backend.access.log;
        error_log /var/log/nginx/bookcars.backend.error.log;
    
        index index.html;
    
        location / {
            # First attempt to serve request as file, then as directory,
            # then as index.html, then fall back to displaying a 404.
            try_files $uri $uri/ /index.html =404;
        }
    }

    Create /var/www/cdn/bookcars folder and add full access permissions to the user who is running bookcars service on /var/www/cdn/bookcars.

    If you want to enable SSL, uncomment and set these lines:

    #listen 3001 http2 ssl default_server
    #ssl_certificate_key /etc/ssl/bookcars.com.key
    #ssl_certificate /etc/ssl/bookcars.com.pem
    #error_page 497 301 =307 https://$host:$server_port$request_uri;

    Then, check NGINX configuration and restart NGINX service:

    Shell
    sudo nginx -t
    sudo systemctl restart nginx.service
    sudo systemctl status nginx.service
  10. Enable the firewall and open BookCars ports:
    Shell
    sudo ufw enable
    sudo ufw allow 4002/tcp
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw allow 3001/tcp
    sudo ufw allow 27017/tcp
  11. Start bookcars service:
    Shell
    cd /opt/bookcars/api
    npm install --omit=dev
    sudo systemctl start bookcars.service

    Make sure that BookCars service is running with the following command:

    Shell
    sudo systemctl status bookcars.service

    Make sure that the database connection is established by checking the logs:

    Shell
    tail -f /var/log/bookcars.log
  12. Deploy BookCars:
    Shell
    bc-deploy all

    BookCars backend is accessible on port 3001 and the frontend is accessible on port 80.

  13. If you don't want to use the demo database, create an admin by navigating to hostname:3001/sign-up

  14. Open backend/src/App.tsx and comment these lines to secure the backend:

    const SignUp = lazy(() => import('./pages/SignUp'))
    <Route exact path='/sign-up' element={<Signup />} />

    And run backend deployment again:

    Shell
    bc-deploy backend

If you want to deploy the frontend only, run the following command:

Shell
bc-deploy frontend

If you want to deploy the api only, run the following command:

Shell
bc-deploy api

If you want to deploy the api, the backend and the frontend, run the following command:

Shell
bc-deploy all

If you want to change the currency follow these instructions.

Docker

BookCars can run in a Docker container on Linux and Docker Desktop for Windows or Mac.

Docker Image

This section describes how to build BookCars Docker image and run it in a Docker container.

  1. Clone BookCars repo:
    Shell
    git clone https://github.com/aelassas/bookcars.git
  2. Create ./api/.env.docker file with the following content:
    NODE_ENV=production
    BC_PORT=4002
    BC_HTTPS=false
    BC_PRIVATE_KEY=/etc/ssl/bookcars.key
    BC_CERTIFICATE=/etc/ssl/bookcars.crt
    BC_DB_URI=mongodb://admin:PASSWORD@mongo:27017/bookcars?authSource=admin&appName=bookcars
    BC_DB_SSL=false
    BC_DB_SSL_CERT=/etc/ssl/bookcars.crt
    BC_DB_SSL_CA=/etc/ssl/bookcars.ca.pem
    BC_DB_DEBUG=false
    BC_COOKIE_SECRET=COOKIE_SECRET
    BC_AUTH_COOKIE_DOMAIN=localhost
    BC_JWT_SECRET=JWT_SECRET
    BC_JWT_EXPIRE_AT=86400
    BC_TOKEN_EXPIRE_AT=86400
    BC_SMTP_HOST=in-v3.mailjet.com
    BC_SMTP_PORT=587
    BC_SMTP_USER=USER
    BC_SMTP_PASS=PASSWORD
    BC_SMTP_FROM=admin@bookcars.ma
    BC_CDN_USERS=/var/www/cdn/bookcars/users
    BC_CDN_TEMP_USERS=/var/www/cdn/bookcars/temp/users
    BC_CDN_CARS=/var/www/cdn/bookcars/cars
    BC_CDN_TEMP_CARS=/var/www/cdn/bookcars/temp/cars
    BC_DEFAULT_LANGUAGE=en
    BC_BACKEND_HOST=http://localhost:3001/
    BC_FRONTEND_HOST=http://localhost/
    BC_MINIMUM_AGE=21
    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN

    Set the following options:

    BC_DB_URI=mongodb://admin:PASSWORD@mongo:27017/bookcars?authSource=admin&appName=bookcars
    BC_COOKIE_SECRET=COOKIE_SECRET
    BC_AUTH_COOKIE_DOMAIN=localhost
    BC_JWT_SECRET=JWT_SECRET
    BC_SMTP_HOST=in-v3.mailjet.com
    BC_SMTP_PORT=587
    BC_SMTP_USER=USER
    BC_SMTP_PASS=PASSWORD
    BC_SMTP_FROM=admin@bookcars.com
    BC_BACKEND_HOST=http://localhost:3001/
    BC_FRONTEND_HOST=http://localhost/

    Replace PASSWORD in BC_DB_URI with a strong password of your choice and JWT_SECRET with a secret token. Finally, set the SMTP options. SMTP options are necessary for sign up. You can use mailjet, brevo, sendgrid or any other transactional email provider.

    COOKIE_SECRET and JWT_SECRET should at least be 32 characters long, but the longer the better. You can use an online password generator and set the password length to 32 or longer.

    Leave localhost if you want to test locally or Replace it with an IP, hostname or FQDN.

    If you want to enable push notifications in the mobile app, follow these instructions and set the following option:

    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN
  3. Create ./backend/.env.docker file with the following content:
    REACT_APP_NODE_ENV=production
    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_DEFAULT_LANGUAGE=en
    REACT_APP_BC_PAGE_SIZE=30
    REACT_APP_BC_CARS_PAGE_SIZE=15
    REACT_APP_BC_BOOKINGS_PAGE_SIZE=20
    REACT_APP_BC_BOOKINGS_MOBILE_PAGE_SIZE=10
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_TEMP_USERS=http://localhost/cdn/bookcars/temp/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_CDN_TEMP_CARS=http://localhost/cdn/bookcars/temp/cars
    REACT_APP_BC_COMPANY_IMAGE_WIDTH=60
    REACT_APP_BC_COMPANY_IMAGE_HEIGHT=30
    REACT_APP_BC_CAR_IMAGE_WIDTH=300
    REACT_APP_BC_CAR_IMAGE_HEIGHT=200
    REACT_APP_BC_MINIMUM_AGE=21
    REACT_APP_BC_PAGINATION_MODE=classic

    Set the following options:

    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_TEMP_USERS=http://localhost/cdn/bookcars/temp/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_CDN_TEMP_CARS=http://localhost/cdn/bookcars/temp/cars

    Leave localhost if you want to test locally or Replace it with an IP, hostname or FQDN.

    If you want to change the pagination mode, change REACT_APP_BC_PAGINATION_MODE option. You can choose between classic or infinite_scroll. This option defaults to classic. If you choose classic, you will get a classic pagination with next and previous buttons on desktop and infinite scroll on mobile. If you choose infinite_scroll, you will get infinite scroll on desktop and mobile.

  4. Create ./frontend/.env.docker file with the following content:
    REACT_APP_NODE_ENV=production
    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_RECAPTCHA_ENABLED=false
    REACT_APP_BC_DEFAULT_LANGUAGE=en
    REACT_APP_BC_PAGE_SIZE=30
    REACT_APP_BC_CARS_PAGE_SIZE=15
    REACT_APP_BC_BOOKINGS_PAGE_SIZE=20
    REACT_APP_BC_BOOKINGS_MOBILE_PAGE_SIZE=10
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_COMPANY_IMAGE_WIDTH=60
    REACT_APP_BC_COMPANY_IMAGE_HEIGHT=30
    REACT_APP_BC_CAR_IMAGE_WIDTH=300
    REACT_APP_BC_CAR_IMAGE_HEIGHT=200
    REACT_APP_BC_MINIMUM_AGE=21
    REACT_APP_BC_PAGINATION_MODE=classic

    Set the following options:

    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars

    Leave localhost if you want to test locally or Replace it with an IP, hostname or FQDN.

    If you want to change pagination mode, change REACT_APP_BC_PAGINATION_MODE option.

    reCAPTCHA is by default disabled on the frontend. If you want to enable it, you have to set REACT_APP_BC_RECAPTCHA_ENABLED to true and REACT_APP_BC_RECAPTCHA_SITE_KEY to Google reCAPTCHA site key.

  5. Open ./docker-compose.yml and set MongoDB password:
    version: "3.8"
    services:
      api:
        build: 
          context: .
          dockerfile: ./api/Dockerfile
        env_file: ./api/.env.docker
        restart: always
        ports:
          - 4002:4002
        depends_on:
          - mongo
        volumes:
          - cdn:/var/www/cdn/bookcars
    
      mongo:
        image: mongo:latest
        command: mongod --quiet --logpath /dev/null
        restart: always
        environment:
          # Provide your credentials here
          MONGO_INITDB_ROOT_USERNAME: admin
          MONGO_INITDB_ROOT_PASSWORD: PASSWORD
        ports:
          - 27017:27017
    
      backend:
        build: 
          context: .
          dockerfile: ./backend/Dockerfile
        depends_on:
          - api
        ports:
          - 3001:3001
    
      frontend:
        build: 
          context: .
          dockerfile: ./frontend/Dockerfile
        depends_on:
          - api
        ports:
          - 80:80
        volumes:
          - cdn:/var/www/cdn/bookcars
    
    volumes:
      cdn:

    Replace PASSWORD with the password that you have set in BC_DB_URI in ./api/.env.docker.

  6. Build and run Docker image:
    Shell
    sudo docker compose up

    To run the compose in background, add the -d option with the command:

    Shell
    sudo docker compose up -d

    If you want to rebuild, use the following commands:

    Shell
    sudo docker compose build --no-cache
    sudo docker compose up

    If you want to check the logs of the containers for troubleshooting, use the following command:

    Shell
    sudo docker compose logs

    If you want to stop and remove containers, volumes, networks, and images created by the compose, and start from a clean image, use the following commands:

    Shell
    sudo docker compose down --volumes
    sudo docker compose build --no-cache
    sudo docker compose up

    Be careful when using the commands above. MongoDB database and cdn content will be deleted. So make sure you backup the database and cdn content before to proceed.

That's it! BookCars backend is accessible from http://<hostname>:3001 and BookCars frontend is accessible from http://<hostname>.

If you run BookCars for the first time, you'll start from an empty database. So you have to create an admin from the backend by filling the from http://<hostname>:3001/sign-up. SMTP settings need to be configured to process sign up. Then, secure the backend by opening backend/src/App.tsx and commenting the following lines:

const SignUp = lazy(() => import('./pages/SignUp'))
<Route exact path='/sign-up' element={<Signup />} />

You'll need to rebuild and run Docker image:

Shell
sudo docker compose build --no-cache
sudo docker compose up

Once you create the admin user, do the following:

  • Go to the suppliers page and create one or multiple suppliers
  • Go to the locations page and create one or multiple locations
  • Go to the cars page and create one or multiple cars
  • Go to the frontend, sign up, choose a car and checkout.

Finally, you will see bookings listed in the backend dashboard.

You can use the demo database if you want.

Below are Docker configuration files:

That's it. You can explore the other pages in the backend and the frontend.

SSL

This section will walk you through how to enable SSL in the API, the backend and the frontend in a docker container.

Copy your private key bookcars.key and your certificate bookcars.crt in ./.

bookcars.key will be loaded as /etc/ssl/bookcars.key and bookcars.crt will be loaded as /etc/ssl/bookcars.crt in ./docker-compose.yml.

API

For the API, update ./api/.env.docker as follow to enable SSL:

BC_HTTPS=true
BC_PRIVATE_KEY=/etc/ssl/bookcars.key
BC_CERTIFICATE=/etc/ssl/bookcars.crt
BC_BACKEND_HOST=http://localhost:3001/
BC_FRONTEND_HOST=http://localhost/

Replace http://localhost with https://<fqdn>.

Backend

For the backend, update the following options in ./backend/.env.docker:

REACT_APP_BC_API_HOST=http://localhost:4002
REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
REACT_APP_BC_CDN_TEMP_USERS=http://localhost/cdn/bookcars/temp/users
REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
REACT_APP_BC_CDN_TEMP_CARS=http://localhost/cdn/bookcars/temp/cars

Replace http://localhost with https://<fqdn>.

Then, update ./backend/nginx.conf as follow to enable SSL:

server {
    listen 3001 ssl;
    root /usr/share/nginx/html;
    index index.html;

    ssl_certificate_key /etc/ssl/bookcars.key;
    ssl_certificate /etc/ssl/bookcars.crt;

    error_page 497 301 =307 https://$host:$server_port$request_uri;

    access_log /var/log/nginx/backend.access.log;
    error_log /var/log/nginx/backend.error.log;

    location / {
        # First attempt to serve request as file, then as directory,
        # then as index.html, then fall back to displaying a 404.
        try_files $uri $uri/ /index.html =404;
    }
}

Frontend

For the frontend, update the following options in ./frontend/.env.docker:

REACT_APP_BC_API_HOST=http://localhost:4002
REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars

Replace http://localhost with https://<fqdn>.

Add the port 443 to ./frontend/Dokerfile as follow:

# syntax=docker/dockerfile:1

FROM node:lts-alpine as build
WORKDIR /frontend
COPY . .
COPY ./.env.docker ./.env
RUN npm install
RUN npm run build

FROM nginx:stable-alpine
COPY --from=build /frontend/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80
EXPOSE 443

Then, update ./frontend/nginx.conf as follow to enable SSL:

server {
    listen 80;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    root /usr/share/nginx/html;
    index index.html;

    ssl_certificate_key /etc/ssl/bookcars.key;
    ssl_certificate /etc/ssl/bookcars.crt;

    access_log /var/log/nginx/frontend.access.log;
    error_log /var/log/nginx/frontend.error.log;

    location / {
      # First attempt to serve request as file, then as directory,
      # then as index.html, then fall back to displaying a 404.
      try_files $uri $uri/ /index.html =404;
    }

    location /cdn {
      alias /var/www/cdn;
    }
}

docker-compose.yml

Update ./docker-compose.yml to load your private key bookcars.key and your certificate bookcars.crt, and add the port 443 to the frontend as follow:

version: "3.8"
services:
  api:
    build: 
      context: .
      dockerfile: ./api/Dockerfile
    env_file: ./api/.env.docker
    restart: always
    ports:
      - 4002:4002
    depends_on:
      - mongo
    volumes:
      - cdn:/var/www/cdn/bookcars
      - ./bookcars.key:/etc/ssl/bookcars.key
      - ./bookcars.crt:/etc/ssl/bookcars.crt

  mongo:
    image: mongo:latest
    restart: always
    environment:
      # Provide your credentials here
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: PASSWORD
    ports:
      - 27017:27017

  backend:
    build: 
      context: .
      dockerfile: ./backend/Dockerfile
    depends_on:
      - api
    ports:
      - 3001:3001
    volumes:
      - ./bookcars.key:/etc/ssl/bookcars.key
      - ./bookcars.crt:/etc/ssl/bookcars.crt

  frontend:
    build: 
      context: .
      dockerfile: ./frontend/Dockerfile
    depends_on:
      - api
    ports:
      - 80:80
      - 443:443
    volumes:
      - cdn:/var/www/cdn/bookcars
      - ./bookcars.key:/etc/ssl/bookcars.key
      - ./bookcars.crt:/etc/ssl/bookcars.crt

volumes:
  cdn:

Rebuild and run Docker image:

Shell
sudo docker compose build --no-cache
sudo docker compose up

Change Currency

To change the currency, follow the instructions below:

Frontend

Open frontend/src/lang/common.ts and change en.CURRENCY and fr.CURRENCY.

Backend

Open backend/src/lang/common.ts and change en.CURRENCY and fr.CURRENCY.

Mobile App

Open mobile/lang/en.ts and change en.CURRENCY, en.CAR_CURRENCY.

Open mobile/lang/fr.ts and change fr.CURRENCY, fr.CAR_CURRENCY.

Add New Language

To add a new language proceed as follow:

Backend and frontend

  1. Add the new language ISO 639-1 code and its label in src/config/env.config.ts in LANGUAGES constant.
  2. Add the translations in src/lang/*.ts.

Mobile App

  1. Add the new language ISO 639-1 code and its label in config/env.config.ts in LANGUAGES constant.
  2. Create a new file <ISO 639-1 code>.ts in lang folder and add the translations in it.
  3. Add your translations to lang/i18n.ts

Demo Database

Windows, Linux and macOS

  • Download and install MongoDB Command Line Database Tools.
  • On Windows, add MongoDB Command Line Database Tools folder to Path environment variable.
  • Download bookcars-db.zip down to your machine.
  • Restore BookCars demo db by using the following command:
    Shell
    mongorestore --verbose --drop --gzip --host=127.0.0.1 --port=27017 --username=admin --password=$PASSWORD --authenticationDatabase=admin --nsInclude="bookcars.*" --archive=bookcars.gz

Replace $PASSWORD with your MongoDB password.

Copy the content of cdn folder on your web server so that the files will be accessible through http://localhost/cdn/bookcars/

cdn folder contains the following folders:

  • users: This folder contains users’ avatars and suppliers’ images.
  • cars: This folder contains cars’ images.
  • temp: This folder contains temporay files.

If you want to run BookCars from the source code or install it on Windows or Linux without using Docker, proceed as follow:

  • On Windows, install IIS and copy the content of cdn folder in C:\inetpub\wwwroot\cdn\bookcars. Finally, add full access permissions to the user who is running BookCars API on C:\inetpub\wwwroot\cdn\bookcars.
  • On Linux, install NGINX and copy content of cdn folder in /var/www/cdn/bookcars. Then, update /etc/nginx/sites-enabled/default as follow:
    server {
        listen 80 default_server;
        server_name _;
        
        ...
    
        location /cdn {
          alias /var/www/cdn;
        }
    }

    Finally, add full access permissions to the user who is running BookCars API on /var/www/cdn/bookcars.

Backend credentials:

Frontend and mobile app credentials:

Docker

To restore BookCars demo database in Docker containers, proceed as follow:

  1. Make sure that the ports 80, 3001, 4002 and 27017 are not used by any application.
  2. Download and install MongoDB Command Line Database Tools on your local machine.
  3. Add MongoDB Command Line Database Tools folder to Path environment variable in your local machine.
  4. Download bookcars-db.zip down to your local machine and unzip it.
  5. Run the compose:
    docker compose up
  6. Go to bookcars-db folder and restore the demo database with the following command:
    mongorestore --verbose --drop --gzip --host=127.0.0.1 --port=27017 --username=admin --password=$PASSWORD --authenticationDatabase=admin --nsInclude="bookcars.*" --archive=bookcars.gz
    Replace $PASSWORD with your MongoDB password set in your docker-compose.yml
  7. Get API Docker container name with the following command:
    docker container ls
    The name should be something like this: src-api-1
  8. Go to bookcars-db/cdn folder and copy the content of the folder in API container with the following commands:
    docker cp ./cdn/users src-api-1:/var/www/cdn/bookcars
    docker cp ./cdn/cars src-api-1:/var/www/cdn/bookcars
    Replace src-api-1 with your API container name.
  9. Go to the backend http://localhost:3001 and login with the following credentials:
    Username: admin@bookcars.ma
    Password: B00kC4r5
  10. Go to the frontend http://localhost and login with the following credentials:
    Username: jdoe@bookcars.ma
    Password: B00kC4r5

Build Mobile App

Prerequisites

To build BookCars the mobile app, you need to have the following tools installed on your machine:

Install eas-cli with the following command:

Shell
npm i -g eas-cli

Configuration

  • You need to download the google-services.json file and place it in ./mobile root directory for push notifications. Otherwise, the mobile app won't build. Don't forget to set Firebase Server key in expo.dev > Credentials > Service Credentials > Google Cloud Messaging Token as stated in the documentation.

  • If you don't have an Expo account, you'll need to create one to build BookCars mobile app.

  • Go to expo.dev, Click on Pojects then Create a Project. Set BookCars as project name and click on Create.

  • Go to BookCars project and copy the project ID. Open ./mobile/app.json and paste the project ID in extra.eas.projectId.

  • Create an Expo Access Token from expo.dev (Account Settings > Access Tokens) and set api/.env BC_EXPO_ACCESS_TOKEN setting:

    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN
  • Create mobile/.env file with the following content:
    BC_API_HOST=https://bookcars.com:4002
    BC_DEFAULT_LANGUAGE=en
    BC_PAGE_SIZE=20
    BC_CARS_PAGE_SIZE=8
    BC_BOOKINGS_PAGE_SIZE=8
    BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
    BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars
    BC_COMPANY_IMAGE_WIDTH=60
    BC_COMPANY_IMAGE_HEIGHT=30
    BC_CAR_IMAGE_WIDTH=300
    BC_CAR_IMAGE_HEIGHT=200
    BC_MINIMUM_AGE=21

    You need to configure the following options:

    BC_API_HOST=https://bookcars.com:4002
    BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
    BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars

    Replace https://bookcars.com with an IP, hostname or FQDN.

Production Build

If you want to use BookCars mobile app on production, you should use HTTPS in BookCars API and disable usesCleartextTraffic expo plugin in ./mobile/app.json by removing the line "./plugins/usesCleartextTraffic" in plugins section.

Instructions

  • Clone the source down to your machine:
    Shell
    git clone https://github.com/aelassas/bookcars.git
  • Go to mobile folder:
    Shell
    cd ./mobile
  • Run the following command:
    Shell
    npm install

Android

EAS Build

To build BookCars Android app with EAS Build hosted service, run the following command:

Shell
npm run build:android
Local Build

macOS or Linux are required for local builds. For Windows, you should use EAS Builds.

You need to install Android SDK, JDK 11, and set ANDROID_HOME and JAVA_HOME environment variables. Then, run the following command:

Shell
npm run build:android:local

iOS

EAS Build

To build BookCars iOS app, run the following command:

Shell
npm run build:ios
Local Build

You need to install fastlane and CocoaPods on macOS.

To build BookCars iOS app locally, run the following command:

npm run build:ios:local

Run from Source

Below are the instructions to run BookCars from source code.

Prerequisites

  1. Install git, Node.js, NGINX or IIS on Windows, MongoDB and mongosh.

  2. Configure MongoDB:

    Shell
    mongosh

    Create admin user:

    db = db.getSiblingDB('admin')
    db.createUser({ user: "admin" , pwd: "PASSWORD", 
    roles: ["userAdminAnyDatabase", "dbAdminAnyDatabase", "readWriteAnyDatabase"]})

    Replace PASSWORD with a strong password.

    Secure MongoDB by changing mongod.conf as follow:

    net:
      port: 27017
      bindIp: 0.0.0.0
    
    security:
      authorization: enabled

    Restart MongoDB service.

Instructions

  1. Download BookCars source code down to your machine.
  2. Add api/.env file:
    NODE_ENV=development
    BC_PORT=4002
    BC_HTTPS=false
    BC_PRIVATE_KEY=/etc/ssl/bookcars.com.key
    BC_CERTIFICATE=/etc/ssl/bookcars.com.crt
    BC_DB_URI=mongodb://admin:PASSWORD@127.0.0.1:27017/bookcars?authSource=admin&appName=bookcars
    BC_DB_SSL=false
    BC_DB_SSL_CERT=/etc/ssl/bookcars.com.crt
    BC_DB_SSL_CA=/etc/ssl/bookcars.com.ca.pem
    BC_DB_DEBUG=false
    BC_COOKIE_SECRET=COOKIE_SECRET
    BC_AUTH_COOKIE_DOMAIN=localhost
    BC_JWT_SECRET=JWT_SECRET
    BC_JWT_EXPIRE_AT=86400
    BC_TOKEN_EXPIRE_AT=86400
    BC_SMTP_HOST=in-v3.mailjet.com
    BC_SMTP_PORT=587
    BC_SMTP_USER=USER
    BC_SMTP_PASS=PASSWORD
    BC_SMTP_FROM=admin@bookcars.com
    BC_CDN_USERS=/var/www/cdn/bookcars/users
    BC_CDN_TEMP_USERS=/var/www/cdn/bookcars/temp/users
    BC_CDN_CARS=/var/www/cdn/bookcars/cars
    BC_CDN_TEMP_CARS=/var/www/cdn/bookcars/temp/cars
    BC_DEFAULT_LANGUAGE=en
    BC_BACKEND_HOST=http://localhost:3001/
    BC_FRONTEND_HOST=http://localhost:3002/
    BC_MINIMUM_AGE=21
    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN

    On Windows, install IIS and update the following settings with these values:

    BC_CDN_USERS=C:\inetpub\wwwroot\cdn\bookcars\users
    BC_CDN_TEMP_USERS=C:\inetpub\wwwroot\cdn\bookcars\temp\users
    BC_CDN_CARS=C:\inetpub\wwwroot\cdn\bookcars\cars
    BC_CDN_TEMP_CARS=C:\inetpub\wwwroot\cdn\bookcars\temp\cars

    Add full access permissions to the user who is running BookCars API on C:\inetpub\wwwroot\cdn\bookcars.

    You need to configure the following options:

    BC_DB_URI=mongodb://admin:PASSWORD@127.0.0.1:27017/bookcars?authSource=admin&appName=bookcars
    BC_COOKIE_SECRET=COOKIE_SECRET
    BC_JWT_SECRET=JWT_SECRET
    BC_SMTP_HOST=in-v3.mailjet.com
    BC_SMTP_PORT=587
    BC_SMTP_USER=USER
    BC_SMTP_PASS=PASSWORD
    BC_SMTP_FROM=admin@bookcars.com

    Replace PASSWORD in BC_DB_URI with a strong password of your choice and JWT_SECRET with a secret token. Finally, set the SMTP options. SMTP options are necessary for sign up. You can use mailjet, brevo, sendgrid or any other transactional email provider.

    COOKIE_SECRET and JWT_SECRET should at least be 32 characters long, but the longer the better. You can use an online password generator and set the password length to 32 or longer.

    If you want to enable push notifications in the mobile app, follow these instructions and set the following option:

    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN

    To run the api, use the follwing commands:

    Shell
    cd ./api
    npm install
    npm run dev
  3. Add backend/.env file:
    PORT=3001
    REACT_APP_NODE_ENV=development
    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_DEFAULT_LANGUAGE=en
    REACT_APP_BC_PAGE_SIZE=30
    REACT_APP_BC_CARS_PAGE_SIZE=15
    REACT_APP_BC_BOOKINGS_PAGE_SIZE=20
    REACT_APP_BC_BOOKINGS_MOBILE_PAGE_SIZE=10
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_TEMP_USERS=http://localhost/cdn/bookcars/temp/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_CDN_TEMP_CARS=http://localhost/cdn/bookcars/temp/cars
    REACT_APP_BC_COMPANY_IMAGE_WIDTH=60
    REACT_APP_BC_COMPANY_IMAGE_HEIGHT=30
    REACT_APP_BC_CAR_IMAGE_WIDTH=300
    REACT_APP_BC_CAR_IMAGE_HEIGHT=200
    REACT_APP_BC_MINIMUM_AGE=21
    REACT_APP_BC_PAGINATION_MODE=classic

    To run the backend, use the follwing commands:

    Shell
    cd ./backend
    npm install
    npm start
  4. Add frontend/.env file:
    PORT=3002
    REACT_APP_NODE_ENV=development
    REACT_APP_BC_API_HOST=http://localhost:4002
    REACT_APP_BC_RECAPTCHA_ENABLED=false
    REACT_APP_BC_RECAPTCHA_SITE_KEY=GOOGLE_RECAPTCHA_SITE_KEY
    REACT_APP_BC_DEFAULT_LANGUAGE=en
    REACT_APP_BC_PAGE_SIZE=30
    REACT_APP_BC_CARS_PAGE_SIZE=15
    REACT_APP_BC_BOOKINGS_PAGE_SIZE=20
    REACT_APP_BC_BOOKINGS_MOBILE_PAGE_SIZE=10
    REACT_APP_BC_CDN_USERS=http://localhost/cdn/bookcars/users
    REACT_APP_BC_CDN_CARS=http://localhost/cdn/bookcars/cars
    REACT_APP_BC_COMPANY_IMAGE_WIDTH=60
    REACT_APP_BC_COMPANY_IMAGE_HEIGHT=30
    REACT_APP_BC_CAR_IMAGE_WIDTH=300
    REACT_APP_BC_CAR_IMAGE_HEIGHT=200
    REACT_APP_BC_MINIMUM_AGE=21
    REACT_APP_BC_PAGINATION_MODE=classic

    reCAPTCHA is by default disabled. If you want to enable it, you have to set REACT_APP_BC_RECAPTCHA_ENABLED to true and REACT_APP_BC_RECAPTCHA_SITE_KEY to Google reCAPTCHA site key.

    To run the frontend, use the follwing commands:

    Shell
    cd ./frontend
    npm install
    npm start
  5. If you want to run the mobile app, you need to add mobile/.env:
    BC_API_HOST=https://bookcars.com:4002
    BC_DEFAULT_LANGUAGE=en
    BC_PAGE_SIZE=20
    BC_CARS_PAGE_SIZE=8
    BC_BOOKINGS_PAGE_SIZE=8
    BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
    BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars
    BC_COMPANY_IMAGE_WIDTH=60
    BC_COMPANY_IMAGE_HEIGHT=30
    BC_CAR_IMAGE_WIDTH=300
    BC_CAR_IMAGE_HEIGHT=200
    BC_MINIMUM_AGE=21

    You need to configure the following options:

    BC_API_HOST=https://bookcars.com:4002
    BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
    BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars

    You need to replace https://bookcars.com with an IP or hostname.

  6. Configure http://localhost/cdn
    • On Windows, install IIS, create C:\inetpub\wwwroot\cdn\bookcars folder and add full access permissions to the user who is running BookCars API on bookcarsC:\inetpub\wwwroot\cdn\bookcars folder.
    • On Linux, install NGINX, create /var/www/cdn/bookcars folder, add full access permissions to the user who is running BookCars API on /var/www/cdn/bookcars folder, and add cdn folder to NGINX by changing /etc/nginx/sites-available/default as follow:
    server {
        listen 80 default_server;
        server_name _;
        
        ...
    
        location /cdn {
          alias /var/www/cdn;
        }
    }
  7. Create an admin user from http://localhost:3001/sign-up
  8. To run the mobile app, simply download Expo app on your device and run the following commands from ./mobile folder:
    Shell
    npm install
    npm start

You need to download the google-services.json file and place it in ./mobile root directory for push notifications.

You can find detailed instructions about running the mobile app here.

To change the currency, follow these instructions.

Run Mobile App

To run the mobile app, create ./mobile/.env file with the following options:

BC_API_HOST=https://bookcars.com:4002
BC_DEFAULT_LANGUAGE=en
BC_PAGE_SIZE=20
BC_CARS_PAGE_SIZE=8
BC_BOOKINGS_PAGE_SIZE=8
BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars
BC_COMPANY_IMAGE_WIDTH=60
BC_COMPANY_IMAGE_HEIGHT=30
BC_CAR_IMAGE_WIDTH=300
BC_CAR_IMAGE_HEIGHT=200
BC_MINIMUM_AGE=21

You need to configure the following options:

BC_API_HOST=https://bookcars.com:4002
BC_CDN_USERS=https://bookcars.com/cdn/bookcars/users
BC_CDN_CARS=https://bookcars.com/cdn/bookcars/cars

Replace https://bookcars.com with an IP, hostname or FQDN.

Install demo database by following these instructions.

Configure http://localhost/cdn

​​On Windows, install IIS and add full access permissions to the user who is running BookCars API on C:\inetpub\wwwroot\cdn\bookcars.

On Linux, install NGINX and update /etc/nginx/sites-enabled/default as follow:

server {
    listen 80 default_server;
    server_name _;
    
    ...

    location /cdn {
      alias /var/www/cdn;
    }
}

Finally, add full access permissions to the user who is running BookCars API on /var/www/cdn/bookcars.

Configure ./api by following these instructions.

Run ./api with the following command:

Shell
cd ./api
npm run dev

Run mobile app by simply downloading Expo app on your device and running the following commands from ./mobile folder:

Shell
cd ./mobile
npm install
npm start

Open Expo app on your device and scan the QR code to run BookCars mobile app.

Push Notifications

If you want to enable BookCars push notifications, Download google-services.json file and place it in ./mobile root directory for push notifications. Its path can be configured from ./mobile/app.json configuration file through googleServicesFile setting option. Don't forget to generate a new private key in the Firebase Console and upload the JSON file to Expo dashboard.

Unit Tests and Coverage

Below are the instructions to run unit tests and build coverage report.

Prerequisites

  1. Install git, Node.js, NGINX or IIS, MongoDB and mongosh.

  2. Configure MongoDB:

    Shell
    mongosh
  3. Create admin user:

    db = db.getSiblingDB('admin')
    db.createUser({ user: "admin" , pwd: "PASSWORD", roles: ["userAdminAnyDatabase", "dbAdminAnyDatabase", "readWriteAnyDatabase"]})

    Replace PASSWORD with a strong password.

  4. Secure MongoDB by changing mongod.conf as follows:

    net:
    	port: 27017
    	bindIp: 0.0.0.0
    
    security:
    	authorization: enabled
  5. Restart MongoDB service.

Instructions

  1. Clone Movin' In repo:
    Shell
    git clone https://github.com/aelassas/bookcars.git
  1. Create api/.env file with the following content:
    NODE_ENV=development
    BC_PORT=4002
    BC_HTTPS=false
    BC_PRIVATE_KEY=/etc/ssl/bookcars.com.key
    BC_CERTIFICATE=/etc/ssl/bookcars.com.crt
    BC_DB_URI=mongodb://admin:PASSWORD@127.0.0.1:27017/bookcars?authSource=admin&appName=bookcars
    BC_DB_SSL=false
    BC_DB_SSL_CERT=/etc/ssl/bookcars.com.crt
    BC_DB_SSL_CA=/etc/ssl/bookcars.com.ca.pem
    BC_DB_DEBUG=false
    BC_COOKIE_SECRET=COOKIE_SECRET
    BC_AUTH_COOKIE_DOMAIN=localhost
    BC_JWT_SECRET=JWT_SECRET
    BC_JWT_EXPIRE_AT=86400
    BC_TOKEN_EXPIRE_AT=86400
    BC_SMTP_HOST=in-v3.mailjet.com
    BC_SMTP_PORT=587
    BC_SMTP_USER=USER
    BC_SMTP_PASS=PASSWORD
    BC_SMTP_FROM=admin@bookcars.com
    BC_CDN_USERS=/var/www/cdn/bookcars/users
    BC_CDN_TEMP_USERS=/var/www/cdn/bookcars/temp/users
    BC_CDN_CARS=/var/www/cdn/bookcars/cars
    BC_CDN_TEMP_CARS=/var/www/cdn/bookcars/temp/cars
    BC_DEFAULT_LANGUAGE=en
    BC_BACKEND_HOST=http://localhost:3001/
    BC_FRONTEND_HOST=http://localhost:3002/
    BC_MINIMUM_AGE=21
    BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN

On Windows, install IIS and update the following settings with these values:

BC_CDN_USERS=C:\inetpub\wwwroot\cdn\bookcars\users
BC_CDN_TEMP_USERS=C:\inetpub\wwwroot\cdn\bookcars\temp\users
BC_CDN_CARS=C:\inetpub\wwwroot\cdn\bookcars\cars
BC_CDN_TEMP_CARS=C:\inetpub\wwwroot\cdn\bookcars\temp\cars

Add full access permissions to the user who is running Movin' In API on C:\inetpub\wwwroot\cdn\bookcars.

Then, set the following options:

BC_DB_URI=mongodb://admin:PASSWORD@127.0.0.1:27017/bookcars?authSource=admin&appName=bookcars
BC_COOKIE_SECRET=COOKIE_SECRET
BC_JWT_SECRET=JWT_SECRET
BC_SMTP_HOST=in-v3.mailjet.com
BC_SMTP_PORT=587
BC_SMTP_USER=USER
BC_SMTP_PASS=PASSWORD
BC_SMTP_FROM=admin@bookcars.com

Replace PASSWORD in BC_DB_URI with MongoDB password and JWT_SECRET with a secret token. Finally, set the SMTP options. SMTP options are necessary for sign up. You can use mailjet, brevo, sendgrid or any other transactional email provider.

COOKIE_SECRET and JWT_SECRET should at least be 32 characters long, but the longer the better. You can use an online password generator and set the password length to 32 or longer.

If you want to enable push notifications in the mobile app, follow these instructions and set the following option:

BC_EXPO_ACCESS_TOKEN=EXPO_ACCESS_TOKEN

Run Movin' In API unit tests:

Shell
cd ./api
npm install
npm test

Coverage

Once you run unit tests, a coverage report is automatically built in:

./api/coverage

You can also view the coverage report on codecov.

Using the Code

Image 39

This section describes the software architecture of BookCars including the API, the frontend, the mobile app and the backend.

BookCars API is a Node.js server application that exposes a RESTful API using Express which gives access to BookCars MongoDB database.

BookCars frontend is a React web application that is the main web interface for booking cars.

Bookcars backend is a React web application that lets admins and suppliers manage car fleets, bookings and customers.

BookCars mobile app is a React Native application that is the main mobile app for booking cars.

A key design decision was made to use TypeScript instead of JavaScript due to its numerous advantages. TypeScript offers strong typing, tooling, and integration, resulting in high-quality, scalable, more readable and maintainable code that is easy to debug and test.

API

Image 40

BookCars API exposes all BookCars functions needed for the backend, the frontend and the mobile app. The API follow the MVC design pattern. JWT is used for authentication. There are some functions that need authentication such as functions related to managing cars, bookings and customers, and others that do not need authentication such as retrieving locations and available cars for non authenticated users.

  • ./api/src/models/ folder contains MongoDB models.
  • ./api/src/routes/ folder contains Express routes.
  • ./api/src/controllers/ folder contains controllers.
  • ./api/src/middlewares/ folder contains middlewares.
  • ./api/src/config/env.config.ts contains the configuration and TypeScript type definitions.
  • ./api/src/lang/ folder contains localization.
  • ./api/src/app.ts is the main server where routes are loaded.
  • ./api/index.ts is the main entry point of BookCars API.

index.ts

index.ts is the main entry point of BookCars API:

TypeScript
import 'dotenv/config'
import process from 'node:process'
import fs from 'node:fs/promises'
import http from 'node:http'
import https from 'node:https'
import * as env from './config/env.config'
import * as DatabaseHelper from './common/DatabaseHelper'
import app from './app'

if (await DatabaseHelper.Connect(env.DB_DEBUG)) {
    let server: http.Server | https.Server

    if (env.HTTPS) {
        https.globalAgent.maxSockets = Number.POSITIVE_INFINITY
        const privateKey = await fs.readFile(env.PRIVATE_KEY, 'utf8')
        const certificate = await fs.readFile(env.CERTIFICATE, 'utf8')
        const credentials = { key: privateKey, cert: certificate }
        server = https.createServer(credentials, app)

        server.listen(env.PORT, () => {
            console.log('HTTPS server is running on Port', env.PORT)
        })
    } else {
        server = app.listen(env.PORT, () => {
            console.log('HTTP server is running on Port', env.PORT)
        })
    }

    const close = () => {
        console.log('\nGracefully stopping...')
        server.close(async () => {
            console.log(`HTTP${env.HTTPS ? 'S' : ''} server closed`)
            await DatabaseHelper.Close(true)
            console.log('MongoDB connection closed')
            process.exit(0)
        })
    }

    ['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach((signal) => process.on(signal, close))
}

This is a TypeScript file that starts a server using Node.js and Express. It imports several modules including dotenv, process, fs, http, https, mongoose, and app. It then checks if the HTTPS environment variable is set to true, and if so, creates an HTTPS server using the https module and the provided private key and certificate. Otherwise, it creates an HTTP server using the http module. The server listens on the port specified in the PORT environment variable.

The close function is defined to gracefully stop the server when a termination signal is received. It closes the server and the MongoDB connection, and then exits the process with a status code of 0. Finally, it registers the close function to be called when the process receives a SIGINT, SIGTERM, or SIGQUIT signal.

app.ts

app.ts is in the main server:

TypeScript
import express, { Express } from 'express'
import compression from 'compression'
import helmet from 'helmet'
import nocache from 'nocache'
import cookieParser from 'cookie-parser'
import strings from './config/app.config'
import * as env from './config/env.config'
import cors from './middlewares/cors'
import allowedMethods from './middlewares/allowedMethods'
import supplierRoutes from './routes/supplierRoutes'
import bookingRoutes from './routes/bookingRoutes'
import locationRoutes from './routes/locationRoutes'
import notificationRoutes from './routes/notificationRoutes'
import carRoutes from './routes/carRoutes'
import userRoutes from './routes/userRoutes'
import * as Helper from './common/Helper'

const app: Express = express()

app.use(helmet.contentSecurityPolicy())
app.use(helmet.dnsPrefetchControl())
app.use(helmet.crossOriginEmbedderPolicy())
app.use(helmet.frameguard())
app.use(helmet.hidePoweredBy())
app.use(helmet.hsts())
app.use(helmet.ieNoOpen())
app.use(helmet.noSniff())
app.use(helmet.permittedCrossDomainPolicies())
app.use(helmet.referrerPolicy())
app.use(helmet.xssFilter())
app.use(helmet.originAgentCluster())
app.use(helmet.crossOriginResourcePolicy({ policy: 'cross-origin' }))
app.use(helmet.crossOriginOpenerPolicy())

app.use(nocache())
app.use(compression({ threshold: 0 }))
app.use(express.urlencoded({ limit: '50mb', extended: true }))
app.use(express.json({ limit: '50mb' }))

app.use(cors())
app.options('*', cors())
app.use(cookieParser(env.COOKIE_SECRET))
app.use(allowedMethods)

app.use('/', supplierRoutes)
app.use('/', bookingRoutes)
app.use('/', locationRoutes)
app.use('/', notificationRoutes)
app.use('/', carRoutes)
app.use('/', userRoutes)

strings.setLanguage(env.DEFAULT_LANGUAGE)

Helper.mkdir(env.CDN_USERS)
Helper.mkdir(env.CDN_TEMP_USERS)
Helper.mkdir(env.CDN_CARS)
Helper.mkdir(env.CDN_TEMP_CARS)

export default app

First of all, we retrieve MongoDB connection string, then we establish a connection with BookCars MongoDB database. Then we create an Express app and load middlewares such as cors, compression, helmet, and nocache. We set up various security measures using the helmet middleware library. we also import various route files for different parts of the application such as supplierRoutes, bookingRoutes, locationRoutes, notificationRoutes, carRoutes, and userRoutes. Finally, we load Express routes and export app.

Routes

There are six routes in BookCars API. Each route has its own controller following the MVC design pattern and SOLID principles. Below are the main routes:

  • userRoutes: Provides REST functions related to users
  • companyRoutes: Provides REST functions related to suppliers
  • locationRoutes: Provides REST functions related to locations
  • carRoutes: Provides REST functions related to cars
  • bookingRoutes: Provides REST functions related to bookings
  • notificationRoutes: Provides REST functions related to notifications

We are not going to explain each route one by one. We'll take, for example, locationRoutes and see how it was made:

TypeScript
import express from 'express'
import routeNames from '../config/locationRoutes.config'
import authJwt from '../middlewares/authJwt'
import * as locationController from '../controllers/locationController'

const routes = express.Router()

routes
.route(routeNames.validate)
.post(authJwt.verifyToken, locationController.validate)

routes
.route(routeNames.create)
.post(authJwt.verifyToken, locationController.create)

routes
.route(routeNames.update)
.put(authJwt.verifyToken, locationController.update)

routes
.route(routeNames.delete)
.delete(authJwt.verifyToken, locationController.deleteLocation)

routes
.route(routeNames.getLocation)
.get(locationController.getLocation)

routes
.route(routeNames.getLocations)
.get(locationController.getLocations)

routes
.route(routeNames.checkLocation)
.get(authJwt.verifyToken, locationController.checkLocation)

export default routes

First of all, we create an Express Router. Then, we create the routes using their name, method, middlewares and controllers.

routeNames contains locationRoutes route names:

TypeScript
export default {
    validate: '/api/validate-location',
    create: '/api/create-location',
    update: '/api/update-location/:id',
    delete: '/api/delete-location/:id',
    getLocation: '/api/location/:id/:language',
    getLocations: '/api/locations/:page/:size/:language',
    checkLocation: '/api/check-location/:id',
}

locationController contains the main business logic regarding locations. We are not going to see all the source code of the controller since it's quite large but we'll take create and getLocations controller functions for example.

Below is Location model:

TypeScript
import { Schema, model } from 'mongoose'
import * as env from '../config/env.config'

const locationSchema = new Schema<env.Location>(
  {
    values: {
      type: [Schema.Types.ObjectId],
      ref: 'LocationValue',
      validate: (value: any): boolean => Array.isArray(value) && value.length > 1,
    },
  },
  {
    timestamps: true,
    strict: true,
    collection: 'Location',
  },
)

const locationModel = model<env.Location>('Location', locationSchema)

locationModel.on('index', (err) => {
  if (err) {
    console.error('Location index error: %s', err)
  } else {
    console.info('Location indexing complete')
  }
})

export default locationModel

Below is env.Location TypeScript type:

TypeScript
export interface Location extends Document {
    values: Types.ObjectId[]
    name?: string
}

A Location has multiple values. One per language. By default, English and French languages are supported.

Below is LocationValue model:

TypeScript
import { Schema, model } from 'mongoose'
import * as env from '../config/env.config'

const locationValueSchema = new Schema<env.LocationValue>(
  {
    language: {
      type: String,
      required: [true, "can't be blank"],
      index: true,
      trim: true,
      lowercase: true,
      minLength: 2,
      maxLength: 2,
    },
    value: {
      type: String,
      required: [true, "can't be blank"],
      index: true,
      trim: true,
    },
  },
  {
    timestamps: true,
    strict: true,
    collection: 'LocationValue',
  },
)

const locationValueModel = model<env.LocationValue>('LocationValue', locationValueSchema)

locationValueModel.on('index', (err) => {
  if (err) {
    console.error('LocationValue index error: %s', err)
  } else {
    console.info('LocationValue indexing complete')
  }
})

export default locationValueModel

Below is env.LocationValue TypeScript type:

TypeScript
export interface LocationValue extends Document {
    language: string
    value: string
}

A LocationValue has a language code (ISO 639-1) and a string value.

Below is create controller function:

TypeScript
export const create = async (req: Request, res: Response) => {
  const body: bookcarsTypes.LocationName[] = req.body
  const names = body

  try {
    const values = []
    for (let i = 0; i < names.length; i++) {
      const name = names[i]
      const locationValue = new LocationValue({
        language: name.language,
        value: name.name,
      })
      await locationValue.save()
      values.push(locationValue._id)
    }

    const location = new Location({ values })
    await location.save()
    return res.sendStatus(200)
  } catch (err) {
    console.error(`[location.create] ${strings.DB_ERROR} ${req.body}`, err)
    return res.status(400).send(strings.DB_ERROR + err)
  }
}

In this function, we retrieve the body of the request, we iterate through the values provided in the body (one value per language) and we create a LocationValue. Finally, we create the location depending on the created location values.

Below is getLocations controller function:

TypeScript
export const getLocations = async (req: Request, res: Response) => {
  try {
    const page = Number.parseInt(req.params.page)
    const size = Number.parseInt(req.params.size)
    const language = req.params.language
    const keyword = escapeStringRegexp(String(req.query.s || ''))
    const options = 'i'

    const locations = await Location.aggregate(
      [
        {
          $lookup: {
            from: 'LocationValue',
            let: { values: '$values' },
            pipeline: [
              {
                $match: {
                  $and: [
                    { $expr: { $in: ['$_id', '$$values'] } },
                    { $expr: { $eq: ['$language', language] } },
                    {
                      $expr: {
                        $regexMatch: {
                          input: '$value',
                          regex: keyword,
                          options,
                        },
                      },
                    },
                  ],
                },
              },
            ],
            as: 'value',
          },
        },
        { $unwind: { path: '$value', preserveNullAndEmptyArrays: false } },
        { $addFields: { name: '$value.value' } },
        {
          $facet: {
            resultData: [{ $sort: { name: 1 } }, { $skip: (page - 1) * size }, { $limit: size }],
            pageInfo: [
              {
                $count: 'totalRecords',
              },
            ],
          },
        },
      ],
      { collation: { locale: env.DEFAULT_LANGUAGE, strength: 2 } },
    )

    return res.json(locations)
  } catch (err) {
    console.error(`[location.getLocations] ${strings.DB_ERROR} ${req.query.s}`, err)
    return res.status(400).send(strings.DB_ERROR + err)
  }
}

In this controller function, we retrieve locations from the database using aggregate MongoDB function and facet to implement pagination.

Below is another simple route, notificationRoutes:

TypeScript
import express from 'express'
import routeNames from '../config/notificationRoutes.config'
import authJwt from '../middlewares/authJwt'
import * as notificationController from '../controllers/notificationController'

const routes = express.Router()

routes
.route(routeNames.notificationCounter)
.get(authJwt.verifyToken, notificationController.notificationCounter)

routes
.route(routeNames.notify)
.post(authJwt.verifyToken, notificationController.notify)

routes
.route(routeNames.getNotifications)
.get(authJwt.verifyToken, notificationController.getNotifications)

routes
.route(routeNames.markAsRead)
.post(authJwt.verifyToken, notificationController.markAsRead)

routes
.route(routeNames.markAsUnRead)
.post(authJwt.verifyToken, notificationController.markAsUnRead)

routes
.route(routeNames.delete)
.post(authJwt.verifyToken, notificationController.deleteNotifications)

export default routes

Below is the Notification model:

TypeScript
import { Schema, model } from 'mongoose'
import * as env from '../config/env.config'

const notificationSchema = new Schema<env.Notification>(
  {
    user: {
      type: Schema.Types.ObjectId,
      required: [true, "can't be blank"],
      ref: 'User',
      index: true,
    },
    message: {
      type: String,
      required: [true, "can't be blank"],
    },
    booking: {
      type: Schema.Types.ObjectId,
      ref: 'Booking',
    },
    isRead: {
      type: Boolean,
      default: false,
    },
  },
  {
    timestamps: true,
    strict: true,
    collection: 'Notification',
  },
)

const notificationModel = model<env.Notification>('Notification', notificationSchema)

notificationModel.on('index', (err) => {
  if (err) {
    console.error('Notification index error: %s', err)
  } else {
    console.info('Notification indexing complete')
  }
})

export default notificationModel

Below is env.Notification TypeScript type:

TypeScript
export interface Notification extends Document {
    user: Types.ObjectId
    message: string
    booking: Types.ObjectId
    isRead?: boolean
}

A Notification is composed of a reference to a user, a message, a reference to a booking and isRead flag.

Below is getNotifications controller function:

TypeScript
export const getNotifications = async (req: Request, res: Response) => {
  const { userId: _userId, page: _page, size: _size } = req.params

  try {
    const userId = new mongoose.Types.ObjectId(_userId)
    const page = Number.parseInt(_page)
    const size = Number.parseInt(_size)

    const notifications = await Notification.aggregate([
      { $match: { user: userId } },
      {
        $facet: {
          resultData: [{ $sort: { createdAt: -1 } }, { $skip: (page - 1) * size }, { $limit: size }],
          pageInfo: [
            {
              $count: 'totalRecords',
            },
          ],
        },
      },
    ])

    return res.json(notifications)
  } catch (err) {
    console.error(`[notification.getNotifications] ${strings.DB_ERROR} ${_userId}`, err)
    return res.status(400).send(strings.DB_ERROR + err)
  }
}

In this simple controller function, we retrieve notifications using MongoDB aggregate function, page and size parameters.

Below is markAsRead controller function:

TypeScript
export const markAsRead = async (req: Request, res: Response) => {
  try {
    const body: { ids: string[] } = req.body
    const { ids: _ids } = body
    const ids = _ids.map((id) => new mongoose.Types.ObjectId(id))
    const { userId: _userId } = req.params
    const userId = new mongoose.Types.ObjectId(_userId)

    const bulk = Notification.collection.initializeOrderedBulkOp()
    const notifications = await Notification.find({
      _id: { $in: ids },
      isRead: false,
    })
    const length = notifications.length

    bulk.find({ _id: { $in: ids }, isRead: false }).update({ $set: { isRead: true } })
    const result = await bulk.execute()

    if (result.modifiedCount !== length) {
      console.error(`[notification.markAsRead] ${strings.DB_ERROR}`)
      return res.status(400).send(strings.DB_ERROR)
    }
    const counter = await NotificationCounter.findOne({ user: userId })
    if (!counter || typeof counter.count === 'undefined') {
      return res.sendStatus(204)
    }
    counter.count -= length
    await counter.save()

    return res.sendStatus(200)
  } catch (err) {
    console.error(`[notification.markAsRead] ${strings.DB_ERROR}`, err)
    return res.status(400).send(strings.DB_ERROR + err)
  }
}

In this controller function, we bulk update notifications and mark them as read.

Frontend

The frontend is a web application built with Node.js, React, MUI and TypeScript. From the frontend, the customer can search for available cars depending on pickup and drop-off points and time, choose a car and proceed to checkout.

  • ./frontend/src/assets/ folder contains CSS and images.
  • ./frontend/src/pages/ folder contains React pages.
  • ./frontend/src/components/ folder contains React components.
  • ./frontend/src/services/ contains BookCars API client services.
  • ./frontend/src/App.tsx is the main React App that contains routes.
  • ./frontend/src/index.tsx is the main entry point of the frontend.

TypeScript type definitions are defined in the package ./packages/bookcars-types.

App.tsx is the main react App:

TypeScript
import React, { lazy, Suspense } from 'react'
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'

const SignIn = lazy(() => import('./pages/SignIn'))
const SignUp = lazy(() => import('./pages/SignUp'))
const Activate = lazy(() => import('./pages/Activate'))
const ForgotPassword = lazy(() => import('./pages/ForgotPassword'))
const ResetPassword = lazy(() => import('./pages/ResetPassword'))
const Home = lazy(() => import('./pages/Home'))
const Cars = lazy(() => import('./pages/Cars'))
const Checkout = lazy(() => import('./pages/Checkout'))
const Bookings = lazy(() => import('./pages/Bookings'))
const Booking = lazy(() => import('./pages/Booking'))
const Settings = lazy(() => import('./pages/Settings'))
const Notifications = lazy(() => import('./pages/Notifications'))
const ToS = lazy(() => import('./pages/ToS'))
const About = lazy(() => import('./pages/About'))
const ChangePassword = lazy(() => import('./pages/ChangePassword'))
const Contact = lazy(() => import('./pages/Contact'))
const NoMatch = lazy(() => import('./pages/NoMatch'))

const App = () => (
  <Router>
    <div className="App">
      <Suspense fallback={<></>}>
        <Routes>
          <Route path="/sign-in" element={<SignIn />} />
          <Route path="/sign-up" element={<SignUp />} />
          <Route path="/activate" element={<Activate />} />
          <Route path="/forgot-password" element={<ForgotPassword />} />
          <Route path="/reset-password" element={<ResetPassword />} />
          <Route path="/" element={<Home />} />
          <Route path="/cars" element={<Cars />} />
          <Route path="/checkout" element={<Checkout />} />
          <Route path="/bookings" element={<Bookings />} />
          <Route path="/booking" element={<Booking />} />
          <Route path="/settings" element={<Settings />} />
          <Route path="/notifications" element={<Notifications />} />
          <Route path="/change-password" element={<ChangePassword />} />
          <Route path="/about" element={<About />} />
          <Route path="/tos" element={<ToS />} />
          <Route path="/contact" element={<Contact />} />

          <Route path="*" element={<NoMatch />} />
        </Routes>
      </Suspense>
    </div>
  </Router>
  )

export default App

We are using React lazy loading to load each route.

We are not going to cover each page of the frontend, but you can open the source code and see each one if you want.

Mobile App

BookCars provides a native mobile app for Android and iOS. The mobile app is built with React Native, Expo and TypeScript. Like for the frontend, the mobile app allows the customer to search for available cars depending on pickup and drop-off points and time, choose a car and proceed to checkout.

The customer receives push notifications if his booking is updated from the backend. Push notifications are built with Node.js, Expo Server SDK and Firebase.

  • ./mobile/assets/ folder contains images.
  • ./mobile/screens/ folder contains main React Native screens.
  • ./mobile/components/ folder contains React Native components.
  • ./mobile/services/ contains BookCars API client services.
  • ./mobile/App.tsx is the main React Native App.

TypeScript type definitions are defined in:

  • ./mobile/types/index.d.ts
  • ./mobile/types/env.d.ts
  • ./mobile/miscellaneous/bookcarsTypes.ts

./mobile/types/ is loaded in ./mobile/tsconfig.json as follow:

JavaScript
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "typeRoots": [
      "./types"
    ]
  }
}

App.tsx is the main React Native app:

TypeScript
import 'react-native-gesture-handler'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { RootSiblingParent } from 'react-native-root-siblings'
import { NavigationContainer, NavigationContainerRef } from '@react-navigation/native'
import { StatusBar as ExpoStatusBar } from 'expo-status-bar'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { Provider } from 'react-native-paper'
import * as SplashScreen from 'expo-splash-screen'
import * as Notifications from 'expo-notifications'
import DrawerNavigator from './components/DrawerNavigator'
import * as Helper from './common/Helper'
import * as NotificationService from './services/NotificationService'
import * as UserService from './services/UserService'

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
})

// Prevent native splash screen from autohiding before App component declaration
SplashScreen.preventAutoHideAsync()
  .then((result) => console.log(`SplashScreen.preventAutoHideAsync() succeeded: ${result}`))
  .catch(console.warn) // it's good to explicitly catch and inspect any error

const App = () => {
  const [appIsReady, setAppIsReady] = useState(false)
  const responseListener = useRef<Notifications.Subscription>()
  const navigationRef = useRef<NavigationContainerRef<StackParams>>(null)

  useEffect(() => {
    const register = async () => {
      const loggedIn = await UserService.loggedIn()
      if (loggedIn) {
        const currentUser = await UserService.getCurrentUser()
        if (currentUser?._id) {
          await Helper.registerPushToken(currentUser._id)
        } else {
          Helper.error()
        }
      }
    }

    // Register push notifiations token
    register()

    // This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
    responseListener.current = Notifications.addNotificationResponseReceivedListener(async (response) => {
      try {
        if (navigationRef.current) {
          const { data } = response.notification.request.content

          if (data.booking) {
            if (data.user && data.notification) {
              await NotificationService.markAsRead(data.user, [data.notification])
            }
            navigationRef.current.navigate('Booking', { id: data.booking })
          } else {
            navigationRef.current.navigate('Notifications', {})
          }
        }
      } catch (err) {
        Helper.error(err, false)
      }
    })

    return () => {
      if (responseListener.current) {
        Notifications.removeNotificationSubscription(responseListener.current)
      }
    }
  }, [])

  setTimeout(() => {
    setAppIsReady(true)
  }, 500)

  const onReady = useCallback(async () => {
    if (appIsReady) {
      // This tells the splash screen to hide immediately! If we call this after
      // `setAppIsReady`, then we may see a blank screen while the app is
      // loading its initial state and rendering its first pixels. So instead,
      // we hide the splash screen once we know the root view has already
      // performed layout.
      await SplashScreen.hideAsync()
    }
  }, [appIsReady])

  if (!appIsReady) {
    return null
  }

  return (
    <SafeAreaProvider>
      <Provider>
        <RootSiblingParent>
          <NavigationContainer ref={navigationRef} onReady={onReady}>
            <ExpoStatusBar style="light" backgroundColor="rgba(0, 0, 0, .9)" />
            <DrawerNavigator />
          </NavigationContainer>
        </RootSiblingParent>
      </Provider>
    </SafeAreaProvider>
  )
}

export default App

We are not going to cover each screen of the mobile app, but you can open the source code and see each one if you want.

Backend

The backend is a web application built with Node.js, React, MUI and TypeScript. From the backend, admins can create and manage suppliers, cars, locations, customers and bookings. When new suppliers are created from the backend, they will receive an email prompting them to create an account in order to access the backend and manage their car fleet and bookings.

  • ./backend/assets/ folder contains CSS and images.
  • ./backend/pages/ folder contains React pages.
  • ./backend/components/ folder contains React components.
  • ./backend/services/ contains BookCars API client services.
  • ./backend/App.tsx is the main React App that contains routes.
  • ./backend/index.tsx is the main entry point of the backend.

TypeScript type definitions are defined in the package ./packages/bookcars-types.

App.tsx of the backend follow similar logic like App.tsx of the frontend.

We are not going to cover each page of the backend but you can open the source code and see each one if you want.

Points of Interest

I started this project for a friend who runs a car rental company. He needed a car rental website and mobile app for Android and iOS, as well as a backend for managing vehicles, bookings and customers. I took it a step further by enhancing the project with additional features to make it more generic. Finally, I released it under the terms of the MIT license and made it available to everyone on CodeProject and GitHub.

Building the mobile app with React Native and Expo is very easy. Expo makes mobile development with React Native very simple.

Using the same language (TypeScript) for backend and frontend development is very convenient.

TypeScript is a very interesting language and has many advantages. By adding static typing to JavaScript, we can avoid many bugs and produce high quality, scalable, more readable and maintainable code that is easy to debug and test.

So, that's it! I hope you enjoyed reading this article.

History

  • 10th November, 2022 - Initial release
  • 19th November, 2022 - Updates in source code and content
  • 26th November, 2022 - Added Run Mobile App instructions
  • 1st December, 2022 - Updates in the backend, the frontend and the mobile app
  • 4th December, 2022 - Updates in the backend, the frontend and the mobile app
  • 9th December, 2022 - Updates in the backend and the frontend
  • 23rd January, 2023 - Updates in installation instructions
  • 31st January, 2023 - Updates in source code and instructions
  • 14th March, 2023 - Under the hood updates
  • 14th April, 2023 - Fixed validation issues
  • 27th May, 2023 - Updates in the API, the backend, the frontend and the mobile app
  • 10th June, 2023 - Updates in the API, the backend and the frontend
  • 20th June, 2023
    • Upgrade to MUI 5.13.4
    • Upgrade to MUI DataGrid 6.7.0
    • Added devDependencies
    • Bug fixes and under the hood updates
  • 27th June, 2023
    • Fixed some issues in the dashboard
    • Fixed some issues related to bookings
    • Fixed some issues related to notifications
    • Bump mongoose from 7.3.0 to 7.3.1
    • Bump @mui/material from 5.13.5 to 5.13.6
    • Bump @mui/x-data-grid from 6.8.0 to 6.9.0
    • Bump @mui/x-date-pickers from 6.8.0 to 6.9.0
    • Bump react-router-dom from 6.13.0 to 6.14.0
  • 8th July, 2023
    • Updated DatePicker and DateTimePicker components
    • Fixed some issues in bookingController.js
    • Fixed localization issues
    • Bump jsonwebtoken from 9.0.0 to 9.0.1
    • Bump mongoose from 7.3.1 to 7.3.2
    • Bump @mui/icons-material from 5.11.16 to 5.13.7
    • Bump @mui/material from 5.13.6 to 5.13.7
    • Bump @mui/x-data-grid from 6.9.0 to 6.9.2
    • Bump @mui/x-date-pickers from 6.9.0 to 6.9.2
    • Bump react-router-dom from 6.14.0 to 6.14.1
  • 18th July, 2023 - BookCars 1.2 released
    • Upgrade to Expo SDK 49
    • Added axios retries
    • Updated push notifications
    • Fixed splash screen issues
    • Fixed expo update issues
    • Fixed cars screen issues
    • Fixed bookings screen issues
    • Fixed settings screen issues
    • Bump @react-native-async-storage/async-storage from 1.17.3 to 1.18.2
    • Bump @react-native-community/datetimepicker from 6.1.2 to 7.2.0
    • Bump @react-navigation/drawer from 6.4.1 to 6.6.3
    • Bump @react-navigation/native from 6.0.10 to 6.1.7
    • Bump @react-navigation/native-stack from 6.6.2 to 6.9.13
    • Bump axios from 0.27.2 to 1.4.0
    • Bump date-fns from 2.28.0 to 2.30.0
    • Bump expo from 45.0.0 to 49.0.0
    • Bump expo-asset from 8.5.0 to 8.10.1
    • Bump expo-constants from 13.1.1 to 14.4.2
    • Bump expo-device from 4.2.0 to 5.4.0
    • Bump expo-image-picker from 13.1.1 to 14.3.1
    • Bump expo-localization from 13.0.0 to 14.3.0
    • Bump expo-notifications from 0.15.4 to 0.20.1
    • Bump expo-splash-screen from 0.15.1 to 0.20.4
    • Bump expo-status-bar from 1.3.0 to 1.6.0
    • Bump expo-updates from 0.13.4 to 0.18.10
    • Bump react from 17.0.2 to 18.2.0
    • Bump react-dom 17.0.2 to 18.2.0
    • Bump react-native from 0.68.2 to 0.72.3
    • Bump react-native-gesture-handler from 2.2.1 to 2.12.0
    • Bump react-native-paper from 4.12.1 to 5.9.1
    • Bump react-native-reanimated from 2.8.0 to 3.3.0
    • Bump react-native-safe-area-context from 4.2.4 to 4.6.3
    • Bump react-native-screens from 3.11.1 to 3.22.0
    • Bump react-native-web from 0.17.7 to 0.19.6
    • Bump validator from 13.7.0 to 13.9.0
    • Bump mongoose from 7.3.2 to 7.3.4
    • Bump nodemon from 2.0.22 to 3.0.1
    • Bump @mui/icons-material from 5.13.7 to 5.14.0
    • Bump @mui/material from 5.13.7 to 5.14.0
    • Bump @mui/x-data-grid from 6.9.2 to 6.10.0
    • Bump @mui/x-date-pickers from 6.9.2 to 6.10.0
    • Bump react-router-dom from 6.14.1 to 6.14.2
  • 27th July, 2023 - BookCars 1.3 released
    • Upgrade to Expo SDK 49.0.5
    • Added one supplier only support
    • Added REACT_APP_BC_PAGINATION_MODE setting option
    • Added REACT_APP_BC_RECAPTCHA_ENABLED setting option
    • Added classic pagination
    • Added infinite scroll pagination
    • Added useNavigate
    • Updated global layout
    • Updated header style
    • Updated fs/promises
    • Updated bookingController.js
    • Updated carController.js
    • Updated locationController.js
    • Updated notificationController.js
    • Updated supplierController.js
    • Updated userController.js
    • Fixed image layout issues
    • Bump mongoose from 7.3.4 to 7.4.1
    • Bump nodemailer from 6.9.3 to 6.9.4
    • Bump @mui/icons-material from 5.14.0 to 5.14.1
    • Bump @mui/material from 5.14.0 to 5.14.2
    • Bump @mui/x-data-grid from 6.10.0 to 6.10.1
    • Bump @mui/x-date-pickers from 6.10.0 to 6.10.1
    • Bump expo from 49.0.0 to 49.0.5
    • Bump expo-image-picker from 14.3.1 to 14.3.2
    • Bump expo-updates from 0.18.10 to 0.18.11
  • 2nd August, 2023 - BookCars 1.4 released
    • Added Docker support
    • Added eslint
    • Upgrade to Expo 49.0.6
    • Updated robots.txt
    • Updated userController.js
    • Updated bookingController.js
    • Updated carController.js
    • Updated locationController.js
    • Updated notificationController.js
    • Renamed CreateBooking to Checkout
    • Optimized mobile images
    • Fixed additional driver checkout issues
    • Fixed DatePicker and DateTimePicker issues
    • Fixed error handling issues
    • Fixed some layout issues
    • Fixed useNavigate issues
    • Fixed supplier issues
    • Fixed carController.js issues
    • Fixed booking issues
    • Fixed splash screen issues
    • Fixed package-lock.json issues
    • Bump @mui/x-date-pickers from 6.10.1 to 6.10.2
    • Bump @mui/x-data-grid from 6.10.1 to 6.10.2
    • Bump expo from 49.0.5 to 49.0.6
    • Bump expo-splash-screen from 0.20.4 to 0.20.5
  • 18th August, 2023 - BookCars 1.5 released
    • Added pure functional components in mobile app
    • Added prettier configuration
    • Simplified localization
    • Added Docker SSL instructions
    • Updated Docker configuration
    • Updated CreateCar and UpdateCar backend pages
    • Updated Cars and Checkout frontend pages
    • Updated backend and frontend configuration
    • Fixed some issues in userController.js
    • Fixed some issues in supplierController.js
    • Fixed addtional driver issues
    • Fixed VirtualizedList mobile issues
    • Bump mongoose from 7.4.1 to 7.4.3
    • Bump bcrypt from 5.1.0 to 5.1.1
    • Bump validator from 13.9.0 to 13.11.0
    • Bump eslint from 8.46.0 to 8.47.0
    • Bump @mui/material from 5.14.2 to 5.14.5
    • Bump @mui/icons-material from 5.14.1 to 5.14.3
    • Bump @mui/x-date-pickers from 6.10.2 to 6.11.2
    • Bump @mui/x-data-grid from 6.10.2 to 6.11.2
    • Bump react-router-dom from 6.14.2 to 6.15.0
    • Bump expo from 49.0.6 to 49.0.7
    • Bump @babel/core from 7.12.9 to 7.22.9
    • Bump axios-retry from 3.5.1 to 3.6.0
    • Bump i18n-js from 3.9.2 to 4.3.0
    • Bump react-native-dotenv from 3.3.1 to 3.4.9
    • Bump react-native-gesture-handler from 2.12.0 to 2.12.1
    • Bump react-native-paper from 5.9.1 to 5.10.0
    • Bump react-native-root-toast from 3.4.0 to 3.4.1
    • Bump react-native-web from 0.19.6 to 0.19.7
  • 27th August, 2023 - BookCars 1.6 released
    • Added one supplier support to mobile app
    • Fixed an issue in frontend Settings page
    • Fixed some issues in the backend
    • Bump eslint from 8.47.0 to 8.48.0
    • Bump mongoose from 7.4.3 to 7.4.5
    • Bump @mui/icons-material from 5.14.3 to 5.14.6
    • Bump @mui/material from 5.14.5 to 5.14.6
    • Bump @mui/x-data-grid from 6.11.2 to 6.12.0
    • Bump @mui/x-date-pickers from 6.11.2 to 6.12.0
    • Bump axios from 1.4.0 to 1.5.0
    • Bump axios-retry from 3.6.0 to 3.6.1
    • Bump expo from 49.0.7 to 49.0.8
  • 10th September, 2023 - BookCars 1.7 released
    • Rewrite of the entire platform to TypeScript
    • Upgrade to Expo 49.0.9
    • Added eslint recommendation extension to vscode
    • Updated Docker and eslint configurations
    • Removed unused and obsolete npm packages
    • Fixed some issues related to users, suppliers and locations
    • Fixed some issues related to account activation
    • Fixed some issues related to bookings and cancellation
    • Fixed some issues related to deleting cars
    • Bump jsonwebtoken from 9.0.1 to 9.0.2
    • Bump mongoose from 7.4.5 to 7.5.0
    • Bump multer from 1.4.4 to 1.4.5-lts.1
    • Bump nodemailer from 6.9.4 to 6.9.5
    • Bump eslint from 8.48.0 to 8.49.0
    • Bump @mui/icons-material from 5.14.6 to 5.14.8
    • Bump @mui/material form 5.14.6 to 5.14.8
    • Bump @mui/x-data-grid from 6.12.0 to 6.13.0
    • Bump @mui/x-date-pickers from 6.12.0 to 6.13.0
    • Bump axios-retry from 3.6.1 to 3.7.0
    • Bump expo from 49.0.8 to 49.0.9
    • Bump react-native-paper from 5.10.0 to 5.10.4
    • Bump react-native-root-toast from 3.4.1 to 3.5.1
    • Bump react-native-web from 0.19.7 to 0.19.8
  • 21st September, 2023 - BookCars 1.8 released
    • Added build:android:preview and build:ios:preview commands
    • Updated api helper and user controller
    • Updated bookcars-helper, bookcars-types and disable-react-devtools packages
    • Fixed some issues related to users, cars and bookings in the backend
    • Fixed some issues related to auto-complete dropdowns
    • Fixed some issues related to search and filters
    • Fixed some issues related to password reset
    • Fixed some issues related to inifinite scroll
    • API:
      • Bump @types/jsonwebtoken from 9.0.2 to 9.0.3
      • Bump @types/node from 20.6.0 to 20.6.3
      • Bump @types/nodemailer from 6.4.9 to 6.4.10
      • Bump mongoose from 7.5.0 to 7.5.2
      • Bump uuid from 9.0.0 to 9.0.1
      • Bump @typescript-eslint/eslint-plugin from 6.6.0 to 6.7.2
      • Bump @typescript-eslint/parser from 6.6.0 to 16.14.4
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.8 to 5.14.9
      • Bump @mui/material from 5.14.8 to 5.14.10
      • Bump @mui/x-data-grid from 6.13.0 to 6.14.0
      • Bump @mui/x-date-pickers from 6.13.0 to 6.14.0
      • Bump @types/node from 20.6.0 to 20.6.3
      • Bump @types/react from 18.2.21 to 18.2.22
      • Bump react-router-dom from 6.15.0 to 6.16.0
    • Mobile App:
      • Bump axios-retry from 3.7.0 to 3.8.0
      • Bump expo from 49.0.9 to 49.0.11
      • Bump expo-updates from 0.18.12 to 0.18.13
      • Bump i18n-js from 4.3.0 to 4.3.2
      • Bump react-native-paper from 5.10.4 to 5.10.6
      • Bump react-native-web from 0.19.8 to 0.19.9
      • Bump @babel/core from 7.22.17 to 7.22.20
      • Bump @types/react from 18.2.21 to 18.2.22
      • Bump @typescript-eslint/eslint-plugin from 6.6.0 to 6.7.2
      • Bump @typescript-eslint/parser from 6.6.0 to 6.7.2
  • 29th September, 2023 - BookCars 1.9 released
    • Added jsdoc to the api, the backend, the frontend and the mobile app
    • Upgrade to node:lts-alpine
    • Fixed some issues related to cars, users and bookings
    • Fixed some issues related to dates and times
    • Fixed some issues related to checkout options
    • Fixed some issues related to cancellation
    • API:
      • Bump @types/express from 4.17.17 to 4.17.18
      • Bump @types/node from 20.6.3 to 20.7.0
      • Bump @types/nodemailer from 6.4.10 to 6.4.11
      • Bump @types/validator from 13.11.1 to 13.11.2
      • Bump mongoose from 7.5.2 to 7.5.3
      • Bump rimraf from 5.0.1 to 5.0.4
      • Bump @typescript-eslint/eslint-plugin from 6.7.2 to 6.7.3
      • Bump @typescript-eslint/parser from 6.7.2 to 6.7.3
      • Bump eslint from 8.49.0 to 8.50.0
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.9 to 5.14.11
      • Bump @mui/material from 5.14.10 to 5.14.11
      • Bump @mui/x-data-grid from 6.14.0 to 6.15.0
      • Bump @mui/x-date-pickers from 6.14.0 to 6.15.0
      • Bump @types/node from 20.6.3 to 20.7.0
      • Bump @types/react from 18.2.22 to 18.2.23
      • Bump @types/validator from 13.11.1 to 13.11.2
      • Bump axios from 1.5.0 to 1.5.1
    • Mobile app:
      • Bump @react-navigation/drawer from 6.6.3 to 6.6.4
      • Bump @react-navigation/native from 6.1.7 to 6.1.8
      • Bump @react-navigation/native-stack from 6.9.13 to 6.9.14
      • Bump @react-navigation/stack from 6.3.17 to 6.3.18
      • Bump @types/validator from 13.11.1 to 13.11.2
      • Bump axios from 1.5.0 to 1.5.1
      • Bump react-native from 0.72.4 to 0.72.5
      • Bump @babel/core from 7.22.20 to 7.23.0
      • Bump @types/react from 18.2.22 to 18.2.23
      • Bump @typescript-eslint/eslint-plugin from 6.7.2 to 6.7.3
      • Bump @typescript-eslint/parser from 6.7.2 to 6.7.3
      • Bump eslint from 8.49.0 to 8.50.0
    • Full Changelog: v1.8...v1.9
  • 4th October, 2023 - BookCars 2.0 released
    • Added airbnb preset
    • Fixed an issue in cars page
    • Fixed other issues in the api, the backend, the frontend and the mobile app
    • API:
      • Bump @types/node from 20.8.0 to 20.8.2
      • Bump @typescript-eslint/eslint-plugin from 6.7.3 to 6.7.4
      • Bump @typescript-eslint/parser from 6.7.3 to 6.7.4
    • Backend and frontend:
      • Bump @mui/x-data-grid from 6.15.0 to 6.16.0
      • Bump @mui/x-date-pickers from 6.15.0 to 6.16.0
      • Bump @types/node from 20.7.0 to 20.8.2
      • Bump @types/react from 18.2.23 to 18.2.24
      • Bump @types/react-dom from 18.2.7 to 18.2.8
      • Bump @types/react-google-recaptcha from 2.1.5 to 2.1.6
    • Mobile app:
      • Bump expo from 49.0.11 to 49.0.13
      • Bump @types/mime from 3.0.1 to 3.0.2
      • Bump @types/react-native-vector-icons from 6.4.14 to 6.4.15
      • Bump react-native-size-matters from 0.4.0 to 0.4.2
      • Bump @types/react from 18.2.23 to 18.2.24
      • Bump @typescript-eslint/eslint-plugin from 6.7.3 to 6.7.4
      • Bump @typescript-eslint/parser from 6.7.3 to 6.7.4
    • Full Changelog: v1.9...v2.0

  • 9th October, 2023 - BookCars 2.1 released
    • Secured the backend and the frontend against XSS, XST, CSRF and MITM
    • Made localization more generic
    • Updated eslint preset
    • Updated dependencies
    • Updated documentation
    • Updated CI workflows
    • API:
      • Bump @types/node from 20.8.2 to 20.8.3
      • Bump @types/uuid from 9.0.4 to 9.0.5
      • Bump eslint from 8.50.0 to 8.51.0
      • Bump mongoose from 7.5.3 to 7.6.0
      • Bump npm-check-updates from 16.14.4 to 16.14.5
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.11 to 5.14.12
      • Bump @mui/material from 5.14.11 to 5.14.12
      • Bump @mui/x-data-grid from 6.16.0 to 6.16.1
      • Bump @mui/x-date-pickers from 6.16.0 to 6.16.1
      • Bump @types/node from 20.8.2 to 20.8.3
      • Bump @types/react from 18.2.24 to 18.2.25
      • Bump @types/react-dom from 18.2.8 to 18.2.11
      • Bump npm-check-updates from 16.14.4 to 16.14.5
    • Mobile app:
      • Bump expo-updates from 0.18.14 to 0.18.16
      • Bump @types/react from 18.2.24 to 18.2.25
      • Bump eslint from 8.50.0 to 8.51.0
    • Full Changelog: v2.0...v2.1

  • 14th October, 2023 - BookCars 2.2 released
    • Added cors and allowedMethods middlewares
    • Fixed some issues related to bookings
    • API:
      • Bump @types/express from 4.17.18 to 4.17.19
      • Bump @types/node from 20.8.3 to 20.8.6
      • Bump @types/validator from 13.11.2 to 13.11.3
      • Bump @typescript-eslint/eslint-plugin from 6.7.4 to 6.7.5
      • Bump @typescript-eslint/parser from 6.7.4 to 6.7.5
      • Bump mongoose from 7.6.0 to 7.6.2
      • Bump nodemailer from 6.9.5 to 6.9.6
      • Bump npm-check-updates from 16.14.5 to 16.14.6
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.12 to 5.14.13
      • Bump @mui/material from 5.14.12 to 5.14.13
      • Bump @mui/x-data-grid from 6.16.1 to 6.16.2
      • Bump @mui/x-date-pickers from 6.16.1 to 6.16.2
      • Bump @types/node from 20.8.3 to 20.8.6
      • Bump @types/react from 18.2.25 to 18.2.28
      • Bump @types/react-dom from 18.2.11 to 18.2.13
      • Bump @types/validator from 13.11.2 to 13.11.3
      • Bump npm-check-updates from 16.14.5 to 16.14.6
    • Mobile app:
      • Bump @types/validator from 13.11.2 to 13.11.3
      • Bump @babel/core from 7.23.0 to 7.23.2
      • Bump @types/react from 18.2.25 to 18.2.28
      • Bump @typescript-eslint/eslint-plugin from 6.7.4 to 6.7.5
      • Bump @typescript-eslint/parser from 6.7.4 to 6.7.5
    • Full Changelog: v2.1...v2.2

  • 2nd November, 2023 - BookCars 2.3 released
    • Added Babel transcompiler to api
    • Updated eslint preset
    • Updated backend, frontend and mobile components
    • Updated documentation
    • Updated dependencies
    • API:
      • Bump @types/bcrypt from 5.0.0 to 5.0.1
      • Bump @types/compression from 1.7.3 to 1.7.4
      • Bump @types/cookie-parser from 1.4.4 to 1.4.5
      • Bump @types/cors from 2.8.14 to 2.8.15
      • Bump @types/express from 4.17.19 to 4.17.20
      • Bump @types/jsonwebtoken from 9.0.3 to 9.0.4
      • Bump @types/multer from 1.4.8 to 1.4.9
      • Bump @types/node from 20.8.6 to 20.8.10
      • Bump @types/nodemailer from 6.4.11 to 6.4.13
      • Bump @types/uuid from 9.0.5 to 9.0.6
      • Bump @types/validator from 13.11.3 to 13.11.5
      • Bump @typescript-eslint/eslint-plugin from 6.7.5 to 6.9.1
      • Bump @typescript-eslint/parser from 6.7.5 to 6.9.1
      • Bump eslint from 8.51.0 to 8.52.0
      • Bump eslint-plugin-import from 2.28.1 to 2.29.0
      • Bump mongoose from 7.6.2 to 8.0.0
      • Bump nodemailer from 6.9.6 to 6.9.7
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.13 to 5.14.16
      • Bump @mui/material from 5.14.13 to 5.14.16
      • Bump @mui/x-data-grid from 6.16.2 to 6.17.0
      • Bump @mui/x-date-pickers from 6.16.2 to 6.17.0
      • Bump @types/node from 20.8.6 to 20.8.10
      • Bump @types/react from 18.2.28 to 18.2.34
      • Bump @types/react-dom from 18.2.13 to 18.2.14
      • Bump @types/validator from 13.11.3 to 13.11.5
      • Bump axios from 1.5.1 to 1.6.0
      • Bump react-router-dom from 6.16.0 to 6.18.0
    • Mobile app:
      • Bump @react-navigation/drawer from 6.6.4 to 6.6.6
      • Bump @react-navigation/native from 6.1.8 to 6.1.9
      • Bump @react-navigation/native-stack from 6.9.14 to 6.9.16
      • Bump @react-navigation/stack from 6.3.18 to 6.3.20
      • Bump @types/lodash.debounce from 4.0.7 to 4.0.8
      • Bump @types/mime from 3.0.2 to 3.0.3
      • Bump @types/react-native-dotenv from 0.2.0 to 0.2.1
      • Bump @types/react-native-vector-icons from 6.4.15 to 6.4.16
      • Bump @types/validator from 13.11.3 to 13.11.5
      • Bump axios from 1.5.1 to 1.6.0
      • Bump axios-retry from 3.8.0 to 3.8.1
      • Bump expo from 49.0.13 to 49.0.16
      • Bump expo-updates from 0.18.16 to 0.18.17
      • Bump react-native from 0.72.5 to 0.72.6
      • Bump react-native-paper from 5.10.6 to 5.11.1
      • Bump react-native-vector-icons from 10.0.0 to 10.0.1
      • Bump @types/react from 18.2.28 to 18.2.34
      • Bump @typescript-eslint/eslint-plugin from 6.7.5 to 6.9.1
      • Bump @typescript-eslint/parser from 6.7.5 to 6.9.1
      • Bump eslint from 8.51.0 to 8.52.0
      • Bump eslint-plugin-import from 2.28.1 to 2.29.0
    • Full Changelog: v2.2...v2.3

  • 18th November, 2023 - BookCars 2.4 released
    • Updated MongoDB queries
    • Updated nodemon configuration
    • Updated dependencies
    • API:
      • Bump @babel/preset-env from 7.23.2 to 7.23.3
      • Bump @types/bcrypt from 5.0.1 to 5.0.2
      • Bump @types/compression from 1.7.4 to 1.7.5
      • Bump @types/cookie-parser from 1.4.5 to 1.4.6
      • Bump @types/cors from 2.8.15 to 2.8.16
      • Bump @types/express from 4.17.20 to 4.17.21
      • Bump @types/jsonwebtoken from 9.0.4 to 9.0.5
      • Bump @types/multer from 1.4.9 to 1.4.10
      • Bump @types/node from 20.8.10 to 20.9.1
      • Bump @types/nodemailer from 6.4.13 to 6.4.14
      • Bump @types/uuid from 9.0.6 to 9.0.7
      • Bump @types/validator from 13.11.5 to 13.11.6
      • Bump @typescript-eslint/eslint-plugin from 6.9.1 to 6.11.0
      • Bump @typescript-eslint/parser from 6.9.1 to 6.11.0
      • Bump eslint from 8.52.0 to 8.54.0
      • Bump helmet from 7.0.0 to 7.1.0
      • Bump mongoose from 8.0.0 to 8.0.1
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.16 to 5.14.18
      • Bump @mui/material from 5.14.16 to 5.14.18
      • Bump @mui/x-data-grid from 6.17.0 to 6.18.1
      • Bump @mui/x-date-pickers from 6.17.0 to 6.18.1
      • Bump @types/draftjs-to-html from 0.8.3 to 0.8.4
      • Bump @types/html-to-draftjs from 1.4.2 to 1.4.3
      • Bump @types/node from 20.8.10 to 20.9.1
      • Bump @types/react from 18.2.34 to 18.2.37
      • Bump @types/react-dom from 18.2.14 to 18.2.15
      • Bump @types/react-draft-wysiwyg from 1.13.6 to 1.13.7
      • Bump @types/validator from 13.11.5 to 13.11.6
      • Bump axios from 1.6.0 to 1.6.2
      • Bump react-router-dom from 6.18.0 to 6.19.0
    • Mobile app:
      • Bump @react-navigation/native-stack from 6.9.16 to 6.9.17
      • Bump @types/lodash.debounce from 4.0.8 to 4.0.9
      • Bump @types/mime from 3.0.3 to 3.0.4
      • Bump @types/react-native-dotenv from 0.2.1 to 0.2.2
      • Bump @types/react-native-vector-icons from 6.4.16 to 6.4.18
      • Bump @types/validator from 13.11.5 to 13.11.6
      • Bump axios from 1.6.0 to 1.6.2
      • Bump axios-retry from 3.8.1 to 3.9.1
      • Bump expo from 49.0.16 to 49.0.19
      • Bump react-native-vector-icons from 10.0.1 to 10.0.2
      • Bump @babel/core from 7.23.2 to 7.23.3
      • Bump @types/react from 18.2.34 to 18.2.37
      • Bump @typescript-eslint/eslint-plugin from 6.9.1 to 6.11.0
      • Bump @typescript-eslint/parser from 6.9.1 to 6.11.0
      • Bump eslint from 8.52.0 to 8.54.0
    • Full Changelog: v2.3...v2.4

  • 3rd December, 2023 - BookCars 2.5 released
    • Upgrade to TypeScript 5.3
    • API:
      • Bump @babel/cli from 7.23.0 to 7.23.4
      • Bump @babel/preset-env from 7.23.3 to 7.23.5
      • Bump @types/cors from 2.8.16 to 2.8.17
      • Bump @types/multer from 1.4.10 to 1.4.11
      • Bump @types/node from 20.9.1 to 20.10.2
      • Bump @types/validafromr from 13.11.6 to 13.11.7
      • Bump @typescript-eslint/eslint-plugin from 6.11.0 to 6.13.1
      • Bump @typescript-eslint/parser from 6.11.0 to 6.13.1
      • Bump eslint from 8.54.0 to 8.55.0
      • Bump mongoose from 8.0.1 to 8.0.2
      • Bump nodemon from 3.0.1 to 3.0.2
      • Bump npm-check-updates from 16.14.6 to 16.14.11
      • Bump typescript from 5.2.2 to 5.3.2
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.18 to 5.14.19
      • Bump @mui/material from 5.14.18 to 5.14.19
      • Bump @mui/x-data-grid from 6.18.1 to 6.18.2
      • Bump @mui/x-date-pickers from 6.18.1 to 6.18.2
      • Bump @types/node from 20.9.1 to 20.10.2
      • Bump @types/react from 18.2.37 to 18.2.41
      • Bump @types/react-dom from 18.2.15 to 18.2.17
      • Bump @types/validator from 13.11.6 to 13.11.7
      • Bump npm-check-updates from 16.14.6 to 16.14.11
      • Bump react-router-dom from 6.19.0 to 6.20.1
    • Mobile app:
      • Bump @types/validator from 13.11.6 to 13.11.7
      • Bump axios-retry from 3.9.1 to 4.0.0
      • Bump expo from 49.0.19 to 49.0.21
      • Bump mime from 3.0.0 to 4.0.0
      • Bump react-native-paper from 5.11.1 to 5.11.3
      • Bump @babel/core from 7.23.3 to 7.23.5
      • Bump @types/react from 8.2.37 to 18.2.41
      • Bump @typescript-eslint/eslint-plugin from 6.11.0 to 6.13.1
      • Bump @typescript-eslint/parser from 6.11.0 to 6.13.1
      • Bump eslint from 8.54.0 to 8.55.0
      • Bump typescript from 5.2.2 to 5.3.2
    • Full Changelog: v2.4...v2.5

  • 14th December, 2023 - BookCars 2.6 released
    • Fixed autocomplete issues
    • Fixed TextField clear issues
    • Updated dependencies
    • API:
      • Bump ts-node from 10.9.1 to 10.9.2
      • Bump @babel/preset-env from 7.23.5 to 7.23.6
      • Bump @types/node from 20.10.2 to 20.10.4
      • Bump @typescript-eslint/eslint-plugin from 6.13.1 to 6.14.0
      • Bump @typescript-eslint/parser from 6.13.1 to 6.14.0
      • Bump mongoose from 8.0.2 to 8.0.3
      • Bump npm-check-updates from 16.14.11 to 16.14.12
      • Bump typescript from 5.3.2 to 5.3.3
    • Backend and frontend:
      • Bump @mui/icons-material from 5.14.19 to 5.15.0
      • Bump @mui/material from 5.14.19 to 5.15.0
      • Bump @mui/x-data-grid from 6.18.2 to 6.18.5
      • Bump @mui/x-date-pickers from 6.18.2 to 6.18.5
      • Bump @types/node from 20.10.2 to 20.10.4
      • Bump @types/react from 18.2.41 to 18.2.45
      • Bump npm-check-updates from 16.14.11 to 16.14.12
      • Bump react-router-dom from 6.20.1 to 6.21.0
    • Mobile app:
      • Bump react-native-paper from 5.11.3 to 5.11.4
      • Bump react-native-vector-icons from 10.0.2 to 10.0.3
      • Bump @babel/core from 7.23.5 to 7.23.6
      • Bump @types/react from 18.2.41 to 18.2.45
      • Bump @typescript-eslint/eslint-plugin from 6.13.1 to 6.14.0
      • Bump @typescript-eslint/parser from 6.13.1 to 6.14.0
      • Bump typescript from 5.3.2 to 5.3.3
    • Full Changelog: v2.5...v2.6

  • 11th January, 2024 - BookCars 2.7 released
    • Updated Search component
    • Replaced ts-node by tsx
    • Fixed permission issues on Android 13 and above
    • API:
      • Bump @babel/preset-env from 7.23.6 to 7.23.8
      • Bump @types/node from 20.10.4 to 20.10.8
      • Bump @types/validator from 13.11.7 to 13.11.8
      • Bump @typescript-eslint/eslint-plugin from 6.14.0 to 6.18.1
      • Bump @typescript-eslint/parser from 6.14.0 to 6.18.1
      • Bump eslint from 8.55.0 to 8.56.0
      • Bump eslint-plugin-import from 2.29.0 to 2.29.1
      • Bump mongoose from 8.0.3 to 8.0.4
      • Bump nodemailer from 6.9.7 to 6.9.8
    • Backend and frontend:
      • Bump @emotion/react from 11.11.1 to 11.11.3
      • Bump @mui/icons-material from 5.15.0 to 5.15.3
      • Bump @mui/material from 5.15.0 to 5.15.3
      • Bump @mui/x-data-grid from 6.18.5 to 6.18.7
      • Bump @mui/x-date-pickers from 6.18.5 to 6.18.7
      • Bump @types/node from 20.10.4 to 20.10.8
      • Bump @types/react from 18.2.45 to 18.2.47
      • Bump @types/react-dom from 18.2.17 to 18.2.18
      • Bump @types/validator from 13.11.7 to 13.11.8
      • Bump axios from 1.6.2 to 1.6.5
      • Bump date-fns from 2.30.0 to 3.2.0
      • Bump react-router-dom from 6.21.0 to 6.21.1
    • Mobile app:
      • Bump @types/validator from 13.11.7 to 13.11.8
      • Bump axios from 1.6.2 to 1.6.5
      • Bump date-fns from 2.30.0 to 3.2.0
      • Bump expo-updates from 0.18.17 to 0.18.19
      • Bump mime from 4.0.0 to 4.0.1
      • Bump react-native-paper from 5.11.4 to 5.11.7
      • Bump react-native-web from 0.19.9 to 0.19.10
      • Bump @babel/core from 7.23.6 to 7.23.7
      • Bump @types/react from 18.2.45 to 18.2.47
      • Bump @typescript-eslint/eslint-plugin from 6.14.0 to 6.18.1
      • Bump @typescript-eslint/parser from 6.14.0 to 6.18.1
      • Bump eslint from 8.55.0 to 8.56.0
      • Bump eslint-plugin-import from 2.29.0 to 2.29.1
    • Full Changelog: v2.6...v2.7

  • 29th January, 2024 - BookCars 2.8 released
    • Upgrade to Expo SDK 50
    • Fixed react-toastify issues
    • Fixed Mobile Drawer issues
    • API:
      • @babel/cli from 7.23.4 to 7.23.9
      • @babel/preset-env from 7.23.8 to 7.23.9
      • @types/node from 20.10.8 to 20.11.10
      • @types/uuid from 9.0.7 to 9.0.8
      • @typescript-eslint/eslint-plugin from 6.18.1 to 6.19.1
      • @typescript-eslint/parser from 6.18.1 to 6.19.1
      • dotenv from 16.3.1 to 16.4.1
      • mongoose from 8.0.4 to 8.1.1
      • nodemon from 3.0.2 to 3.0.3
      • npm-check-updates from 16.14.12 to 16.14.14
    • Backend and frontend:
      • @mui/icons-material from 5.15.3 to 5.15.6
      • @mui/material from 5.15.3 to 5.15.6
      • @mui/x-data-grid from 6.18.7 to 6.19.2
      • @mui/x-date-pickers from 6.18.7 to 6.19.2
      • @types/node from 20.10.8 to 20.11.10
      • @types/react from 18.2.47 to 18.2.48
      • axios from 1.6.5 to 1.6.7
      • npm-check-updates from 16.14.12 to 16.14.14
      • react-router-dom from 6.21.1 to 6.21.3
      • react-toastify from 9.1.3 to 10.0.4
    • Mobile App:
      • Bump @react-native-async-storage/async-storage from 1.18.2 to 1.21.0
      • Bump @react-native-community/datetimepicker from 7.2.0 to 7.6.1
      • Bump axios from 1.6.5 to 1.6.7
      • Bump date-fns from 3.2.0 to 3.3.1
      • Bump expo from 49.0.21 to 50.0.4
      • Bump expo-asset from 8.10.1 to 9.0.2
      • Bump expo-constants from 14.4.2 to 15.4.5
      • Bump expo-device from 5.4.0 to 5.9.3
      • Bump expo-image-picker from 14.3.2 to 14.7.1
      • Bump expo-localization from 14.3.0 to 14.8.3
      • Bump expo-notifications from 0.20.1 to 0.27.6
      • Bump expo-splash-screen from 0.20.5 to 0.26.4
      • Bump expo-status-bar from 1.6.0, ~1.11.1
      • Bump expo-updates from 0.18.19 to 0.24.9
      • Bump react-native from 0.72.6 to 0.73.2
      • Bump react-native-gesture-handler from 2.12.0 to 2.14.1
      • Bump react-native-paper from 5.11.7 to 5.12.3
      • Bump react-native-reanimated from 3.3.0 to 3.6.2
      • Bump react-native-safe-area-context from 4.6.3 to 4.8.2
      • Bump react-native-screens from 3.22.0 to 3.29.0
      • Bump @babel/core from 7.23.7 to 7.23.9
      • Bump @types/react from 18.2.47 to 18.2.48
      • Bump @typescript-eslint/eslint-plugin from 6.18.1 to 6.19.1
      • Bump @typescript-eslint/parser from 6.18.1 to 6.19.1
    • Full Changelog: v2.7...v2.8

  • 29th February, 2024 - BookCars 2.9 released
    • Added unit tests and coverage
    • Updated eslint presets
    • Updated the documentation
    • Fix update avatar in mobile app
    • Fix push notification issues
    • Fix API issues
    • Fix CodeFactor issues
    • Fix eslint issues
    • API:
      • Bump @babel/core from 7.23.9 to 7.24.0
      • Bump @babel/preset-env from 7.23.9 to 7.24.0
      • Bump @types/jsonwebtoken from 9.0.5 to 9.0.6
      • Bump @types/node from 20.11.10 to 20.11.24
      • Bump @types/validator from 13.11.8 to 13.11.9
      • Bump @typescript-eslint/eslint-plugin from 6.19.1 to 7.1.0
      • Bump @typescript-eslint/parser from 6.19.1 to 7.1.0
      • Bump dotenv from 16.4.1 to 16.4.5
      • Bump eslint from 8.56.0 to 8.57.0
      • Bump express from 4.18.2 to 4.18.3
      • Bump mongoose from 8.1.1 to 8.2.0
      • Bump nodemailer from 6.9.8 to 6.9.11
      • Bump nodemon from 3.0.3 to 3.1.0
      • Bump npm-check-updates from 16.14.14 to 16.14.15
      • Bump tsx from 4.7.0 to 4.7.1
    • Backend and frontend:
      • Bump @emotion/react from 11.11.3 to 11.11.4
      • Bump @mui/icons-material from 5.15.6 to 5.15.11
      • Bump @mui/material from 5.15.6 to 5.15.11
      • Bump @mui/x-data-grid from 6.19.2 to 6.19.5
      • Bump @mui/x-date-pickers from 6.19.2 to 6.19.5
      • Bump @types/node from 20.11.10 to 20.11.24
      • Bump @types/react from 18.2.48 to 18.2.61
      • Bump @types/react-dom from 18.2.18 to 18.2.19
      • Bump @types/validator from 13.11.8 to 13.11.9
      • Bump npm-check-updates from 16.14.14 to 16.14.15
      • Bump react-router-dom from 6.21.3 to 6.22.2
    • Mobile App:
      • Bump @react-navigation/drawer from 6.6.6 to 6.6.11
      • Bump @react-navigation/native from 6.1.9 to 6.6.14
      • Bump @react-navigation/native-stack from 6.9.17 to 6.9.22
      • Bump @react-navigation/stack from 6.3.20 to 6.3.25
      • Bump @types/validator from 13.11.8 to 13.11.9
      • Bump expo from 50.0.4 to 50.0.8
      • Bump expo-updates from 0.24.9 to 0.24.11
      • Bump i18n-js from 4.3.2 to 4.4.3
      • Bump react-native from 0.73.2 to 0.73.10
      • Bump react-native-dotenv from 3.4.9 to 3.4.11
      • Bump react-native-gesture-handler from 2.14.1 to 2.14.0
      • Bump @babel/core from 7.23.9 to 7.24.0
      • Bump @types/react from 18.2.48 to 18.2.61
      • Bump @typescript-eslint/eslint-plugin from 6.19.1 to 7.1.0
      • Bump @typescript-eslint/parser from 6.19.1 to 7.1.0
      • Bump eslint from 8.56.0 to 8.57.0
    • Full Changelog: v2.8...v2.9

  • 18th March, 2024 - BookCars 3.0 released
    • Reached 100% code coverage in the API
    • Upgrade to TypeScript 5.4
    • Fix an issue in checkout process
    • Under the hood updates
    • API:
      • Bump @types/cookie-parser from 1.4.6 to 1.4.7
      • Bump @types/node from 20.11.24 to 20.11.28
      • Bump @typescript-eslint/eslint-plugin from 7.1.0 to 7.2.0
      • Bump @typescript-eslint/parser from 7.1.0 to 7.2.0
      • Bump mongoose from 8.2.0 to 8.2.2
      • Bump nodemailer from 6.9.11 to 6.9.12
      • Bump typescript from 5.3.3 to 5.4.2
      • Bump npm-check-updates from 16.14.15 to 16.14.17
    • Backend and frontend:
      • Bump @mui/icons-material from 5.15.11 to 5.15.13
      • Bump @mui/material from 5.15.11 to 5.15.13
      • Bump @mui/x-data-grid from 6.19.5 to 6.19.6
      • Bump @mui/x-date-pickers from 6.19.5 to 6.19.7
      • Bump @types/node from 20.11.24 to 20.11.28
      • Bump @types/react from 18.2.61 to 18.2.67
      • Bump @types/react-dom from 18.2.19 to 18.2.22
      • Bump axios from 1.6.7 to 1.6.8
      • Bump npm-check-updates from 16.14.15 to 16.14.17
      • Bump react-router-dom from 6.22.2 to 6.22.3
      • Bump react-toastify from 10.0.4 to 10.0.5
    • Mobile App:
      • Bump @react-navigation/drawer from 6.6.11 to 6.6.14
      • Bump @react-navigation/native from 6.1.14 to 6.6.16
      • Bump @react-navigation/native-stack from 6.9.22 to 6.9.25
      • Bump @react-navigation/stack from 6.3.25 to 6.3.28
      • Bump axios from 1.6.7 to 1.6.8
      • Bump date-fns from 3.3.1 to 3.6.0
      • Bump expo from 50.0.8 to 50.0.13
      • Bump expo-updates from 0.24.11 to 0.24.12
      • Bump react-native from 0.73.4 to 0.73.5
      • Bump @types/react from 18.2.61 to 18.2.67
      • Bump @typescript-eslint/eslint-plugin from 7.1.0 to 7.2.0
      • Bump @typescript-eslint/parser from 7.1.0 to 7.2.0
      • Bump eslint-plugin-react from 7.33.2 to 7.34.1
      • Bump typescript from 5.3.3 to 5.4.2
    • Full Changelog: v2.9...v3.0

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Engineer
Morocco Morocco
I build innovative and scalable solutions for digital media. With several years of software engineering experience, I have a strong background in web, mobile and desktop development, as well as media asset management and digital asset management systems.

My strength lies in the development of innovative solutions and the ability to adapt them to different industries looking to streamline or automate their work process or data management.

I am passionate about learning new technologies and frameworks and applying them to solve complex and challenging problems. I am proficient in working with Node.js, React, React Native, TypeScript, C# and .NET among other languages and tools. My ultimate aim is to deliver high-quality software products that meet the requirements and expectations of our customers.

Open-source projects:

- Wexflow: .NET Workflow Engine and Automation Platform
- BookCars: Car Rental Platform with Mobile App
- Movin' In: Rental Property Management Platform with Mobile App
- Wexstream: Video Conferencing Platform
- wexCommerce: eCommerce Platform on Next.js

If you'd like to discuss any sort of opportunity, feel free to contact me through GitHub or LinkedIn.

Comments and Discussions

 
Questionunable to access api Pin
franck willy20-Mar-24 1:11
franck willy20-Mar-24 1:11 
AnswerRe: unable to access api Pin
Akram El Assas20-Mar-24 2:49
professionalAkram El Assas20-Mar-24 2:49 
GeneralRe: unable to access api Pin
franck willy21-Mar-24 5:27
franck willy21-Mar-24 5:27 
AnswerRe: unable to access api Pin
Akram El Assas21-Mar-24 6:56
professionalAkram El Assas21-Mar-24 6:56 
GeneralRe: unable to access api Pin
franck willy22-Mar-24 2:23
franck willy22-Mar-24 2:23 
AnswerRe: unable to access api Pin
Akram El Assas22-Mar-24 3:45
professionalAkram El Assas22-Mar-24 3:45 
GeneralRe: unable to access api Pin
franck willy22-Mar-24 6:39
franck willy22-Mar-24 6:39 
GeneralRe: unable to access api Pin
franck willy22-Mar-24 4:34
franck willy22-Mar-24 4:34 
AnswerRe: unable to access api Pin
Akram El Assas22-Mar-24 4:40
professionalAkram El Assas22-Mar-24 4:40 
QuestionMy vote of 5. Pin
GKP199229-Feb-24 18:08
professionalGKP199229-Feb-24 18:08 
AnswerRe: My vote of 5. Pin
Akram El Assas29-Feb-24 19:51
professionalAkram El Assas29-Feb-24 19:51 
QuestionDatabase connection error Pin
Develoopers19-Dec-23 4:56
Develoopers19-Dec-23 4:56 
AnswerRe: Database connection error Pin
Akram El Assas19-Dec-23 5:39
professionalAkram El Assas19-Dec-23 5:39 
GeneralRe: Database connection error Pin
Develoopers19-Dec-23 6:33
Develoopers19-Dec-23 6:33 
AnswerRe: Database connection error Pin
Akram El Assas19-Dec-23 6:45
professionalAkram El Assas19-Dec-23 6:45 
GeneralRe: Database connection error Pin
Develoopers19-Dec-23 11:01
Develoopers19-Dec-23 11:01 
QuestionVirtualmin Pin
Member 161347997-Nov-23 8:57
Member 161347997-Nov-23 8:57 
AnswerRe: Virtualmin Pin
Akram El Assas13-Nov-23 6:39
professionalAkram El Assas13-Nov-23 6:39 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA3-Nov-23 6:07
professionalȘtefan-Mihai MOGA3-Nov-23 6:07 
Questionbackend sign-in error Pin
hseinh9319-Sep-23 22:16
hseinh9319-Sep-23 22:16 
AnswerRe: backend sign-in error Pin
Akram El Assas20-Sep-23 1:27
professionalAkram El Assas20-Sep-23 1:27 
GeneralRe: backend sign-in error Pin
hseinh9320-Sep-23 21:55
hseinh9320-Sep-23 21:55 
AnswerRe: backend sign-in error Pin
Akram El Assas20-Sep-23 22:39
professionalAkram El Assas20-Sep-23 22:39 
GeneralRe: backend sign-in error Pin
hseinh9320-Sep-23 22:59
hseinh9320-Sep-23 22:59 
AnswerRe: backend sign-in error Pin
Akram El Assas20-Sep-23 23:46
professionalAkram El Assas20-Sep-23 23:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.