diff --git a/backend/app/main.py b/backend/app/main.py index 27faaaf..344a2c5 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,5 +1,6 @@ """ TicketHub - Lightweight Issue Tracking System +Professional Edition """ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware @@ -18,7 +19,7 @@ async def lifespan(app: FastAPI): app = FastAPI( title="TicketHub", description="Lightweight open-source ticket/issue tracking system", - version="1.0.0", + version="2.0.0", lifespan=lifespan ) @@ -36,17 +37,531 @@ app.include_router(projects.router, prefix="/api/projects", tags=["projects"]) app.include_router(tickets.router, prefix="/api/tickets", tags=["tickets"]) app.include_router(webhooks.router, prefix="/api/webhooks", tags=["webhooks"]) -# Serve embedded frontend +# Professional Frontend HTML = """ -TicketHub - -
🎫

TicketHub

-

Projects

-

Tickets

Select a project
- - - -""" + + + + + TicketHub + + + + + +
+ + + + +
+
+
+ 📋 +

Select a project

+

Choose a project from the sidebar or create a new one

+
+
+ + +
+
+ + + + + + + + + + + + + + + +""" @app.get("/", response_class=HTMLResponse) async def root(): diff --git a/backend/app/main_v2.py b/backend/app/main_v2.py new file mode 100644 index 0000000..344a2c5 --- /dev/null +++ b/backend/app/main_v2.py @@ -0,0 +1,568 @@ +""" +TicketHub - Lightweight Issue Tracking System +Professional Edition +""" +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import HTMLResponse +from contextlib import asynccontextmanager +import os + +from app.routers import tickets, projects, webhooks, health +from app.services.database import init_db + +@asynccontextmanager +async def lifespan(app: FastAPI): + await init_db() + yield + +app = FastAPI( + title="TicketHub", + description="Lightweight open-source ticket/issue tracking system", + version="2.0.0", + lifespan=lifespan +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# API routes +app.include_router(health.router, prefix="/api", tags=["health"]) +app.include_router(projects.router, prefix="/api/projects", tags=["projects"]) +app.include_router(tickets.router, prefix="/api/tickets", tags=["tickets"]) +app.include_router(webhooks.router, prefix="/api/webhooks", tags=["webhooks"]) + +# Professional Frontend +HTML = """ + + + + + TicketHub + + + + + +
+ + + + +
+
+
+ 📋 +

Select a project

+

Choose a project from the sidebar or create a new one

+
+
+ + +
+
+ + + + + + + + + + + + + + + +""" + +@app.get("/", response_class=HTMLResponse) +async def root(): + return HTML diff --git a/backend/app/routers/projects.py b/backend/app/routers/projects.py index 2bbba2c..f8a19a6 100644 --- a/backend/app/routers/projects.py +++ b/backend/app/routers/projects.py @@ -63,3 +63,51 @@ async def delete_project(project_id: int): 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)