Skip to content
Merged
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
3 changes: 1 addition & 2 deletions backend/database.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# app/database.py
import sys

from sqlalchemy import event

from constants import DATABASE_URL, TEST_DATABASE_URL
from sqlalchemy import event
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Expand Down
6 changes: 4 additions & 2 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from database import create_tables
from fastapi import FastAPI
from routes import good, good_category, login
from routes import good, good_category, login, payment, recipient


@asynccontextmanager
Expand All @@ -23,10 +23,12 @@ async def lifespan(_: FastAPI):

app.include_router(good_category.router, prefix="/api/v1/good-categories", tags=["Категории товаров"])
app.include_router(good.router, prefix="/api/v1/goods", tags=["Товары"])

app.include_router(payment.router, prefix="/api/v1/payments", tags=["Методы оплаты"])
app.include_router(recipient.router, prefix="/api/v1/recipients", tags=["Получатели"])
app.include_router(login.router, prefix="/api/v1/auth", tags=["Авторизация"])



@app.get("/")
async def root():
return {"detail": "Welcome to the API! Go to /docs to see the documentation."}
Empty file added backend/models/basket.py
Empty file.
Empty file added backend/models/checkout.py
Empty file.
Empty file added backend/models/delivery.py
Empty file.
12 changes: 12 additions & 0 deletions backend/models/payment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from database import Base
from sqlalchemy import Column, Integer, LargeBinary, String, Text


class PaymentMethods(Base):
__tablename__ = "payment_methods"

id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
image = Column(String, nullable=True)

16 changes: 16 additions & 0 deletions backend/models/recipient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from database import Base
from sqlalchemy import Column, ForeignKey, Integer, String, Text


class Recipients(Base):
__tablename__ = "recipients"

id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'))
first_name = Column(String(50), nullable=False)
last_name = Column(String(50), nullable=True)
middle_name = Column(String(50), nullable=True)
address = Column(String(250), nullable=False)
zipcode = Column(String(50), nullable=True)
phone = Column(String(50), nullable=False)
email = Column(String(50), nullable=True)
Empty file added backend/models/transaction.py
Empty file.
5 changes: 2 additions & 3 deletions backend/models/user.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from datetime import datetime
from enum import Enum as PyEnum

from database import Base
from sqlalchemy import Column, Integer, String, DateTime, Enum

from enum import Enum as PyEnum
from sqlalchemy import Column, DateTime, Enum, Integer, String


class UserRole(PyEnum):
Expand Down
3 changes: 1 addition & 2 deletions backend/routes/good.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from utils.auth import verify_token
from constants import GOOD_PAGE_SIZE as PAGE_SIZE
from database import get_db
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from models.good import Goods
from schemas import GoodCreate, GoodModel
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from utils.auth import verify_token
from validators import validate_category_exists

router = APIRouter()
Expand Down
2 changes: 1 addition & 1 deletion backend/routes/good_category.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# app/routes/good_category.py
from utils.auth import verify_token
from constants import GOOD_CATEGORY_PAGE_SIZE as PAGE_SIZE
from database import get_db
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from models.category import GoodCategory
from schemas import GoodCategoryCreate, GoodCategoryModel
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from utils.auth import verify_token
from validators import (validate_category_exists, validate_category_name,
validate_category_name_update,
validate_parent_category, validate_parent_itself)
Expand Down
41 changes: 29 additions & 12 deletions backend/routes/login.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
# app/routes/login.py
from datetime import datetime, timedelta
from typing import Optional

import jwt
from constants import ALGORITHM, SECRET_KEY
from database import get_db, is_testing
from fastapi import (APIRouter, Cookie, Depends, Header, HTTPException,
Request, Response)
from fastapi.security import HTTPAuthorizationCredentials
from models.user import OTP, User
from pydantic.v1 import NotNoneError
from schemas import UserCreate, UserLogin, UserVerify
from sqlalchemy import select

