keysmusician / Hbnb

The completed version of the "AirBnB clone" Holberton School project.

Home Page:https://hbnb.fly.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Holberton School HBnB logo

Hbnb

The completed version of the "AirBnB clone" Holberton School project.

See it on Fly.io here: hbnb.fly.dev/

Table of Contents

Generated by gh-md-toc

About

Hbnb is a web application designed to mimic Airbnb. In this final version, I extended the project to try to recreate the feel of Airbnb's current web experience. The project was divided into 7 parts:

  1. The Console
  2. Web Static
  3. Database
  4. Deploy Static
  5. Web Framework
  6. REST API
  7. Web Dynamic

Technologies

This project was developed with the following tools:

  • Platform: Debian GNU/Linux 12 (bookworm)
  • Application Codebase: Python 3
    • ORM: SQLAlchemy
    • Web Framework: Flask 3
  • Linter: PEP8
  • Database: MariaDB
  • Web Deployment: Docker
  • Frontend Stack
    • HTML5
    • CSS3
    • JavaScript
      • React

Installation

Note: In this final personalized version of Hbnb, I have migrated to other technologies that were not part of the original assignment. To try any part of this project yourself:

  1. Clone this repository: git clone https://github.com/keysmusician/Hbnb

  2. Install dependencies:

    • Docker
    • Python3
    • PDM (optional, for Python package management)

    If you prefer, you can run the project locally without Docker by installing the necessary Python dependencies listed in pyproject.toml.

    Note: Not all dependencies are needed for every section of this project.

  3. Start the Docker container:

    docker compose up --detach
    
  4. Start the React development server:

    cd frontend
    npm run dev
    
  5. Visit http://localhost:8000/ to view the site.

[TODO: rewrite this section once set up] x. To use the database storage engine: x. To run the database storage tests: [ENDTODO]

The Console

The Console was the first stage of the project. In this stage, we wrote classes for representing users and listings, a file storage engine for saving and recalling data between interactive sessions, as well as a command interpreter (the console proper), Hbnb CLI, for easily managing our data. The Hbnb CLI provides a backend interface to our storage engine(s).

Hbnb CLI

The Hbnb CLI (command line interpreter) provides a convenient command line interface specifically to manage (add, delete, modify, etc.) Hbnb data. It offers an improved workflow over alternatives such as embedding data into source code, manually managing a data file, or using the Python interpreter to manage the data.

Commands

Bracketed arguments are optional.

  • all [CLASS] - Show all objects.
  • create CLASS - Create a new object.
  • destroy CLASS ID - Destroy a specified instance.
  • help [COMMAND] - Get information about a command.
  • quit - Close an interactive session. Also quit with ^-D or EOF.
  • show CLASS ID - Display a single instance.
  • update CLASS ID ATTRIBUTE VALUE - Edit attributes of an instance.

Usage

Start an interactive Hbnb CLI session by executing console.py:

./console.py

If it runs successfully, it will display the prompt and await input:

(hbnb)

Simply type any valid command(s) listed above. Type quit to exit the interactive session.

The Hbnb CLI may also be used non-non-interactively by piping input to it from a shell:

$ echo "help" | ./console.py

Examples

(hbnb) help

Documented commands (type help <topic>):
========================================
EOF  all  create  destroy  help  quit  show  update

(hbnb) create Place
aba364fd-8b9e-4c4b-b865-8db6a6e9bc03

(hbnb) all
["[Place] (aba364fd-8b9e-4c4b-b865-8db6a6e9bc03) {'created_at': datetime.datetime(2021, 7, 1, 13, 29, 27, 264673), 'id': 'aba364fd-8b9e-4c4b-b865-8db6a6e9bc03', 'updated_at': datetime.datetime(2021, 7, 1, 13, 29, 27, 264832)}"]
(hbnb)

Web Static

The Web Static part of this project consisted of designing the website HTML and CSS. The majority of the website is rendered serverside using Jinja templates.

Database

In the Database stage, we built a second storage engine: database storage. This engine uses a MySQL database and SQLAlchemy to manage data persistence. We introduced the following environment variables:

  • HBNB_MYSQL_USER
  • HBNB_MYSQL_PWD
  • HBNB_MYSQL_HOST
  • HBNB_MYSQL_DB
  • HBNB_TYPE_STORAGE

If HBNB_TYPE_STORAGE = db, the database storage engine will be used. The values of the other environment variables are used to establish a connection to a MySQL database. Consequently, the specified database must exist and contain the expected tables. Both storage engines' classes have the same methods implemented to provide seamless toggling between them. The SQLAlchemy ORM required significant additions to our models in order to properly link them to a database.

setup_mysql_dev.sql sets up a development environment database and user. Use the following environment variables to use the development environment: HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db

setup_mysql_test.sql sets up a test database and user. Use this database and user when running tests. Use the following environment variables to use the test environment: HBNB_MYSQL_USER=hbnb_test HBNB_MYSQL_PWD=hbnb_test_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_test_db HBNB_TYPE_STORAGE=db

Deploy Static

In this stage, we set up an Nginx web server and deployed our static files. I have containerized the project in Docker, and deployed the container to Fly.io.

Web Framework

In the Web Framework stage we created a dynamic web application using Flask as our frontend application framework. We learned how to set up routes in Flask and create Jinja templates. That allowed us to create dynamic HTML using data pulled from our MySQL database.

