from fastapi import APIRouter, HTTPException from typing import List from app.models import Project, ProjectCreate from app.services.database import get_db import json router = APIRouter() @router.get("", response_model=List[Project]) async def list_projects(): db = await get_db() cursor = await db.execute(""" SELECT p.*, COUNT(t.id) as ticket_count FROM projects p LEFT JOIN tickets t ON p.id = t.project_id GROUP BY p.id """) rows = await cursor.fetchall() await db.close() return [dict(row) for row in rows] @router.post("", response_model=Project) async def create_project(project: ProjectCreate): db = await get_db() try: cursor = await db.execute( "INSERT INTO projects (name, key, description, webhook_url) VALUES (?, ?, ?, ?)", (project.name, project.key.upper(), project.description, project.webhook_url) ) await db.commit() project_id = cursor.lastrowid cursor = await db.execute("SELECT * FROM projects WHERE id = ?", (project_id,)) row = await cursor.fetchone() await db.close() return {**dict(row), "ticket_count": 0} except Exception as e: await db.close() raise HTTPException(status_code=400, detail=str(e)) @router.get("/{project_id}", response_model=Project) async def get_project(project_id: int): db = await get_db() cursor = await db.execute(""" SELECT p.*, COUNT(t.id) as ticket_count FROM projects p LEFT JOIN tickets t ON p.id = t.project_id WHERE p.id = ? GROUP BY p.id """, (project_id,)) row = await cursor.fetchone() await db.close() if not row: raise HTTPException(status_code=404, detail="Project not found") return dict(row) @router.delete("/{project_id}") async def delete_project(project_id: int): db = await get_db() await db.execute("DELETE FROM tickets WHERE project_id = ?", (project_id,)) await db.execute("DELETE FROM webhooks WHERE project_id = ?", (project_id,)) await db.execute("DELETE FROM projects WHERE id = ?", (project_id,)) await db.commit() await db.close() return {"status": "deleted"} from pydantic import BaseModel from typing import Optional class ProjectUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None webhook_url: Optional[str] = None @router.patch("/{project_id}", response_model=Project) async def update_project(project_id: int, update: ProjectUpdate): db = await get_db() # Check if exists cursor = await db.execute("SELECT * FROM projects WHERE id = ?", (project_id,)) if not await cursor.fetchone(): await db.close() raise HTTPException(status_code=404, detail="Project not found") # Build update query updates = [] params = [] if update.name is not None: updates.append("name = ?") params.append(update.name) if update.description is not None: updates.append("description = ?") params.append(update.description if update.description else None) if update.webhook_url is not None: updates.append("webhook_url = ?") params.append(update.webhook_url if update.webhook_url else None) if updates: params.append(project_id) await db.execute(f"UPDATE projects SET {', '.join(updates)} WHERE id = ?", params) await db.commit() cursor = await db.execute(""" SELECT p.*, COUNT(t.id) as ticket_count FROM projects p LEFT JOIN tickets t ON p.id = t.project_id WHERE p.id = ? GROUP BY p.id """, (project_id,)) row = await cursor.fetchone() await db.close() return dict(row)