Automated Action e122f16dea Build complete blockchain-enabled carbon offset trading platform
Features implemented:
- User authentication with JWT tokens and role-based access (developer/buyer)
- Blockchain wallet linking and management with Ethereum integration
- Carbon project creation and management for developers
- Marketplace for browsing and purchasing carbon offsets
- Transaction tracking with blockchain integration
- Database models for users, projects, offsets, and transactions
- Comprehensive API with authentication, wallet, project, and trading endpoints
- Health check endpoint and platform information
- SQLite database with Alembic migrations
- Full API documentation with OpenAPI/Swagger

Technical stack:
- FastAPI with Python
- SQLAlchemy ORM with SQLite
- Web3.py for blockchain integration
- JWT authentication with bcrypt
- CORS enabled for frontend integration
- Comprehensive error handling and validation

Environment variables required:
- SECRET_KEY (JWT secret)
- BLOCKCHAIN_RPC_URL (optional, defaults to localhost)
2025-06-20 13:45:14 +00:00

175 lines
5.0 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import desc
from typing import Optional
from app.db.session import get_db
from app.core.deps import get_current_user, get_current_developer
from app.models.user import User
from app.models.carbon_project import CarbonProject
from app.models.carbon_offset import CarbonOffset
from app.schemas.carbon_project import (
CarbonProjectCreate,
CarbonProjectResponse,
CarbonProjectUpdate,
CarbonProjectListResponse
)
router = APIRouter()
@router.post("/", response_model=CarbonProjectResponse)
def create_project(
project: CarbonProjectCreate,
current_user: User = Depends(get_current_developer),
db: Session = Depends(get_db)
):
"""Create a new carbon offset project (Developer only)"""
# Validate project dates
if project.start_date >= project.end_date:
raise HTTPException(
status_code=400,
detail="Start date must be before end date"
)
# Create project
db_project = CarbonProject(
**project.dict(),
developer_id=current_user.id
)
db.add(db_project)
db.commit()
db.refresh(db_project)
# Create initial carbon offsets
db_offset = CarbonOffset(
serial_number=f"CO{db_project.id}-{project.total_credits_available}",
vintage_year=project.start_date.year,
quantity=project.total_credits_available,
project_id=db_project.id
)
db.add(db_offset)
db.commit()
return db_project
@router.get("/", response_model=CarbonProjectListResponse)
def list_projects(
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100),
project_type: Optional[str] = None,
verification_status: Optional[str] = None,
db: Session = Depends(get_db)
):
"""List all active carbon offset projects"""
query = db.query(CarbonProject).filter(CarbonProject.is_active == True)
if project_type:
query = query.filter(CarbonProject.project_type == project_type)
if verification_status:
query = query.filter(CarbonProject.verification_status == verification_status)
# Get total count
total = query.count()
# Apply pagination
projects = query.order_by(desc(CarbonProject.created_at)).offset(
(page - 1) * page_size
).limit(page_size).all()
return CarbonProjectListResponse(
projects=projects,
total=total,
page=page,
page_size=page_size
)
@router.get("/my-projects", response_model=CarbonProjectListResponse)
def get_my_projects(
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100),
current_user: User = Depends(get_current_developer),
db: Session = Depends(get_db)
):
"""Get projects created by the current developer"""
query = db.query(CarbonProject).filter(CarbonProject.developer_id == current_user.id)
total = query.count()
projects = query.order_by(desc(CarbonProject.created_at)).offset(
(page - 1) * page_size
).limit(page_size).all()
return CarbonProjectListResponse(
projects=projects,
total=total,
page=page,
page_size=page_size
)
@router.get("/{project_id}", response_model=CarbonProjectResponse)
def get_project(project_id: int, db: Session = Depends(get_db)):
"""Get a specific project by ID"""
project = db.query(CarbonProject).filter(
CarbonProject.id == project_id,
CarbonProject.is_active == True
).first()
if not project:
raise HTTPException(status_code=404, detail="Project not found")
return project
@router.put("/{project_id}", response_model=CarbonProjectResponse)
def update_project(
project_id: int,
project_update: CarbonProjectUpdate,
current_user: User = Depends(get_current_developer),
db: Session = Depends(get_db)
):
"""Update a project (Developer only - own projects)"""
project = db.query(CarbonProject).filter(
CarbonProject.id == project_id,
CarbonProject.developer_id == current_user.id
).first()
if not project:
raise HTTPException(status_code=404, detail="Project not found")
# Update project fields
update_data = project_update.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(project, field, value)
db.commit()
db.refresh(project)
return project
@router.delete("/{project_id}")
def delete_project(
project_id: int,
current_user: User = Depends(get_current_developer),
db: Session = Depends(get_db)
):
"""Delete a project (Developer only - own projects)"""
project = db.query(CarbonProject).filter(
CarbonProject.id == project_id,
CarbonProject.developer_id == current_user.id
).first()
if not project:
raise HTTPException(status_code=404, detail="Project not found")
# Soft delete - mark as inactive
project.is_active = False
db.commit()
return {"message": "Project deleted successfully"}