import os from datetime import datetime from typing import Optional from fastapi import FastAPI, Depends, HTTPException, Query from fastapi.middleware.cors import CORSMiddleware from sqlalchemy.orm import Session # These imports now work because of the path fix in the root main.py from database import get_db, engine, Base from models.todo import Todo from models.user import User from schemas.todo import TodoCreate, TodoUpdate, TodoResponse, TodoListResponse from services.todo_service import TodoService from api.routes.auth import router as auth_router from api.chat_router import router as chat_router from api.task_router import router as task_router from middleware.auth import get_current_user app = FastAPI( title="Todo API", description="REST API for managing todo items", version="2.0.0" ) # FIX: Use on_event to avoid 'generator object is not an async iterator' error @app.on_event("startup") def startup_event(): print("Backend starting... Creating database tables.") Base.metadata.create_all(bind=engine) # FIX: Allow GitHub Pages to connect app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include All Routers app.include_router(auth_router, prefix="/api/v1") app.include_router(chat_router) app.include_router(task_router, prefix="/api/v1") @app.get("/") def health(): return {"status": "online", "server": "Hugging Face Spaces"} # --- COMPLETE TODO ROUTES --- @app.get("/api/v1/todos", response_model=TodoListResponse) def get_todos( db: Session = Depends(get_db), current_user: User = Depends(get_current_user), search: Optional[str] = Query(None), status: Optional[str] = Query(None), priority: Optional[str] = Query(None), sort_by: Optional[str] = Query("created_at"), sort_order: Optional[str] = Query("desc"), ): service = TodoService(db, user_id=current_user.id) todos = service.get_all(search=search, status=status, priority=priority, sort_by=sort_by, sort_order=sort_order) # Helper to convert models to response def _todo_to_response(todo): return TodoResponse( id=todo.id, user_id=todo.user_id, title=todo.title, description=todo.description, completed=todo.completed, priority=todo.priority, tags=todo.tags, due_date=todo.due_date, recurrence=todo.recurrence, created_at=todo.created_at, updated_at=todo.updated_at, overdue=False if todo.due_date is None or todo.completed else todo.due_date < datetime.utcnow() ) todos_response = [_todo_to_response(t) for t in todos] return {"todos": todos_response, "count": len(todos_response), "has_more": False} @app.post("/api/v1/todos", response_model=TodoResponse, status_code=201) def create_todo(todo_data: TodoCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): service = TodoService(db, user_id=current_user.id) todo = service.create(todo_data) # Re-using logic to return response return TodoResponse( id=todo.id, user_id=todo.user_id, title=todo.title, description=todo.description, completed=todo.completed, priority=todo.priority, created_at=todo.created_at, updated_at=todo.updated_at ) @app.delete("/api/v1/todos/{todo_id}") def delete_todo(todo_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): service = TodoService(db, user_id=current_user.id) if not service.delete(todo_id): raise HTTPException(status_code=404, detail="Todo not found") return {"message": "Todo deleted successfully"}