from database import get_db
from fastapi import APIRouter, Depends, HTTPException, Response
from schemas import UserLogin, UserVerify
from sqlalchemy.ext.asyncio import AsyncSession
from database import is_testing

from models.user import OTP
from utils.auth import send_verification_email, generate_code, generate_access_token, generate_refresh_token
from utils.auth import (generate_access_token, generate_code,
generate_refresh_token, security,
send_verification_email)

router = APIRouter()


@router.post("/login")
async def login(user_login: UserLogin, db: AsyncSession = Depends(get_db)):
async def login(user_login: UserLogin,
db: AsyncSession = Depends(get_db)):
"""Вход пользователя"""
otp = generate_code()
expiration = datetime.now() + timedelta(minutes=5)

db_otp = OTP(email=user_login.email, otp=otp, expiration=expiration)
db_otp = OTP(email=str(user_login.email), otp=otp, expiration=expiration)
db.add(db_otp)
await db.commit()

if is_testing:
return {"otp": otp}

send_verification_email(user_login.email, "Код для входа в аккаунт", otp)
send_verification_email(str(user_login.email), "Код для входа в аккаунт", otp)
return {"message": "OTP sent to your email."}


Expand Down Expand Up @@ -61,5 +66,17 @@ async def confirm(user_verify: UserVerify, response: Response, db: AsyncSession
samesite="strict", # Ограничьте доступ к этому домену
)

result = await db.execute(select(User).filter(User.email == user_verify.email))
db_user = result.scalar_one_or_none()

if not db_user:
db_user = User(email=str(user_verify.email),
created_at=datetime.utcnow())
db.add(db_user)
db_user.last_login = datetime.utcnow()

await db.commit()
await db.refresh(db_user)

# Возврат access-токена
return {"access_token": access_token}
71 changes: 71 additions & 0 deletions backend/routes/payment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from database import get_db
from fastapi import APIRouter, Depends, HTTPException
from models.payment import PaymentMethods
from schemas import PaymentMethodCreate, PaymentMethodModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from utils.auth import verify_token

router = APIRouter()

@router.get("")
async def get_payments(db: AsyncSession = Depends(get_db)):
"""Получение списка методов оплаты"""
payments = (await db.execute(select(PaymentMethods))).scalars().all()

return {
"items": [PaymentMethodModel.model_validate(payment) for payment in payments],
}


