Scaffold Next.js + FastAPI + Postgres tasks board (no auth)

This commit is contained in:
Abhimanyu Saharan
2026-02-01 22:25:28 +05:30
commit 8b6e8c8d07
2967 changed files with 621159 additions and 0 deletions

Binary file not shown.

Binary file not shown.

64
backend/app/api/tasks.py Normal file
View File

@@ -0,0 +1,64 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.db.session import SessionLocal
from app.models.task import Task
from app.schemas.task import TaskCreate, TaskOut, TaskUpdate
router = APIRouter(prefix="/tasks", tags=["tasks"])
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.get("", response_model=list[TaskOut])
def list_tasks(db: Session = Depends(get_db)):
return db.query(Task).order_by(Task.id.desc()).all()
@router.post("", response_model=TaskOut)
def create_task(payload: TaskCreate, db: Session = Depends(get_db)):
task = Task(
title=payload.title,
description=payload.description,
status=payload.status,
assignee=payload.assignee,
)
db.add(task)
db.commit()
db.refresh(task)
return task
@router.patch("/{task_id}", response_model=TaskOut)
def update_task(task_id: int, payload: TaskUpdate, db: Session = Depends(get_db)):
task = db.get(Task, task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
data = payload.model_dump(exclude_unset=True)
for k, v in data.items():
setattr(task, k, v)
db.add(task)
db.commit()
db.refresh(task)
return task
@router.delete("/{task_id}")
def delete_task(task_id: int, db: Session = Depends(get_db)):
task = db.get(Task, task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
db.delete(task)
db.commit()
return {"ok": True}

Binary file not shown.

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
database_url: str
cors_origins: str = ""
settings = Settings() # type: ignore

Binary file not shown.

Binary file not shown.

5
backend/app/db/base.py Normal file
View File

@@ -0,0 +1,5 @@
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass

View File

@@ -0,0 +1,9 @@
from __future__ import annotations
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
engine = create_engine(settings.database_url, pool_pre_ping=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

26
backend/app/main.py Normal file
View File

@@ -0,0 +1,26 @@
from __future__ import annotations
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.tasks import router as tasks_router
from app.core.config import settings
app = FastAPI(title="OpenClaw Agency API", version="0.1.0")
origins = [o.strip() for o in settings.cors_origins.split(",") if o.strip()]
if origins:
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"] ,
allow_headers=["*"],
)
app.include_router(tasks_router)
@app.get("/health")
def health():
return {"ok": True}

View File

@@ -0,0 +1,2 @@
# Import models here so Alembic can discover them
from .task import Task # noqa: F401

Binary file not shown.

View File

@@ -0,0 +1,28 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import DateTime, Integer, String, Text
from sqlalchemy.sql import func
from sqlalchemy.orm import Mapped, mapped_column
from app.db.base import Base
class Task(Base):
__tablename__ = "tasks"
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
title: Mapped[str] = mapped_column(String(200), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
# kanban columns: todo | doing | done
status: Mapped[str] = mapped_column(String(32), nullable=False, default="todo")
# simple attribution (no auth)
assignee: Mapped[str | None] = mapped_column(String(120), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)

Binary file not shown.

View File

@@ -0,0 +1,35 @@
from __future__ import annotations
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, Field
TaskStatus = Literal["todo", "doing", "done"]
class TaskCreate(BaseModel):
title: str = Field(min_length=1, max_length=200)
description: str | None = None
status: TaskStatus = "todo"
assignee: str | None = None
class TaskUpdate(BaseModel):
title: str | None = Field(default=None, min_length=1, max_length=200)
description: str | None = None
status: TaskStatus | None = None
assignee: str | None = None
class TaskOut(BaseModel):
id: int
title: str
description: str | None
status: TaskStatus
assignee: str | None
created_at: datetime
updated_at: datetime | None
model_config = {"from_attributes": True}