From 5badbdf14a6b8fc0206b114a6ff89729ce8f8e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dmitruk?= Date: Sun, 12 Jan 2025 00:39:04 +0100 Subject: [PATCH] feat: update README for Docker setup, add Dockerfile, docker-compose, and Nginx configuration --- README.md | 84 ++++++++++-------------------------------- backend/Dockerfile | 18 +++++++++ backend/app.py | 7 ++-- backend/pyproject.toml | 3 +- backend/uv.lock | 17 ++++++++- docker-compose.yml | 41 +++++++++++++++++++++ nginx.conf | 12 ++++++ 7 files changed, 112 insertions(+), 70 deletions(-) create mode 100644 backend/Dockerfile create mode 100644 docker-compose.yml create mode 100644 nginx.conf diff --git a/README.md b/README.md index d70ef67..93a8c5c 100644 --- a/README.md +++ b/README.md @@ -8,40 +8,17 @@ This project is a pharmacy application that includes user account management, pr Follow these steps to set up the project locally: -#### 1. **Clone the Repository** +#### Access the Docker Environment -Clone the project repository and navigate to the backend directory: +In VSCode, use the "Reopen in Container" option to seamlessly open the project inside the container. -```bash -git clone https://github.com/hackerman70000/pharmacy-app.git -``` - -#### 2. Access the Docker Environment - -For an optimal development experience, it is recommended to use Visual Studio Code with the Dev Containers extension. - -You can access the container in two ways: - -1. **Reopen in Container**: In VSCode, use the "Reopen in Container" option to seamlessly open the project inside the container. - - During container creation, you'll be prompted to enter e-mail credentials for the email notification system - -2. **Access via Terminal**: Alternatively, you can connect to the container directly through the terminal using the appropriate Docker commands. - -#### 3. **Create the Database** - -Use the `createdb` command to create the PostgreSQL database: - -```bash -createdb pharmacy_db -``` - -#### 4. **Navigate to `/backend`** +#### Navigate to `/backend` ```bash cd backend ``` -#### 5. **Apply Migrations** +#### Apply Migrations Run the following command to apply existing database migrations: @@ -49,7 +26,7 @@ Run the following command to apply existing database migrations: uv run flask db upgrade ``` -#### 6. **Run the Development Server** +#### Run the Development Server Start the Flask development server: @@ -66,9 +43,8 @@ uv run flask run To completely reset the database and its data: ```bash -uv run flask db downgrade base # Go back to empty state -uv run flask db upgrade # Recreate all tables -uv run flask seed products # Reseed with example data +uv run flask db downgrade base +uv run flask db upgrade ``` #### CLI Commands @@ -76,55 +52,33 @@ uv run flask seed products # Reseed with example data Available custom CLI commands: ```bash -uv run flask seed products # Add example pharmacy products to the database +uv run flask seed products ``` -#### PostgreSQL Database - -To inspect the database directly using PostgreSQL CLI: +### Tests -1. Connect to the database: +Run all tests: ```bash -psql pharmacy_db +uv run pytest ``` -2. Useful PostgreSQL commands: +To run tests in the Docker container: -```sql -\l # List all databases -\dt # List all tables in the current database -\d TABLE # Describe table structure -\du # List all users and their roles -\q # Quit psql -``` - -Example queries: - -```sql -SELECT * FROM users; # View all users -SELECT * FROM products; # View all products -SELECT * FROM cart; # View all cart items +```bash +docker compose exec backend uv run pytest ``` --- -### Common Issues and Solutions +## Production deployment -1. Migration issues: +Configure Environment Variables in `backend/.env` file with all necessary configuration. -To reset migrations: +Build and start services: ```bash -rm -rf migrations/ -uv run flask db init -uv run flask db migrate -m "Reset migrations" -uv run flask db upgrade +docker compose up --build -d ``` -2. **Order Email Notifications**: - If order confirmation emails are not being received: - - Check your spam folder - - Verify email credentials in .env file - - Ensure the Gmail App Password is correct - - Check the application logs for email-related errors \ No newline at end of file +Access the application at http://localhost (Port 80). All requests go through Nginx reverse proxy. diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..bba13bc --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.12-slim + +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + gcc \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install uv + +COPY . . +RUN uv sync + +ENV FLASK_APP=app.py +ENV FLASK_ENV=production + +CMD uv run flask db upgrade && \ + uv run gunicorn --bind 0.0.0.0:5000 'app:create_app()' \ No newline at end of file diff --git a/backend/app.py b/backend/app.py index 284bc16..4526960 100644 --- a/backend/app.py +++ b/backend/app.py @@ -2,10 +2,11 @@ from app.cli.commands import seed_cli from flask.cli import FlaskGroup -app = create_app() +application = create_app() +app = application cli = FlaskGroup(create_app=create_app) -app.cli.add_command(seed_cli) +application.cli.add_command(seed_cli) if __name__ == "__main__": - app.run(host='0.0.0.0', debug=True) \ No newline at end of file + application.run(host="0.0.0.0", debug=True) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 436af6d..cf17101 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "pytest>=8.3.4", "pytest-cov>=6.0.0", "pytest-mock>=3.14.0", + "gunicorn>=23.0.0", ] [dependency-groups] @@ -51,4 +52,4 @@ exclude_lines = [ "raise ImportError", ] show_missing = true -fail_under = 60 \ No newline at end of file +fail_under = 60 diff --git a/backend/uv.lock b/backend/uv.lock index da0c883..a48411c 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -119,7 +119,7 @@ name = "click" version = "8.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "platform_system == 'Windows'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } wheels = [ @@ -360,6 +360,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, ] +[[package]] +name = "gunicorn" +version = "23.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -539,6 +551,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 }, { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 }, { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 }, + { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 }, ] [[package]] @@ -768,6 +781,7 @@ dependencies = [ { name = "flask-mail" }, { name = "flask-migrate" }, { name = "flask-sqlalchemy" }, + { name = "gunicorn" }, { name = "psycopg2-binary" }, { name = "pyseto" }, { name = "pytest" }, @@ -789,6 +803,7 @@ requires-dist = [ { name = "flask-mail", specifier = ">=0.10.0" }, { name = "flask-migrate", specifier = "==4.0.7" }, { name = "flask-sqlalchemy", specifier = "==3.1.1" }, + { name = "gunicorn", specifier = ">=23.0.0" }, { name = "psycopg2-binary", specifier = "==2.9.10" }, { name = "pyseto", specifier = ">=1.8.0" }, { name = "pytest", specifier = ">=8.3.4" }, diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..236dc6f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +services: + backend: + build: + context: ./backend + dockerfile: Dockerfile + restart: always + env_file: + - ./backend/.env + depends_on: + - db + networks: + - internal + + db: + image: postgres:latest + restart: always + volumes: + - postgres_data:/var/lib/postgresql/data + env_file: + - ./backend/.env + networks: + - internal + + nginx: + image: nginx:latest + restart: always + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf + depends_on: + - backend + networks: + - internal + +volumes: + postgres_data: + +networks: + internal: + driver: bridge \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..4b5b7c1 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,12 @@ +server { + listen 80; + server_name _; + + location / { + proxy_pass http://backend:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} \ No newline at end of file