PromptGarageUI / app.py
smlflg's picture
Initial public upload from Projekte/PromptGarageUI
6156d2f verified
Raw History Blame Contribute Delete
6.18 kB
#!/usr/bin/env python3
"""PromptGarage UI - FastAPI web application."""
import os
from pathlib import Path
from typing import Optional
from fastapi import FastAPI, Form, Query, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import db_client as db
app = FastAPI(title="PromptGarage", description="Web UI for your prompts")
# Setup templates
BASE_DIR = Path(__file__).parent
templates = Jinja2Templates(directory=BASE_DIR / "templates")
# Mount static files
app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static")
# --- Routes ---
@app.get("/", response_class=RedirectResponse)
async def root():
return RedirectResponse("/prompts")
@app.get("/prompts", name="prompts_list")
async def prompts_list(
request: Request,
q: Optional[str] = Query(None, description="Search query"),
tag: Optional[str] = Query(None, description="Filter by tag"),
starred: Optional[int] = Query(None, description="Filter starred (1)"),
page: int = Query(1, ge=1, description="Page number"),
limit: int = Query(20, ge=1, le=100, description="Items per page"),
):
offset = (page - 1) * limit
# Get all tags for filter dropdown
all_tags = db.get_stats()["by_tag"]
# Search or list
if q:
prompts = db.search_fts(q)
# Apply tag/star filters manually after FTS search
filtered = []
for p in prompts:
if tag and tag not in (p["tags"] or ""):
continue
if starred is not None and p["is_starred"] != starred:
continue
filtered.append(p)
prompts = filtered[offset:offset + limit]
total = len(filtered)
else:
# Build query params for list_prompts
prompts = db.list_prompts(
starred=bool(starred) if starred is not None else None,
tag=tag,
limit=limit + offset
)
if offset > 0:
prompts = prompts[offset:]
total = len(db.list_prompts(
starred=bool(starred) if starred is not None else None,
tag=tag,
limit=100000
))
total_pages = (total + limit - 1) // limit if total > 0 else 1
return templates.TemplateResponse("prompts/list.html", {
"request": request,
"prompts": prompts,
"all_tags": all_tags,
"q": q,
"selected_tag": tag,
"starred_filter": starred,
"page": page,
"limit": limit,
"total": total,
"total_pages": total_pages,
})
@app.get("/prompts/new", name="prompts_new")
async def prompts_new(request: Request):
stats = db.get_stats()
return templates.TemplateResponse("prompts/new.html", {
"request": request,
"all_tags": stats["by_tag"],
})
@app.post("/prompts/new")
async def prompts_create(
title: str = Form(...),
content: str = Form(...),
tags: Optional[str] = Form(None),
model: Optional[str] = Form(None),
):
prompt_id = db.add_prompt(title=title, content=content, tags=tags, model=model)
return RedirectResponse(f"/prompts/{prompt_id}", status_code=303)
@app.get("/prompts/{prompt_id}", name="prompts_detail")
async def prompts_detail(request: Request, prompt_id: int):
prompt = db.get_prompt(prompt_id)
if not prompt:
return templates.TemplateResponse("error.html", {
"request": request,
"error": f"Prompt #{prompt_id} not found",
}, status_code=404)
return templates.TemplateResponse("prompts/detail.html", {
"request": request,
"prompt": prompt,
})
@app.post("/prompts/{prompt_id}/star")
async def prompts_toggle_star(prompt_id: int):
result = db.toggle_star(prompt_id)
if result is None:
return {"error": "Prompt not found"}
return {"is_starred": result}
@app.post("/prompts/{prompt_id}/rate")
async def prompts_set_rating(prompt_id: int, rating: int = Query(..., ge=1, le=5)):
db.set_rating(prompt_id, rating)
return {"rating": rating}
@app.post("/prompts/{prompt_id}/use")
async def prompts_use(prompt_id: int):
db.increment_use(prompt_id)
prompt = db.get_prompt(prompt_id)
if prompt:
return {"use_count": prompt["use_count"]}
return {"error": "Prompt not found"}
@app.get("/prompts/{prompt_id}/edit", name="prompts_edit")
async def prompts_edit(request: Request, prompt_id: int):
prompt = db.get_prompt(prompt_id)
if not prompt:
return templates.TemplateResponse("error.html", {
"request": request,
"error": f"Prompt #{prompt_id} not found",
}, status_code=404)
stats = db.get_stats()
return templates.TemplateResponse("prompts/edit.html", {
"request": request,
"prompt": prompt,
"all_tags": stats["by_tag"],
})
@app.post("/prompts/{prompt_id}/edit")
async def prompts_update(
prompt_id: int,
title: str = Form(...),
content: str = Form(...),
tags: Optional[str] = Form(None),
model: Optional[str] = Form(None),
):
db.update_prompt(prompt_id, title=title, content=content, tags=tags, model=model)
return RedirectResponse(f"/prompts/{prompt_id}", status_code=303)
@app.post("/prompts/{prompt_id}/delete")
async def prompts_delete(prompt_id: int):
db.delete_prompt(prompt_id)
return RedirectResponse("/prompts", status_code=303)
@app.get("/stats", name="stats")
async def stats_page(request: Request):
stats = db.get_stats()
# Calculate rating distribution
all_prompts = db.list_prompts(limit=100000)
rating_dist = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
for p in all_prompts:
r = p["rating"] or 0
rating_dist[r] = rating_dist.get(r, 0) + 1
# Get top tags
top_tags = sorted(stats["by_tag"].items(), key=lambda x: -x[1])[:15]
return templates.TemplateResponse("stats.html", {
"request": request,
"stats": stats,
"rating_dist": rating_dist,
"top_tags": top_tags,
})
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)