Skip to content
Merged

Dev #52

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/test_app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ jobs:
with:
python-version: '3.12.10'

- name: Install dependencies
- name: Install Poetry
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install poetry

- name: Install dependencies
run: |
poetry config virtualenvs.create false
poetry install --no-interaction --no-ansi --no-root

- name: Lint application
run: |
Expand Down
160 changes: 54 additions & 106 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,15 @@

REST API для управления рецептами, ингредиентами и списками покупок. Пользователь может сохранять рецепты, создавать из них списки покупок.

## 🛠️ Используемые технологии

## ⚙️ Функциональность:

- 📐 Аннотации типов и валидация данных

- 🗃️ Асинхронные запросы к БД с SQLAlchemy 2.0

- 🗝️ Аутентификация через `fastapi-users`

- 📦 API для работы с рецептами и ингредиентами

- 🔁 Связи:

`User ↔ Profile` (один к одному)
`User ↔ Recipes` (один ко многим)
`Recipes ↔ Products` (многие ко многим)

- 📄 Alembic миграции

- 📝 Автоматическая OpenAPI-документация (`/docs`, `/redoc`)

- 🐳 Docker/Docker Compose

- 🧪 Юнит и интеграционные тесты

- **Бэкенд**: FastAPI
- **ORM**: SQLAlchemy 2.0 + Alembic
- **Валидация**: Pydantic
- **СУБД**: PostgreSQL
- **Контейнеризация**: Docker + Docker Compose + Poetry
- **Тестирование**: Pytest
- **Фронтенд**: Jinja + HTML/CSS/JS (Free Template Simple House)

## 🚧 В разработке:

Expand All @@ -39,6 +23,52 @@ REST API для управления рецептами, ингредиента
`User ↔ ShoppingLists` (один ко многим)
`ShoppingLists ↔ Products` (многие ко многим)

## 📡 Эндпоинты

### 🔸 Auth
- `POST /api/v1/auth/login` — Аутентификация пользователя.
- `POST /api/v1/auth/logout` — Выход из системы.
- `POST /api/v1/auth/register` — Регистрация нового пользователя.
- `POST /api/v1/auth/request-verify-token` — Запрос токена верификации.
- `POST /api/v1/auth/verify` — Подтверждение email.
- `POST /api/v1/auth/forgot-password` — Запрос сброса пароля.
- `POST /api/v1/auth/reset-password` — Сброс пароля.

### 🔸 Users
- `GET /api/v1/users/me` — Получение текущего пользователя.
- `PATCH /api/v1/users/me` — Обновление профиля.
- `GET /api/v1/users/{id}` — Получение пользователя по ID.
- `PATCH /api/v1/users/{id}` — Обновление пользователя.
- `DELETE /api/v1/users/{id}` — Удаление пользователя.

### 🔸 Messages
- `GET /api/v1/messages` — Получение публичных сообщений.
- `GET /api/v1/messages/secrets` — Получение приватных сообщений.

### 🔸 Products
- `GET /api/v1/products/` — Получение списка продуктов.
- `POST /api/v1/products/` — Создание продукта.
- `GET /api/v1/products/{product_id}/` — Получение продукта по ID.
- `PUT /api/v1/products/{product_id}/` — Полное обновление продукта.
- `DELETE /api/v1/products/{product_id}/` — Удаление продукта.

### 🔸 Recipes
- `GET /api/v1/recipes/` — Получение списка рецептов.
- `POST /api/v1/recipes/` — Создание рецепта.
- `GET /api/v1/recipes/{recipe_id}/` — Получение рецепта по ID.
- `PUT /api/v1/recipes/{recipe_id}/` — Полное обновление рецепта.
- `PATCH /api/v1/recipes/{recipe_id}/` — Частичное обновление рецепта
- `DELETE /api/v1/recipes/{recipe_id}/` — Удаление рецепта.
- `POST /api/v1/recipes/upload/recipe-image` — Загрузка изображения рецепта.

### 🔸 Views
- `GET /Home` — Главная страница.
- `GET /login` — Страница входа.
- `GET /register` — Страница регистрации.
- `GET /profile` — Страница профиля.
- `GET /profile_info` — Информация о пользователе и его рецептах.
- `GET /recipes` — Страница создания рецепта.