The following environment variables determine the host and port the development server will use:

  • HBNB_FRONTEND_HOST
  • HBNB_FRONTEND_PORT

The recommended way to serve the frontend locally is to launch Flask's development server with FLASK_APP=frontend/hbnb.py FLASK_DEBUG=1 flask run. By default, Flask binds to localhost:5000. You may specify a host and port or use the environment variables described above with FLASK_APP=web_flask/hbnb.py FLASK_DEBUG=1 flask run --host="$HBNB_FRONTEND_HOST" --port "$HBNB_FRONTEND_PORT".

Alternatively, the server may be launched from the root directory of this repository with python3 -m web_flask.hbnb. Unless specified in the environment variables, the host and port default to 0.0.0.0 and 5001 respectively.

Notes on launching the web application server:

  • Executing with ./web_flask/hbnb.py will not use your virtual environment's version of python. In addition:
  • Executing with a python interpreter and path (relative or absolute) such as python ./web_flask/hbnb.py won't work because the modules being imported won't be found. hbnb.py needs to be imported as a module from the root of the project directory.

REST API

In this section of the project, we built a REST API. Our Flask blueprint and views for the API can be found in the api directory. Our API base URL is <host>/api/v1/.

Usage

Note that for the purposes of this project, we hosted locally with the built in development server that comes with Flask. The following environment variables determine the host and port the development server will use:

  • HBNB_API_HOST
  • HBNB_API_PORT

Unless specified, the host and port default to 0.0.0.0 and 5002 respectively. To try this API yourself, follow the installation steps, launch our API on the Flask development server, and go to http://0.0.0.0:5002/api/v1/<endpoint>. A successful request returns JSON.

Example

Running the development server:

$ python3 -m api.v1.app
 * Serving Flask app "app" (lazy loading)
. . .
 * Running on http://0.0.0.0:5002/ (Press CTRL+C to quit)

Requesting an endpoint:

$ curl http://0.0.0.0:5002/api/v1/stats
{
  "amenities": 1,
  "cities": 1,
  "places": 1,
  "reviews": 1,
  "states": 1,
  "users": 1
}

Endpoints

The following are the endpoints we defined and their corresponding supported HTTP methods:

Endpoint Methods
/amenities/ GET, POST
/amenities/<amenity_id> DELETE, GET, PUT
/cities/<city_id> DELETE, GET, PUT
/cities/<city_id>/places GET, POST
/places/<place_id> DELETE, GET, PUT
/places/<place_id>/reviews GET, POST
/reviews/<review_id> DELETE, GET, PUT
/states/ GET, POST
/states/<state_id> DELETE, GET, PUT
/states/<state_id>/cities GET, POST
/status GET
/summary * GET
/users/ GET, POST
/users/<user_id> DELETE, GET, PUT

* Renamed from /stats in the Holberton School project.

Web Dynamic

In this final phase of the project, we wrote jQuery scripts which dynamically update portions of the website. We added:

  • An API status indicator
  • Dynamic display of selected amenities
  • Frontend requests to the API for search results
  • Dynamically updated HTML based on the API’s response

Because the HTML is now being dynamically updated client-side, the frontend Flask application server is no longer strictly necessary; The frontend could be written entirely in JavaScript. However, the server provides the advantage of ensuring the initial landing page load is fast and pre-rendered, as well as search engine optimized, since bots, which typically don't execute JavaScript, would not be able to parse the full page it if was rendered client-side. One drawback is that it creates redundancy, because any changes to the results section of the home page's Jinja template would require an equivalent change on the client-side (in the JavaScript which modifies the DOM after a search). The same code has to be written twice: in Jinja, and in JavaScript. However, while it violates the DRY principle, I have opted to keep the frontend application server for the given reasons.

Application Architecture

Holberton School allowed us three host devices for use in deploying our Airbnb clones. Using these hosts, we configured our application architecture as an internet-facing load balancer in front of a primary-replica pair.

Application architecture diagram initially used for Hbnb

Our servers were complete with firewalls, monitoring, TLS/SSL, production-grade web and application servers, and database synchronization.

Our access to those hosts has since expired, so I have now deployed the project on Fly.io, but with a simplified application architecture (due to the limitations of Fly's free tier). I am running the entire application in a single Docker container. I'm still using Nginx for the web server and Gunicorn for the application servers. TLS/SSL is handled by Fly. Nginx proxies requests between the Internet and the two Gunicorn application servers for the frontend and API.

What's Next?

There are a few improvements I can implement to make this project more robust and similar to Airbnb's website:

  • Switch over to the database storage engine (the deployed version uses the file storage engine).
  • Place ratings
  • Images of places
  • Improved styling and content of places pages and 404 page
  • User accounts
  • Map view
  • Migrate to client-side rendering to consolidate duplicate code (Jinja and JavaScript both rendering the same content)

Authors

V1 authors:

Alexa Orrico - Github / Twitter

Jennifer Huang - Github / Twitter

V2 Authors:

Joann Vuong

V3 Authors:

Justin Masayda @keysmusician

Carson Stearn @krytech

V4 Authors:

Justin Masayda @keysmusician

License

All rights reserved.

About

The completed version of the "AirBnB clone" Holberton School project.

https://hbnb.fly.dev/


Languages

Language:Python 62.7%Language:TypeScript 26.6%Language:CSS 5.6%Language:Jinja 2.8%Language:Shell 1.5%Language:Dockerfile 0.6%Language:JavaScript 0.2%