Create comprehensive Task Manager API with FastAPI

- Add user authentication with JWT tokens
- Implement task CRUD operations with status and priority
- Set up SQLite database with SQLAlchemy ORM
- Create Alembic migrations for database schema
- Add comprehensive API documentation
- Include health check endpoint and CORS configuration
- Structure codebase with proper separation of concerns
This commit is contained in:
Automated Action 2025-06-20 19:35:55 +00:00
parent 81d5ad9c9d
commit c9baaa994e
28 changed files with 938 additions and 2 deletions

176
README.md
View File

@ -1,3 +1,175 @@
# FastAPI Application # Task Manager API
This is a FastAPI application bootstrapped by BackendIM, the AI-powered backend generation platform. A comprehensive Task Manager API built with FastAPI, featuring user authentication, task management, and SQLite database integration.
## Features
- User registration and authentication with JWT tokens
- Task CRUD operations (Create, Read, Update, Delete)
- Task filtering by status and priority
- User profile management
- SQLite database with SQLAlchemy ORM
- Database migrations with Alembic
- API documentation with Swagger UI
- Health check endpoint
- CORS enabled for all origins
## Project Structure
```
├── main.py # FastAPI application entry point
├── requirements.txt # Python dependencies
├── alembic.ini # Alembic configuration
├── alembic/ # Database migrations
├── app/
│ ├── api/ # API route handlers
│ │ ├── auth.py # Authentication endpoints
│ │ ├── tasks.py # Task management endpoints
│ │ └── users.py # User management endpoints
│ ├── core/ # Core application logic
│ │ ├── auth.py # Authentication dependencies
│ │ ├── config.py # Application configuration
│ │ └── security.py # Security utilities
│ ├── db/ # Database configuration
│ │ ├── base.py # SQLAlchemy base class
│ │ └── session.py # Database session management
│ ├── models/ # SQLAlchemy models
│ │ ├── task.py # Task model
│ │ └── user.py # User model
│ ├── schemas/ # Pydantic schemas
│ │ ├── task.py # Task schemas
│ │ └── user.py # User schemas
│ └── services/ # Business logic layer
│ ├── task.py # Task service
│ └── user.py # User service
└── storage/
└── db/ # SQLite database storage
```
## Installation
1. Install dependencies:
```bash
pip install -r requirements.txt
```
2. Run database migrations:
```bash
alembic upgrade head
```
3. Start the application:
```bash
uvicorn main:app --reload
```
The API will be available at `http://localhost:8000`
## Environment Variables
Set the following environment variables for production:
- `SECRET_KEY`: Secret key for JWT token signing (default: "your-secret-key-here")
Example:
```bash
export SECRET_KEY="your-super-secret-key-here"
```
## API Endpoints
### Authentication
- `POST /auth/register` - Register a new user
- `POST /auth/login` - Login and get access token
### Tasks
- `GET /tasks/` - Get all tasks for authenticated user
- `POST /tasks/` - Create a new task
- `GET /tasks/{task_id}` - Get a specific task
- `PUT /tasks/{task_id}` - Update a task
- `DELETE /tasks/{task_id}` - Delete a task
### Users
- `GET /users/me` - Get current user profile
- `PUT /users/me` - Update current user profile
### System
- `GET /` - API information
- `GET /health` - Health check endpoint
## API Documentation
- Swagger UI: `http://localhost:8000/docs`
- ReDoc: `http://localhost:8000/redoc`
- OpenAPI JSON: `http://localhost:8000/openapi.json`
## Task Management
### Task Status Options
- `pending` - Task is pending
- `in_progress` - Task is in progress
- `completed` - Task is completed
- `cancelled` - Task is cancelled
### Task Priority Options
- `low` - Low priority
- `medium` - Medium priority
- `high` - High priority
- `urgent` - Urgent priority
## Authentication
The API uses JWT (JSON Web Tokens) for authentication. After registration or login, include the token in the Authorization header:
```
Authorization: Bearer <your-jwt-token>
```
## Database
The application uses SQLite as the database with the following features:
- Database file stored at `/app/storage/db/db.sqlite`
- Automatic table creation on startup
- Database migrations managed by Alembic
- Relationships between users and tasks
## Development
### Running Tests
To run tests (when available):
```bash
pytest
```
### Code Formatting
The project uses Ruff for code formatting and linting:
```bash
ruff check .
ruff format .
```
### Database Operations
Create a new migration:
```bash
alembic revision --autogenerate -m "description"
```
Apply migrations:
```bash
alembic upgrade head
```
## Health Check
The application provides a health check endpoint at `/health` that returns:
```json
{
"status": "healthy",
"service": "Task Manager API",
"version": "1.0.0"
}
```
## License
This project is generated by BackendIM, the AI-powered backend generation platform.