## 🐳 Установка и запуск с Docker:

### 🔹 1. Клонируйте репозиторий и перейдите в директорию проекта:
Expand Down Expand Up @@ -114,86 +144,4 @@ make stop
### 🔹 7. 🧪 Запуск тестов:
```
make test
```

## ⚙️ Установка и запуск без Docker:

### 🔹 1. Клонируйте репозиторий и перейдите в директорию проекта:
```
git clone https://github.com/MaxBakshaev/FastAPI-Recipe-And-Grocery-Planner.git
```
```
cd FastAPI-Recipe-And-Grocery-Planner
```

### 🔹 2. Создайте и активируйте виртуальное окружение:
```
python -m venv venv
```

Для Linux или macOS:
```
source venv/bin/activate
```
Для Windows:
```
venv\Scripts\activate
```

### 🔹 3. Установите зависимости:
```
pip install -r requirements.txt
```

### 🔹 4. Настройка окружения:

4.1. Создайте БД вручную в PostgreSQL.

4.2. Укажите свои данные в `app/.env.template`:

✏️ Замените в APP_CONFIG__DB__URL:

- `user` — имя пользователя БД
- `pwd` — пароль
- `DBname` — название БД

🔑 Сгенерируйте секретные ключи и добавьте в APP_CONFIG__ACCESS_TOKEN__RESET_PASSWORD_TOKEN_SECRET и APP_CONFIG__ACCESS_TOKEN__VERIFICATION_TOKEN_SECRET:

```
python -c "import secrets; print(secrets.token_urlsafe(32))"
```

### 🔹 5. Перейдите в каталог `app` и примените миграции:
```
cd app
```
```
alembic upgrade head
```
Вернитесь в корневую директорию:
```
cd ..
```

### 🔹 6. Запустите сервер:
```
python app/main.py
```

Для отключения сервера используйте команду:
```
Ctrl + C
```

### 🔹 7. Откройте в браузере:

- 🏠 Приложение: http://127.0.0.1:8000/

- 📚 Swagger UI: http://127.0.0.1:8000/docs

- 📘 Redoc: http://127.0.0.1:8000/redoc

### 🔹 8. 🧪 Запуск тестов:
```
pytest app/tests
```
8 changes: 4 additions & 4 deletions app/actions/create_superuser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import contextlib
from os import getenv

from api.dependencies.authentication import get_users_db, get_user_manager
from core.authentication import UserManager
from core.models import db_helper, User
from core.schemas import UserCreate
from app.api.dependencies.authentication import get_users_db, get_user_manager
from app.core.authentication import UserManager
from app.core.models import db_helper, User
from app.core.schemas import UserCreate


get_users_db_context = contextlib.asynccontextmanager(get_users_db)
Expand Down
4 changes: 2 additions & 2 deletions app/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

from alembic import context

from core.config import settings
from core.models import Base
from app.core.config import settings
from app.core.models import Base

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
Expand Down
2 changes: 1 addition & 1 deletion app/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from fastapi import APIRouter

from core.config import settings
from app.core.config import settings
from .api_v1 import router as router_api_v1