@router.post("", response_model=PaymentMethodModel)
async def create_payment(payment: PaymentMethodCreate, db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Добавление метода оплаты"""

db_payment = PaymentMethods(**payment.model_dump())
db.add(db_payment)
await db.commit()
await db.refresh(db_payment)

return PaymentMethodModel.model_validate(db_payment)


@router.get("/{payment_id}", response_model=PaymentMethodModel)
async def get_payment(payment_id: int, db: AsyncSession = Depends(get_db)):
"""Получение метода оплаты по идентификатору"""
payment = await db.get(PaymentMethods, payment_id)
if payment is None:
raise HTTPException(status_code=404, detail="Payment method not found")

return PaymentMethodModel.model_validate(payment)


@router.patch("/{payment_id}", response_model=PaymentMethodModel)
async def update_payment(payment_id: int, payment: PaymentMethodCreate, db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Обновление товара"""
db_payment = await db.get(PaymentMethods, payment_id)
if db_payment is None:
raise HTTPException(status_code=404, detail="Payment method not found")

for key, value in payment.model_dump().items():
setattr(db_payment, key, value)

await db.commit()
await db.refresh(db_payment)

return PaymentMethodModel.model_validate(db_payment)


@router.delete("/{payment_id}")
async def delete_good(payment_id: int, db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Удаление товара"""
db_payment = await db.get(PaymentMethods, payment_id)
if db_payment is None:
raise HTTPException(status_code=404, detail="Payment method not found")

await db.delete(db_payment)
await db.commit()
return {"detail": "Payment method deleted"}
124 changes: 124 additions & 0 deletions backend/routes/recipient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from database import get_db
from fastapi import APIRouter, Depends, HTTPException
from models.recipient import Recipients
from models.user import User, UserRole
from schemas import RecipientCreate, RecipientEdit, RecipientModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from utils.auth import verify_token

router = APIRouter()


@router.get("")
async def get_recipients(db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Получение всех получателей, если запрос делает админ (или продавец), и только своих, если - пользователь"""
user_email = user_data.get("sub")
result = await db.execute(select(User).filter(User.email == user_email))
db_user = result.scalars().first()

if db_user.role in [UserRole.ADMIN, UserRole.SELLER]:
result_recipients = await db.execute(select(Recipients))
else:
result_recipients = await db.execute(
select(Recipients).filter(Recipients.user_id == db_user.id)
)

recipients = result_recipients.scalars().all()

return {
"items": [RecipientModel.model_validate(recipient) for recipient in recipients],
}


@router.post("", response_model=RecipientModel)
async def add_recipients(payment: RecipientCreate, db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Добавление получателя"""
user_email = user_data.get("sub")
result = await db.execute(select(User).filter(User.email == user_email))
db_user = result.scalars().first()

if db_user.role in [UserRole.ADMIN, UserRole.SELLER]:
user_id = payment.user_id
else:
user_id = db_user.id

new_recipient = Recipients(
user_id=user_id,
first_name=payment.first_name,
last_name=payment.last_name,
middle_name=payment.middle_name,
address=payment.address,
zipcode=payment.zipcode,
phone=payment.phone,
email=str(payment.email),
)

db.add(new_recipient)
await db.commit()
await db.refresh(new_recipient)

return RecipientModel.model_validate(new_recipient)


@router.get("/{recipient_id}", response_model=RecipientModel)
async def get_payment(recipient_id: int, db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Получение метода оплаты по идентификатору"""
recipient = await db.get(Recipients, recipient_id)
if recipient is None:
raise HTTPException(status_code=404, detail="Recipient not found")

user_email = user_data.get("sub")
result = await db.execute(select(User).filter(User.email == user_email))
db_user = result.scalars().first()

if recipient.user_id != db_user.id and db_user.role not in [UserRole.ADMIN, UserRole.SELLER]:
raise HTTPException(status_code=404, detail="Recipient not found")
return RecipientModel.model_validate(recipient)


@router.patch("/{recipient_id}", response_model=RecipientModel)
async def update_recipient(recipient_id: int, recipient: RecipientEdit, db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Обновление товара"""
db_recipient = await db.get(Recipients, recipient_id)
if db_recipient is None:
raise HTTPException(status_code=404, detail="Recipient not found")

user_email = user_data.get("sub")
result = await db.execute(select(User).filter(User.email == user_email))
db_user = result.scalars().first()

if db_recipient.user_id != db_user.id and db_user.role not in [UserRole.ADMIN, UserRole.SELLER]:
raise HTTPException(status_code=404, detail="Recipient not found")

for key, value in recipient.model_dump().items():
setattr(db_recipient, key, value)

await db.commit()
await db.refresh(db_recipient)

return RecipientModel.model_validate(db_recipient)


@router.delete("/{recipient_id}")
async def delete_recipient(recipient_id: int, db: AsyncSession = Depends(get_db),
user_data: dict = Depends(verify_token)):
"""Удаление товара"""
db_recipient = await db.get(Recipients, recipient_id)
if db_recipient is None:
raise HTTPException(status_code=404, detail="Recipient not found")

user_email = user_data.get("sub")
result = await db.execute(select(User).filter(User.email == user_email))
db_user = result.scalars().first()

if db_recipient.user_id != db_user.id and db_user.role not in [UserRole.ADMIN, UserRole.SELLER]:
raise HTTPException(status_code=404, detail="Recipient not found")

await db.delete(db_recipient)
await db.commit()
return {"detail": "Recipient deleted"}
Loading
Loading