Update code via agent code generation

This commit is contained in:
Automated Action 2025-06-09 12:29:47 +00:00
parent a5fe775f3b
commit cb90be1c1f
19 changed files with 415 additions and 0 deletions

1
app/__init__.py Normal file
View File

@ -0,0 +1 @@
# E-commerce API application package

1
app/core/__init__.py Normal file
View File

@ -0,0 +1 @@
# Core module for the application

39
app/core/config.py Normal file
View File

@ -0,0 +1,39 @@
from typing import List, Union, Dict, Any, Optional
from pydantic import AnyHttpUrl, validator
from pydantic_settings import BaseSettings
import secrets
from pathlib import Path
class Settings(BaseSettings):
API_V1_STR: str = "/api/v1"
SECRET_KEY: str = secrets.token_urlsafe(32)
# 60 minutes * 24 hours * 8 days = 8 days
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
PROJECT_NAME: str = "E-Commerce API"
# CORS
BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = []
# Database
DB_DIR: Path = Path("/app") / "storage" / "db"
DB_PATH: Path = DB_DIR / "db.sqlite"
SQLALCHEMY_DATABASE_URL: str = f"sqlite:///{DB_PATH}"
@validator("BACKEND_CORS_ORIGINS", pre=True)
def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]:
if isinstance(v, str) and not v.startswith("["):
return [i.strip() for i in v.split(",")]
elif isinstance(v, (list, str)):
return v
raise ValueError(v)
class Config:
case_sensitive = True
env_file = ".env"
settings = Settings()
# Create the DB directory if it doesn't exist
settings.DB_DIR.mkdir(parents=True, exist_ok=True)

33
app/core/security.py Normal file
View File

@ -0,0 +1,33 @@
from datetime import datetime, timedelta
from typing import Any, Union, Optional
from jose import jwt
from passlib.context import CryptContext
from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
ALGORITHM = "HS256"
def create_access_token(
subject: Union[str, Any], expires_delta: Optional[timedelta] = None
) -> str:
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
to_encode = {"exp": expire, "sub": str(subject)}
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)

1
app/db/__init__.py Normal file
View File

@ -0,0 +1 @@
# Database module

5
app/db/base.py Normal file
View File

@ -0,0 +1,5 @@
# Import all the models here, so that Alembic can detect them
from app.db.base_class import Base
from app.models.user import User
from app.models.product import Product
from app.models.order import Order, OrderItem

13
app/db/base_class.py Normal file
View File

@ -0,0 +1,13 @@
from typing import Any
from sqlalchemy.ext.declarative import as_declarative, declared_attr
@as_declarative()
class Base:
id: Any
__name__: str
# Generate tablename automatically
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()

22
app/db/session.py Normal file
View File

@ -0,0 +1,22 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
# Create the SQLAlchemy engine
engine = create_engine(
settings.SQLALCHEMY_DATABASE_URL,
connect_args={"check_same_thread": False} # Only needed for SQLite
)
# Create a SessionLocal class
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Dependency to get DB session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

1
app/models/__init__.py Normal file
View File

@ -0,0 +1 @@
# Models package

40
app/models/order.py Normal file
View File

