Backend API for FEG Power System Units — premium permanent magnet generator sales.
Built with FastAPI, SQLAlchemy 2.0 async, PostgreSQL, Alembic, and Cloudinary.
| Layer |
Technology |
| Language |
Python 3.13 |
| Framework |
FastAPI |
| Package manager |
uv |
| Database |
PostgreSQL 16 |
| ORM |
SQLAlchemy 2.0 (async) |
| Migrations |
Alembic |
| Auth |
JWT (access + refresh) |
| Password hashing |
bcrypt |
| Image storage |
Cloudinary |
| Containerisation |
Docker + Docker Compose |
| Logging |
structlog |
app/
├── api/
│ └── v1/
│ ├── router.py # Aggregates all feature routers
│ ├── auth/router.py # Register, login, refresh, logout, /me
│ ├── admin/router.py # Admin stats dashboard
│ ├── customers/router.py # Customer profile + order history
│ ├── products/router.py # Public catalog + admin CRUD
│ ├── cart/router.py # Cart management (authenticated)
│ ├── checkout/router.py # Checkout → order + Telegram URL
│ ├── orders/router.py # Admin order management
│ ├── tracking/router.py # Public tracking + admin updates
│ └── health/router.py # Liveness + DB connectivity probe
│
├── core/
│ ├── config.py # Pydantic-settings — all env vars
│ ├── constants.py # App-wide magic values
│ ├── exceptions.py # Domain exception hierarchy
│ ├── exception_handlers.py # Maps exceptions → HTTP responses
│ ├── security.py # bcrypt password hashing
│ ├── jwt.py # JWT creation and decoding
│ ├── logger.py # structlog — JSON prod / colour dev
│ └── dependencies.py # DI wiring — repos, services, auth, authz
│
├── db/
│ ├── base.py # DeclarativeBase + TimestampMixin
│ ├── database.py # Engine lifecycle (init / dispose)
│ └── session.py # Session factory + get_db dependency
│
├── models/
│ ├── user.py
│ ├── product.py
│ ├── cart.py # Cart + CartItem
│ ├── order.py # Order + OrderItem
│ └── tracking.py
│
├── repositories/ # Database I/O only — no business logic
│ ├── base.py
│ ├── user.py
│ ├── product.py
│ ├── cart.py
│ ├── order.py
│ └── tracking.py
│
├── schemas/ # Pydantic v2 request/response schemas
│ ├── base.py
│ ├── response.py # Standard { success, message, data } envelope
│ ├── user.py
│ ├── product.py
│ ├── cart.py
│ ├── order.py
│ └── tracking.py
│
├── services/ # All business logic
│ ├── auth_service.py
│ ├── product_service.py
│ ├── cart_service.py
│ ├── checkout_service.py
│ ├── order_service.py
│ └── tracking_service.py
│
├── integrations/
│ └── cloudinary.py # Upload, delete, replace image
│
├── utils/
│ ├── identifiers.py # Order number + tracking ID generators
│ ├── slugify.py # URL-safe slug from product name
│ ├── telegram.py # Pre-filled Telegram deep-link builder
│ └── pagination.py # PaginationParams dependency + meta
│
└── main.py # FastAPI app factory + lifespan
- Python 3.13+
- PostgreSQL 16
- uv package manager
- Cloudinary account
cp .env.example .env
# Fill in DATABASE_URL, JWT_SECRET_KEY, and Cloudinary credentials
uv run alembic upgrade head
4. Start the development server
uv run uvicorn app.main:app --reload
API: http://localhost:8000
Docs: http://localhost:8000/docs
docker compose up --build
Runs PostgreSQL 16 + API. Migrations execute automatically on container start.
| Method |
Path |
Auth |
Description |
| POST |
/api/v1/auth/register |
— |
Register customer |
| POST |
/api/v1/auth/login |
— |
Login, get tokens |
| POST |
/api/v1/auth/refresh |
— |
Refresh token pair |
| POST |
/api/v1/auth/logout |
Bearer |
Logout (stateless) |
| GET |
/api/v1/auth/me |
Bearer |
Current user |
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/customers/me |
Customer |
View profile |
| GET |
/api/v1/customers/me/orders |
Customer |
My orders |
| GET |
/api/v1/customers/me/orders/{id} |
Customer |
My order detail |
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/products |
— |
Browse catalog |
| GET |
/api/v1/products/{slug} |
— |
Product detail |
| POST |
/api/v1/products |
Admin |
Create product |
| PATCH |
/api/v1/products/{id} |
Admin |
Update product |
| POST |
/api/v1/products/{id}/image |
Admin |
Upload image |
| DELETE |
/api/v1/products/{id} |
Admin |
Delete product |
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/cart |
Customer |
View cart |
| POST |
/api/v1/cart/items |
Customer |
Add item |
| PATCH |
/api/v1/cart/items/{product_id} |
Customer |
Set quantity |
| DELETE |
/api/v1/cart/items/{product_id} |
Customer |
Remove item |
| DELETE |
/api/v1/cart |
Customer |
Clear cart |
| Method |
Path |
Auth |
Description |
| POST |
/api/v1/checkout |
Customer |
Place order from cart |
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/orders |
Admin |
All orders |
| GET |
/api/v1/orders/{id} |
Admin |
Any order |
| PATCH |
/api/v1/orders/{id}/status |
Admin |
Update status / ship |
| PATCH |
/api/v1/orders/{id}/notes |
Admin |
Admin notes |
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/tracking/{tracking_id} |
— |
Public shipment status |
| PATCH |
/api/v1/tracking/{tracking_id} |
Admin |
Update tracking |
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/health |
— |
Liveness + DB probe |
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/admin/stats |
Admin |
Dashboard counts |
Every endpoint returns the same envelope:
{
"success": true,
"message": "Product created successfully.",
"data": { "..." }
}
Errors:
{
"success": false,
"message": "Product not found.",
"data": null,
"errors": null
}
pending_payment → paid → preparing → packed → shipped → out_for_delivery → delivered
Setting status to shipped automatically generates: FEG-TRK-XXXXXXXXX
Format: FEG-ORD-YYYYMMDD-XXXXXX
Example: FEG-ORD-20260702-000124
# After modifying models
uv run alembic revision --autogenerate -m "describe_change"
uv run alembic upgrade head
See .env.example. Key variables:
| Variable |
Required |
Description |
DATABASE_URL |
✓ |
PostgreSQL async URL |
JWT_SECRET_KEY |
✓ |
Min 32 chars — use openssl rand -hex 32 |
CLOUDINARY_CLOUD_NAME |
✓ |
Cloudinary account |
CLOUDINARY_API_KEY |
✓ |
Cloudinary API key |
CLOUDINARY_API_SECRET |
✓ |
Cloudinary API secret |
ALLOWED_ORIGINS |
— |
JSON array: ["http://localhost:3000"] |