Skip to content
Open
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
141 changes: 140 additions & 1 deletion apps/backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.openapi.utils import get_openapi
from contextlib import asynccontextmanager
import logging

Expand All @@ -13,6 +14,8 @@
from .routes import auth, users, activities, contacts, suggestions
from .middleware import setup_middleware
from .exceptions import setup_exception_handlers
from .schemas.errors import COMMON_ERROR_RESPONSES
from .schemas.rate_limits import RATE_LIMIT_DOCUMENTATION


# Configure logging
Expand Down Expand Up @@ -45,13 +48,149 @@ def create_app() -> FastAPI:

app = FastAPI(
title="La Vida Luca API",
description="API pour la plateforme collaborative La Vida Luca",
description="API for La Vida Luca platform - farm network and educational platform",
version="1.0.0",
lifespan=lifespan,
openapi_url="/openapi.json",
docs_url="/docs" if settings.ENVIRONMENT != "production" else None,
redoc_url="/redoc" if settings.ENVIRONMENT != "production" else None,
)

def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="La Vida Luca API",
version="1.0.0",
description="""
# La Vida Luca API

Complete API documentation for La Vida Luca platform - a farm network and educational platform connecting people with sustainable agriculture and rural life learning experiences.

## Features

- **User Authentication**: JWT-based authentication system
- **Educational Activities**: Create, discover, and participate in learning activities
- **AI-Powered Suggestions**: Personalized activity recommendations using OpenAI
- **Contact Management**: Public contact forms and admin management
- **User Profiles**: Comprehensive user management and profiles

## Authentication

Most endpoints require authentication using JWT Bearer tokens. Include your token in the Authorization header:

```
Authorization: Bearer <your-jwt-token>
```

Get your token by calling the `/api/v1/auth/login` endpoint with valid credentials.

## Rate Limiting

This API implements comprehensive rate limiting. See the Rate Limiting section below for details.

## Error Handling

All errors follow a consistent format with appropriate HTTP status codes and descriptive messages.
""",
routes=app.routes,
)

# Add logo
openapi_schema["info"]["x-logo"] = {
"url": "https://la-vida-luca.vercel.app/logo.png"
}

# Add contact information
openapi_schema["info"]["contact"] = {
"name": "La Vida Luca Support",
"email": "support@lavidaluca.com",
"url": "https://la-vida-luca.vercel.app"
}

# Add license information
openapi_schema["info"]["license"] = {
"name": "MIT License",
"url": "https://opensource.org/licenses/MIT"
}

# Add server information
openapi_schema["servers"] = [
{
"url": "https://api.lavidaluca.com",
"description": "Production server"
},
{
"url": "https://staging-api.lavidaluca.com",
"description": "Staging server"
},
{
"url": "http://localhost:8000",
"description": "Development server"
}
]

# Add security schemes
openapi_schema["components"]["securitySchemes"] = {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "JWT token for API authentication. Include 'Bearer ' prefix."
}
}

# Add common error response schemas
if "components" not in openapi_schema:
openapi_schema["components"] = {}
if "responses" not in openapi_schema["components"]:
openapi_schema["components"]["responses"] = {}

# Add error response schemas
openapi_schema["components"]["responses"].update({
"BadRequest": COMMON_ERROR_RESPONSES[400],
"Unauthorized": COMMON_ERROR_RESPONSES[401],
"Forbidden": COMMON_ERROR_RESPONSES[403],
"NotFound": COMMON_ERROR_RESPONSES[404],
"Conflict": COMMON_ERROR_RESPONSES[409],
"ValidationError": COMMON_ERROR_RESPONSES[422],
"TooManyRequests": COMMON_ERROR_RESPONSES[429],
"InternalServerError": COMMON_ERROR_RESPONSES[500],
"ServiceUnavailable": COMMON_ERROR_RESPONSES[503]
})

# Add tags for organization
openapi_schema["tags"] = [
{
"name": "Authentication",
"description": "User authentication and token management"
},
{
"name": "User Management",
"description": "User profile and account management"
},
{
"name": "Activities",
"description": "Educational activities and learning experiences"
},
{
"name": "Contact",
"description": "Contact forms and communication"
},
{
"name": "AI Suggestions",
"description": "AI-powered activity recommendations"
}
]

# Add rate limiting documentation as extension
openapi_schema["x-rate-limiting"] = RATE_LIMIT_DOCUMENTATION

app.openapi_schema = openapi_schema
return app.openapi_schema