router = APIRouter(
Expand Down
2 changes: 1 addition & 1 deletion app/api/api_v1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from fastapi import APIRouter, Depends
from fastapi.security import HTTPBearer

from core.config import settings
from app.core.config import settings

from .auth import router as auth_router
from .users import router as users_router
Expand Down
6 changes: 3 additions & 3 deletions app/api/api_v1/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

from fastapi import APIRouter

from core.schemas import UserCreate, UserRead
from app.core.schemas import UserCreate, UserRead

from .fastapi_users import fastapi_users_bearer
from api.dependencies.authentication import authentication_backend_bearer
from core.config import settings
from app.api.dependencies.authentication import authentication_backend_bearer
from app.core.config import settings

router = APIRouter(
prefix=settings.api.v1.auth,
Expand Down
6 changes: 3 additions & 3 deletions app/api/api_v1/fastapi_users.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from fastapi_users import FastAPIUsers

from core.models import User
from core.types.user_id import UserIdType
from app.core.models import User
from app.core.types.user_id import UserIdType

from api.dependencies.authentication import (
from app.api.dependencies.authentication import (
authentication_backend_bearer,
authentication_backend_cookie,
get_user_manager,
Expand Down
8 changes: 4 additions & 4 deletions app/api/api_v1/messages.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from typing import Annotated
from fastapi import APIRouter, Depends

from api.api_v1.fastapi_users import (
from app.api.api_v1.fastapi_users import (
current_active_user_bearer,
current_active_super_user_bearer,
)
from core.config import settings
from core.models import User
from core.schemas import UserRead
from app.core.config import settings
from app.core.models import User
from app.core.schemas import UserRead

router = APIRouter(
prefix=settings.api.v1.messages,
Expand Down
4 changes: 2 additions & 2 deletions app/api/api_v1/mixins.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from core.models import Recipe
from core.schemas import ProductInRecipe, RecipeResponse
from app.core.models import Recipe
from app.core.schemas import ProductInRecipe, RecipeResponse


def map_recipe_to_response(recipe: Recipe) -> RecipeResponse:
Expand Down
8 changes: 4 additions & 4 deletions app/api/api_v1/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from fastapi import APIRouter, Depends, HTTPException, Path, status
from sqlalchemy.ext.asyncio import AsyncSession

from crud import products
from core.models import db_helper
from core.schemas import Product, ProductCreate, ProductUpdate
from core.config import settings
from app.crud import products
from app.core.models import db_helper
from app.core.schemas import Product, ProductCreate, ProductUpdate
from app.core.config import settings

router = APIRouter(
prefix=settings.api.v1.products,
Expand Down
12 changes: 6 additions & 6 deletions app/api/api_v1/recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from typing import List
from sqlalchemy.ext.asyncio import AsyncSession

from api.api_v1.fastapi_users import current_active_user_bearer
from api.api_v1.mixins import map_recipe_to_response
from crud import recipes
from core.config import settings
from core.models import db_helper, User
from core.schemas import (
from app.api.api_v1.fastapi_users import current_active_user_bearer
from app.api.api_v1.mixins import map_recipe_to_response
from app.crud import recipes
from app.core.config import settings
from app.core.models import db_helper, User
from app.core.schemas import (
RecipeCreateRequest,
RecipeResponse,
RecipeUpdateRequest,
Expand Down
6 changes: 3 additions & 3 deletions app/api/api_v1/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from fastapi import APIRouter

from api.api_v1.fastapi_users import fastapi_users_bearer
from core.config import settings
from core.schemas import UserRead, UserUpdate
from app.api.api_v1.fastapi_users import fastapi_users_bearer
from app.core.config import settings
from app.core.schemas import UserRead, UserUpdate

router = APIRouter(
prefix=settings.api.v1.users,
Expand Down
2 changes: 1 addition & 1 deletion app/api/dependencies/authentication/access_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from fastapi import Depends

from core.models import db_helper, AccessToken
from app.core.models import db_helper, AccessToken

if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession
Expand Down
2 changes: 1 addition & 1 deletion app/api/dependencies/authentication/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from fastapi_users.authentication import AuthenticationBackend

from .strategy import get_database_strategy
from core.authentication import bearer_transport, cookie_transport
from app.core.authentication import bearer_transport, cookie_transport

authentication_backend_bearer = AuthenticationBackend(
name="access-tokens-db",
Expand Down
4 changes: 2 additions & 2 deletions app/api/dependencies/authentication/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from fastapi import Depends
from fastapi_users.authentication.strategy.db import DatabaseStrategy

from core.config import settings
from app.core.config import settings
from .access_tokens import get_access_tokens_db

if TYPE_CHECKING:
from core.models import AccessToken
from app.core.models import AccessToken
from fastapi_users.authentication.strategy.db import AccessTokenDatabase


Expand Down
2 changes: 1 addition & 1 deletion app/api/dependencies/authentication/user_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from fastapi import Depends

from core.authentication import UserManager
from app.core.authentication import UserManager
from .users import get_users_db

if TYPE_CHECKING:
Expand Down
2 changes: 1 addition & 1 deletion app/api/dependencies/authentication/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from fastapi import Depends

from core.models import db_helper, User
from app.core.models import db_helper, User

if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession
Expand Down
Loading