diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..bdff6c4 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1 @@ +# App package \ No newline at end of file diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..a757e69 --- /dev/null +++ b/app/api/__init__.py @@ -0,0 +1 @@ +# API package \ No newline at end of file diff --git a/app/db/__init__.py b/app/db/__init__.py new file mode 100644 index 0000000..8cc3bc6 --- /dev/null +++ b/app/db/__init__.py @@ -0,0 +1 @@ +# Database package \ No newline at end of file diff --git a/app/db/base.py b/app/db/base.py new file mode 100644 index 0000000..7c2377a --- /dev/null +++ b/app/db/base.py @@ -0,0 +1,3 @@ +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() \ No newline at end of file diff --git a/app/db/session.py b/app/db/session.py new file mode 100644 index 0000000..9758316 --- /dev/null +++ b/app/db/session.py @@ -0,0 +1,30 @@ +from pathlib import Path +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from .base import Base + +# Database setup with specified path format +DB_DIR = Path("/app") / "storage" / "db" +DB_DIR.mkdir(parents=True, exist_ok=True) + +SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_DIR}/db.sqlite" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, + connect_args={"check_same_thread": False} +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Create all tables +def create_tables(): + Base.metadata.create_all(bind=engine) + +# Dependency to get database session +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 0000000..d8cfe8a --- /dev/null +++ b/app/models/__init__.py @@ -0,0 +1 @@ +# Models package \ No newline at end of file diff --git a/app/models/todo.py b/app/models/todo.py new file mode 100644 index 0000000..e0edd79 --- /dev/null +++ b/app/models/todo.py @@ -0,0 +1,15 @@ +from datetime import datetime +from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime + +from app.db.base import Base + + +class Todo(Base): + __tablename__ = "todos" + + id = Column(Integer, primary_key=True, index=True, autoincrement=True) + title = Column(String(200), nullable=False, index=True) + description = Column(Text, nullable=True) + completed = Column(Boolean, default=False, nullable=False) + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) \ No newline at end of file diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/schemas/todo.py b/app/schemas/todo.py new file mode 100644 index 0000000..58663e6 --- /dev/null +++ b/app/schemas/todo.py @@ -0,0 +1,28 @@ +from datetime import datetime +from typing import Optional +from pydantic import BaseModel + + +class TodoBase(BaseModel): + title: str + description: Optional[str] = None + + +class TodoCreate(TodoBase): + pass + + +class TodoUpdate(BaseModel): + title: Optional[str] = None + description: Optional[str] = None + completed: Optional[bool] = None + + +class TodoResponse(TodoBase): + id: int + completed: bool + created_at: datetime + updated_at: datetime + + class Config: + from_attributes = True \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..08c5316 --- /dev/null +++ b/main.py @@ -0,0 +1,42 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse + +app = FastAPI( + title="Todo App API", + description="A simple todo application API", + version="1.0.0", + openapi_url="/openapi.json" +) + +# Configure CORS to allow all origins +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/") +async def root(): + """Base URL endpoint returning project information""" + return { + "title": "Todo App API", + "documentation": "/docs", + "redoc": "/redoc", + "health": "/health", + "openapi": "/openapi.json" + } + + +@app.get("/health") +async def health_check(): + """Health check endpoint""" + return {"status": "healthy", "message": "Todo App API is running"} + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..17438c7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +fastapi==0.104.1 +uvicorn[standard]==0.24.0 +sqlalchemy==2.0.23 +pydantic==2.5.0 +python-multipart==0.0.6 +ruff==0.1.6 \ No newline at end of file