feat: add web UI with login, CRUD, admin, and API key management

- Add login page with JWT authentication
- Add dashboard with stats and quick actions
- Add links management page (full CRUD with search)
- Add collections management page
- Add API key management page with copy-to-clipboard
- Add admin user management page (admin only)
- Fix UUID type mismatches across all endpoints
- Add updated_at column to api_keys and audit_log in schema.sql
- Fix DB_PASSWORD default in docker-compose.yml
- Add PyJWT to requirements.txt
- Fix API docs URL (/docs instead of /api/docs)
- Improve JS error handling (show actual messages)
- Rewrite conftest.py with proper DB lifecycle management
- Add 42 new integration tests (84 total, all passing)
  - test_admin.py: 15 tests for admin endpoints
  - test_auth_extended.py: 9 tests for API key CRUD
  - test_tags.py: 12 tests for tag endpoints
  - test_sync.py: 6 tests for sync endpoints
This commit is contained in:
DavidSaylor
2026-05-21 07:21:49 -05:00
parent 09d30427f4
commit 77b076c7d7
31 changed files with 2740 additions and 213 deletions

View File

@@ -3,10 +3,11 @@ LinkSyncServer - Main Application
"""
import os
from fastapi import FastAPI
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import RedirectResponse
from contextlib import asynccontextmanager
from api.routes import router as api_router
@@ -51,5 +52,35 @@ def health():
@app.get("/")
def index(request):
return templates.TemplateResponse("index.html", {"request": request})
def index():
return RedirectResponse(url="/login")
@app.get("/login")
def login_page(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
@app.get("/dashboard")
def dashboard(request: Request):
return templates.TemplateResponse("dashboard.html", {"request": request})
@app.get("/links")
def links_page(request: Request):
return templates.TemplateResponse("links.html", {"request": request})
@app.get("/collections")
def collections_page(request: Request):
return templates.TemplateResponse("collections.html", {"request": request})
@app.get("/api-keys")
def apikeys_page(request: Request):
return templates.TemplateResponse("apikeys.html", {"request": request})
@app.get("/admin")
def admin_page(request: Request):
return templates.TemplateResponse("admin.html", {"request": request})