41
alembic.ini Normal file
View File

@ -0,0 +1,41 @@
[alembic]
script_location = alembic
prepend_sys_path = .
version_path_separator = os
sqlalchemy.url = sqlite:////app/storage/db/db.sqlite
[post_write_hooks]
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

49
alembic/env.py Normal file
View File

@ -0,0 +1,49 @@
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from app.db.base import Base
config = context.config
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = Base.metadata
def run_migrations_offline() -> None:
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

24
alembic/script.py.mako Normal file
View File

@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}

View File

@ -0,0 +1,79 @@
"""Initial migration
Revision ID: 001
Revises:
Create Date: 2024-01-01 12:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = "001"
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"users",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("email", sa.String(), nullable=False),
sa.Column("hashed_password", sa.String(), nullable=False),
sa.Column("full_name", sa.String(), nullable=True),
sa.Column("is_active", sa.Boolean(), nullable=True),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("(CURRENT_TIMESTAMP)"),
nullable=True,
),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
op.create_index(op.f("ix_users_id"), "users", ["id"], unique=False)
op.create_table(
"tasks",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("title", sa.String(), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column(
"status",
sa.Enum(
"PENDING", "IN_PROGRESS", "COMPLETED", "CANCELLED", name="taskstatus"
),
nullable=True,
),
sa.Column(
"priority",
sa.Enum("LOW", "MEDIUM", "HIGH", "URGENT", name="taskpriority"),
nullable=True,
),
sa.Column("is_completed", sa.Boolean(), nullable=True),
sa.Column("due_date", sa.DateTime(timezone=True), nullable=True),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("(CURRENT_TIMESTAMP)"),
nullable=True,
),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("owner_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["owner_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_tasks_id"), "tasks", ["id"], unique=False)
def downgrade() -> None:
op.drop_index(op.f("ix_tasks_id"), table_name="tasks")
op.drop_table("tasks")
op.drop_index(op.f("ix_users_id"), table_name="users")
op.drop_index(op.f("ix_users_email"), table_name="users")
op.drop_table("users")

0
app/__init__.py Normal file
View File

0
app/api/__init__.py Normal file
View File

37
app/api/auth.py Normal file
View File

@ -0,0 +1,37 @@
from datetime import timedelta
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.schemas.user import UserCreate, User, Token
from app.services.user import create_user, authenticate_user, get_user_by_email
from app.core.security import create_access_token
from app.core.config import settings
router = APIRouter()
@router.post("/register", response_model=User)
def register(user: UserCreate, db: Session = Depends(get_db)):
db_user = get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return create_user(db=db, user=user)
@router.post("/login", response_model=Token)
def login(
form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)
):
user = authenticate_user(db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=settings.access_token_expire_minutes)
access_token = create_access_token(
data={"sub": user.email}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}

79
app/api/tasks.py Normal file
View File

@ -0,0 +1,79 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from app.db.session import get_db
from app.schemas.task import Task, TaskCreate, TaskUpdate
from app.services.task import (
get_tasks_by_user,
get_task_by_id,
create_task,
update_task,
delete_task,
get_tasks_by_status,
)
from app.core.auth import get_current_active_user
from app.models.user import User
from app.models.task import TaskStatus
router = APIRouter()
@router.get("/", response_model=List[Task])
def read_tasks(
skip: int = 0,
limit: int = 100,
status: TaskStatus = None,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
if status:
return get_tasks_by_status(db, user_id=current_user.id, status=status)
return get_tasks_by_user(db, user_id=current_user.id, skip=skip, limit=limit)
@router.post("/", response_model=Task)
def create_new_task(
task: TaskCreate,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
return create_task(db, task=task, user_id=current_user.id)
@router.get("/{task_id}", response_model=Task)
def read_task(
task_id: int,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
db_task = get_task_by_id(db, task_id=task_id, user_id=current_user.id)
if db_task is None:
raise HTTPException(status_code=404, detail="Task not found")
return db_task
@router.put("/{task_id}", response_model=Task)
def update_existing_task(
task_id: int,
task: TaskUpdate,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
db_task = update_task(
db, task_id=task_id, task_update=task, user_id=current_user.id
)
if db_task is None:
raise HTTPException(status_code=404, detail="Task not found")
return db_task
@router.delete("/{task_id}")
def delete_existing_task(
task_id: int,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
success = delete_task(db, task_id=task_id, user_id=current_user.id)
if not success:
raise HTTPException(status_code=404, detail="Task not found")
return {"message": "Task deleted successfully"}

23
app/api/users.py Normal file
View File

@ -0,0 +1,23 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.schemas.user import User, UserUpdate
from app.services.user import update_user
from app.core.auth import get_current_active_user
from app.models.user import User as UserModel
router = APIRouter()
@router.get("/me", response_model=User)
def read_current_user(current_user: UserModel = Depends(get_current_active_user)):
return current_user
@router.put("/me", response_model=User)
def update_current_user(
user_update: UserUpdate,
current_user: UserModel = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
return update_user(db, user_id=current_user.id, user_update=user_update)

0
app/core/__init__.py Normal file
View File

37
app/core/auth.py Normal file
View File

@ -0,0 +1,37 @@
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.core.security import verify_token
from app.services.user import get_user_by_email
from app.models.user import User
security = HTTPBearer()
def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: Session = Depends(get_db),
) -> User:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
token = credentials.credentials
email = verify_token(token)
if email is None:
raise credentials_exception
user = get_user_by_email(db, email=email)
if user is None:
raise credentials_exception
return user
def get_current_active_user(current_user: User = Depends(get_current_user)) -> User:
if not current_user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

16
app/core/config.py Normal file
View File

@ -0,0 +1,16 @@
import os
from pydantic import BaseSettings
class Settings(BaseSettings):
secret_key: str = os.getenv("SECRET_KEY", "your-secret-key-here")
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
app_name: str = "Task Manager API"
app_version: str = "1.0.0"
class Config:
env_file = ".env"
settings = Settings()

41
app/core/security.py Normal file
View File

@ -0,0 +1,41 @@
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(
to_encode, settings.secret_key, algorithm=settings.algorithm
)
return encoded_jwt
def verify_token(token: str):
try:
payload = jwt.decode(
token, settings.secret_key, algorithms=[settings.algorithm]
)
email: str = payload.get("sub")
if email is None:
return None
return email
except JWTError:
return None

0
app/db/__init__.py Normal file
View File

3
app/db/base.py Normal file
View File

@ -0,0 +1,3 @@
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

22
app/db/session.py Normal file
View File

@ -0,0 +1,22 @@
from pathlib import Path
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
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)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

0
app/models/__init__.py Normal file
View File

45
app/models/task.py Normal file
View File

@ -0,0 +1,45 @@
from sqlalchemy import (
Column,
Integer,
String,
Text,
Boolean,
DateTime,
ForeignKey,
Enum,
)
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.db.base import Base
import enum
class TaskStatus(enum.Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
CANCELLED = "cancelled"
class TaskPriority(enum.Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
URGENT = "urgent"
class Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, nullable=False)
description = Column(Text, nullable=True)
status = Column(Enum(TaskStatus), default=TaskStatus.PENDING)
priority = Column(Enum(TaskPriority), default=TaskPriority.MEDIUM)
is_completed = Column(Boolean, default=False)
due_date = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
owner = relationship("User", back_populates="tasks")

18
app/models/user.py Normal file
View File

@ -0,0 +1,18 @@
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.db.base import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
full_name = Column(String, nullable=True)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
tasks = relationship("Task", back_populates="owner")

0
app/schemas/__init__.py Normal file
View File

36
app/schemas/task.py Normal file
View File

@ -0,0 +1,36 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from app.models.task import TaskStatus, TaskPriority
class TaskBase(BaseModel):
title: str
description: Optional[str] = None
status: TaskStatus = TaskStatus.PENDING
priority: TaskPriority = TaskPriority.MEDIUM
due_date: Optional[datetime] = None
class TaskCreate(TaskBase):
pass
class TaskUpdate(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
status: Optional[TaskStatus] = None
priority: Optional[TaskPriority] = None
due_date: Optional[datetime] = None
is_completed: Optional[bool] = None
class Task(TaskBase):
id: int
is_completed: bool
created_at: datetime
updated_at: Optional[datetime] = None
owner_id: int
class Config:
from_attributes = True

42
app/schemas/user.py Normal file
View File

@ -0,0 +1,42 @@
from pydantic import BaseModel, EmailStr
from typing import Optional
from datetime import datetime
class UserBase(BaseModel):
email: EmailStr
full_name: Optional[str] = None
class UserCreate(UserBase):
password: str
class UserUpdate(BaseModel):
email: Optional[EmailStr] = None
full_name: Optional[str] = None
password: Optional[str] = None
class User(UserBase):
id: int
is_active: bool
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class UserLogin(BaseModel):
email: EmailStr
password: str
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
email: Optional[str] = None

0
app/services/__init__.py Normal file
View File

64
app/services/task.py Normal file
View File

@ -0,0 +1,64 @@
from sqlalchemy.orm import Session
from app.models.task import Task, TaskStatus
from app.schemas.task import TaskCreate, TaskUpdate
from typing import List, Optional
def get_tasks_by_user(
db: Session, user_id: int, skip: int = 0, limit: int = 100
) -> List[Task]:
return (
db.query(Task).filter(Task.owner_id == user_id).offset(skip).limit(limit).all()
)
def get_task_by_id(db: Session, task_id: int, user_id: int) -> Optional[Task]:
return db.query(Task).filter(Task.id == task_id, Task.owner_id == user_id).first()
def create_task(db: Session, task: TaskCreate, user_id: int) -> Task:
db_task = Task(**task.dict(), owner_id=user_id)
db.add(db_task)
db.commit()
db.refresh(db_task)
return db_task
def update_task(
db: Session, task_id: int, task_update: TaskUpdate, user_id: int
) -> Optional[Task]:
db_task = get_task_by_id(db, task_id, user_id)
if not db_task:
return None
update_data = task_update.dict(exclude_unset=True)
if "status" in update_data:
if update_data["status"] == TaskStatus.COMPLETED:
update_data["is_completed"] = True
elif update_data["status"] != TaskStatus.COMPLETED and db_task.is_completed:
update_data["is_completed"] = False
if "is_completed" in update_data and update_data["is_completed"]:
update_data["status"] = TaskStatus.COMPLETED
for field, value in update_data.items():
setattr(db_task, field, value)
db.commit()
db.refresh(db_task)
return db_task
def delete_task(db: Session, task_id: int, user_id: int) -> bool:
db_task = get_task_by_id(db, task_id, user_id)
if not db_task:
return False
db.delete(db_task)
db.commit()
return True
def get_tasks_by_status(db: Session, user_id: int, status: TaskStatus) -> List[Task]:
return db.query(Task).filter(Task.owner_id == user_id, Task.status == status).all()

53
app/services/user.py Normal file
View File

@ -0,0 +1,53 @@
from sqlalchemy.orm import Session
from app.models.user import User
from app.schemas.user import UserCreate, UserUpdate
from app.core.security import get_password_hash, verify_password
from typing import Optional
def get_user_by_email(db: Session, email: str) -> Optional[User]:
return db.query(User).filter(User.email == email).first()
def get_user_by_id(db: Session, user_id: int) -> Optional[User]:
return db.query(User).filter(User.id == user_id).first()
def create_user(db: Session, user: UserCreate) -> User:
hashed_password = get_password_hash(user.password)
db_user = User(
email=user.email,
hashed_password=hashed_password,
full_name=user.full_name,
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def authenticate_user(db: Session, email: str, password: str) -> Optional[User]:
user = get_user_by_email(db, email)
if not user:
return None
if not verify_password(password, user.hashed_password):
return None
return user
def update_user(db: Session, user_id: int, user_update: UserUpdate) -> Optional[User]:
db_user = get_user_by_id(db, user_id)
if not db_user:
return None
update_data = user_update.dict(exclude_unset=True)
if "password" in update_data:
update_data["hashed_password"] = get_password_hash(update_data.pop("password"))
for field, value in update_data.items():
setattr(db_user, field, value)
db.commit()
db.refresh(db_user)
return db_user

46
main.py Normal file
View File

@ -0,0 +1,46 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api import auth, tasks, users
from app.core.config import settings
from app.db.session import engine
from app.db.base import Base
app = FastAPI(
title=settings.app_name,
version=settings.app_version,
description="A comprehensive Task Manager API built with FastAPI",
openapi_url="/openapi.json",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Base.metadata.create_all(bind=engine)
app.include_router(auth.router, prefix="/auth", tags=["authentication"])
app.include_router(tasks.router, prefix="/tasks", tags=["tasks"])
app.include_router(users.router, prefix="/users", tags=["users"])
@app.get("/")
def read_root():
return {
"title": settings.app_name,
"version": settings.app_version,
"documentation": "/docs",
"health_check": "/health",
}
@app.get("/health")
def health_check():
return {
"status": "healthy",
"service": settings.app_name,
"version": settings.app_version,
}

9
requirements.txt Normal file
View File

@ -0,0 +1,9 @@
fastapi==0.104.1
uvicorn==0.24.0
sqlalchemy==2.0.23
alembic==1.12.1
pydantic==2.5.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.6
ruff==0.1.6