@ -0,0 +1,40 @@
from sqlalchemy import Column, String, Integer, Float, ForeignKey, DateTime, Enum
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
import enum
from app.db.base_class import Base
class OrderStatus(str, enum.Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
class Order(Base):
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
status = Column(Enum(OrderStatus), default=OrderStatus.PENDING, nullable=False)
total_amount = Column(Float, nullable=False)
shipping_address = Column(String, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
user = relationship("User", backref="orders")
items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
class OrderItem(Base):
id = Column(Integer, primary_key=True, index=True)
order_id = Column(Integer, ForeignKey("order.id"), nullable=False)
product_id = Column(Integer, ForeignKey("product.id"), nullable=False)
quantity = Column(Integer, nullable=False)
unit_price = Column(Float, nullable=False)
# Relationships
order = relationship("Order", back_populates="items")
product = relationship("Product")

16
app/models/product.py Normal file
View File

@ -0,0 +1,16 @@
from sqlalchemy import Column, String, Integer, Float, Text, Boolean, DateTime
from sqlalchemy.sql import func
from app.db.base_class import Base
class Product(Base):
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True, nullable=False)
description = Column(Text, nullable=True)
price = Column(Float, nullable=False)
stock = Column(Integer, nullable=False, default=0)
image_url = Column(String, nullable=True)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

15
app/models/user.py Normal file
View File

@ -0,0 +1,15 @@
from sqlalchemy import Boolean, Column, String, Integer, DateTime
from sqlalchemy.sql import func
from app.db.base_class import Base
class User(Base):
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
full_name = Column(String, index=True)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

1
app/schemas/__init__.py Normal file
View File

@ -0,0 +1 @@
# Schemas package

70
app/schemas/order.py Normal file
View File

@ -0,0 +1,70 @@
from typing import List, Optional
from pydantic import BaseModel, Field
from datetime import datetime
from app.models.order import OrderStatus
# OrderItem schemas
class OrderItemBase(BaseModel):
product_id: int
quantity: int = Field(..., gt=0)
class OrderItemCreate(OrderItemBase):
pass
class OrderItemUpdate(OrderItemBase):
pass
class OrderItemInDBBase(OrderItemBase):
id: int
order_id: int
unit_price: float
class Config:
from_attributes = True
class OrderItem(OrderItemInDBBase):
pass
# Order schemas
class OrderBase(BaseModel):
user_id: Optional[int] = None
shipping_address: Optional[str] = None
status: Optional[OrderStatus] = None
class OrderCreate(OrderBase):
user_id: int
shipping_address: str
items: List[OrderItemCreate]
class OrderUpdate(OrderBase):
status: Optional[OrderStatus] = None
class OrderInDBBase(OrderBase):
id: int
user_id: int
total_amount: float
status: OrderStatus
shipping_address: str
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class Order(OrderInDBBase):
items: List[OrderItem]
class OrderInDB(OrderInDBBase):
pass

47
app/schemas/product.py Normal file
View File

@ -0,0 +1,47 @@
from typing import Optional
from pydantic import BaseModel, Field
from datetime import datetime
# Shared properties
class ProductBase(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = None
stock: Optional[int] = None
image_url: Optional[str] = None
is_active: Optional[bool] = True
# Properties to receive on product creation
class ProductCreate(ProductBase):
name: str
price: float = Field(..., gt=0)
stock: int = Field(..., ge=0)
# Properties to receive on product update
class ProductUpdate(ProductBase):
pass
class ProductInDBBase(ProductBase):
id: int
name: str
price: float
stock: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# Additional properties to return via API
class Product(ProductInDBBase):
pass
# Additional properties stored in DB
class ProductInDB(ProductInDBBase):
pass

11
app/schemas/token.py Normal file
View File

@ -0,0 +1,11 @@
from typing import Optional
from pydantic import BaseModel
class Token(BaseModel):
access_token: str
token_type: str
class TokenPayload(BaseModel):
sub: Optional[int] = None

38
app/schemas/user.py Normal file
View File

@ -0,0 +1,38 @@
from typing import Optional
from pydantic import BaseModel, EmailStr, Field
# Shared properties
class UserBase(BaseModel):
email: Optional[EmailStr] = None
is_active: Optional[bool] = True
is_superuser: bool = False
full_name: Optional[str] = None
# Properties to receive via API on creation
class UserCreate(UserBase):
email: EmailStr
password: str = Field(..., min_length=8)
# Properties to receive via API on update
class UserUpdate(UserBase):
password: Optional[str] = Field(None, min_length=8)
class UserInDBBase(UserBase):
id: Optional[int] = None
class Config:
from_attributes = True
# Additional properties to return via API
class User(UserInDBBase):
pass
# Additional properties stored in DB
class UserInDB(UserInDBBase):
hashed_password: str

49
main.py Normal file
View File

@ -0,0 +1,49 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pathlib import Path
from app.api.routes import api_router
from app.core.config import settings
app = FastAPI(
title=settings.PROJECT_NAME,
description="E-commerce API for managing products, users, and orders",
version="0.1.0",
openapi_url="/openapi.json",
docs_url="/docs",
redoc_url="/redoc",
)
# Set up CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include the API router
app.include_router(api_router)
@app.get("/")
async def root():
"""
Root endpoint returning basic information about the API.
"""
return {
"title": settings.PROJECT_NAME,
"docs": f"{settings.API_V1_STR}/docs",
"health": "/health"
}
@app.get("/health", status_code=200)
async def health_check():
"""
Health check endpoint.
"""
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

12
requirements.txt Normal file
View File

@ -0,0 +1,12 @@
fastapi>=0.100.0
uvicorn>=0.23.0
pydantic>=2.0.0
pydantic-settings>=2.0.0
python-jose[cryptography]>=3.3.0
passlib[bcrypt]>=1.7.4
alembic>=1.11.0
sqlalchemy>=2.0.0
python-multipart>=0.0.5
email-validator>=2.0.0
python-dotenv>=1.0.0
ruff>=0.0.270