Global labels: remove project_id, endpoints at /labels
Some checks failed
Deploy Tracker / deploy (push) Failing after 3s

Labels are now app-wide, not project-scoped.
GET/POST /labels, PATCH/DELETE /labels/{id}
This commit is contained in:
markov 2026-02-28 00:42:03 +01:00
parent 1f57f869ba
commit 009789ad8b
2 changed files with 11 additions and 20 deletions

View File

@ -1,4 +1,4 @@
"""Labels API — CRUD for project-scoped labels.""" """Labels API — CRUD for global labels."""
import uuid import uuid
@ -26,31 +26,27 @@ class LabelUpdate(BaseModel):
class LabelOut(BaseModel): class LabelOut(BaseModel):
id: str id: str
project_id: str
name: str name: str
color: str color: str
@router.get("/projects/{project_id}/labels", response_model=list[LabelOut]) @router.get("/labels", response_model=list[LabelOut])
async def list_labels(project_id: str, db: AsyncSession = Depends(get_db)): async def list_labels(db: AsyncSession = Depends(get_db)):
result = await db.execute( result = await db.execute(select(Label).order_by(Label.name))
select(Label).where(Label.project_id == uuid.UUID(project_id)).order_by(Label.name) return [LabelOut(id=str(l.id), name=l.name, color=l.color) for l in result.scalars()]
)
return [LabelOut(id=str(l.id), project_id=str(l.project_id), name=l.name, color=l.color) for l in result.scalars()]
@router.post("/projects/{project_id}/labels", response_model=LabelOut) @router.post("/labels", response_model=LabelOut)
async def create_label( async def create_label(
project_id: str,
req: LabelCreate, req: LabelCreate,
current_member: Member = Depends(get_current_member), current_member: Member = Depends(get_current_member),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
label = Label(project_id=uuid.UUID(project_id), name=req.name, color=req.color) label = Label(name=req.name, color=req.color)
db.add(label) db.add(label)
await db.commit() await db.commit()
await db.refresh(label) await db.refresh(label)
return LabelOut(id=str(label.id), project_id=str(label.project_id), name=label.name, color=label.color) return LabelOut(id=str(label.id), name=label.name, color=label.color)
@router.patch("/labels/{label_id}", response_model=LabelOut) @router.patch("/labels/{label_id}", response_model=LabelOut)
@ -70,7 +66,7 @@ async def update_label(
label.color = req.color label.color = req.color
await db.commit() await db.commit()
await db.refresh(label) await db.refresh(label)
return LabelOut(id=str(label.id), project_id=str(label.project_id), name=label.name, color=label.color) return LabelOut(id=str(label.id), name=label.name, color=label.color)
@router.delete("/labels/{label_id}") @router.delete("/labels/{label_id}")
@ -83,8 +79,6 @@ async def delete_label(
label = result.scalar_one_or_none() label = result.scalar_one_or_none()
if not label: if not label:
raise HTTPException(404, "Label not found") raise HTTPException(404, "Label not found")
# Remove task associations
await db.execute(select(TaskLabel).where(TaskLabel.label_id == label.id))
await db.delete(label) await db.delete(label)
await db.commit() await db.commit()
return {"ok": True} return {"ok": True}

View File

@ -54,15 +54,12 @@ class Step(Base):
class Label(Base): class Label(Base):
"""Project-scoped labels (like Jira).""" """Global labels (app-wide)."""
__tablename__ = "labels" __tablename__ = "labels"
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id"), nullable=False) name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
name: Mapped[str] = mapped_column(String(100), nullable=False)
color: Mapped[str] = mapped_column(String(7), default="#6366f1") # hex color color: Mapped[str] = mapped_column(String(7), default="#6366f1") # hex color
project: Mapped["Project"] = relationship()
class TaskLabel(Base): class TaskLabel(Base):
"""Many-to-many: tasks ↔ labels.""" """Many-to-many: tasks ↔ labels."""