app.openapi = custom_openapi

# Setup middleware
setup_middleware(app)

Expand Down
2 changes: 1 addition & 1 deletion apps/backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pydantic-settings==2.1.0
gunicorn==21.2.0

# Database
sqlalchemy==2.0.23
sqlalchemy>=1.4.42,<2.0.0
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change downgrades SQLAlchemy from 2.0.23 to a version constraint that excludes 2.x entirely. This appears to be a breaking change that could cause compatibility issues with existing code written for SQLAlchemy 2.x. Consider if this change is intentional and ensure all SQLAlchemy usage in the codebase is compatible with 1.x APIs.

Suggested change
sqlalchemy>=1.4.42,<2.0.0
sqlalchemy==2.0.23

Copilot uses AI. Check for mistakes.
psycopg2-binary==2.9.9
asyncpg==0.29.0
alembic==1.13.1
Expand Down
128 changes: 125 additions & 3 deletions apps/backend/routes/activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from pydantic import Field
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, or_, and_

Expand All @@ -21,14 +22,89 @@
router = APIRouter()


@router.post("/", response_model=ApiResponse[ActivityResponse])
@router.post(
"/",
response_model=ApiResponse[ActivityResponse],
status_code=status.HTTP_201_CREATED,
summary="Create a new educational activity",
description="Create a new learning activity with educational content, difficulty level, and resource requirements.",
responses={
201: {
"description": "Activity successfully created",
"content": {
"application/json": {
"example": {
"success": True,
"data": {
"id": "uuid-string",
"title": "Planting Seeds Workshop",
"summary": "Learn how to plant and care for seeds",
"description": "A comprehensive workshop on seed planting...",
"category": "agriculture",
"difficulty_level": "beginner",
"duration_min": 60,
"is_published": True,
"created_at": "2024-01-01T00:00:00Z"
},
"message": "Activity created successfully"
}
}
}
},
401: {
"description": "Authentication required",
"content": {
"application/json": {
"example": {
"detail": "Not authenticated"
}
}
}
},
422: {
"description": "Validation error",
"content": {
"application/json": {
"example": {
"detail": [
{
"loc": ["body", "title"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
}
}
}
},
tags=["Activities"]
)
async def create_activity(
activity_data: ActivityCreate,
activity_data: ActivityCreate = Field(
...,
example={
"title": "Planting Seeds Workshop",
"summary": "Learn how to plant and care for seeds",
"description": "A comprehensive workshop covering seed selection, planting techniques, and care instructions.",
"category": "agriculture",
"difficulty_level": "beginner",
"duration_min": 60,
"is_published": True
}
),
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db_session)
):
"""
Create a new educational activity.

This endpoint allows authenticated users to create new learning activities
for the La Vida Luca educational platform. Activities can include workshops,
tutorials, or other educational experiences.

**Authentication Required:** Bearer Token
**Rate Limit:** 20 activities per hour per user
"""
new_activity = Activity(
**activity_data.dict(),
Expand All @@ -46,14 +122,60 @@ async def create_activity(
)


@router.get("/", response_model=ApiResponse[PaginatedResponse[ActivityListResponse]])
@router.get(
"/",
response_model=ApiResponse[PaginatedResponse[ActivityListResponse]],
summary="List educational activities",
description="Retrieve a paginated list of published educational activities with optional filtering by category, difficulty, duration, and other criteria.",
responses={
200: {
"description": "Activities retrieved successfully",
"content": {
"application/json": {
"example": {
"success": True,
"data": {
"items": [
{
"id": "uuid-string",
"title": "Planting Seeds Workshop",
"summary": "Learn how to plant and care for seeds",
"category": "agriculture",
"difficulty_level": "beginner",
"duration_min": 60,
"is_featured": False
}
],
"total": 1,
"page": 1,
"size": 20,
"pages": 1
},
"message": "Activities retrieved successfully"
}
}
}
}
},
tags=["Activities"]
)
async def list_activities(
pagination: PaginationParams = Depends(),
filters: ActivitySearchFilters = Depends(),
db: AsyncSession = Depends(get_db_session)
):
"""
List activities with optional filtering and pagination.

This endpoint returns published educational activities with support for:
- Pagination (page, size)
- Category filtering
- Difficulty level filtering
- Duration range filtering
- Keyword search
- Featured activities

**Rate Limit:** 100 requests per minute per IP
"""
# Build query with filters
query = select(Activity).where(Activity.is_published == True)
Expand Down
Loading
Loading