using router

This commit is contained in:
Gamosoft 2025-11-14 17:42:25 +01:00
parent 261afbdafe
commit 4b8d65134e
1 changed files with 59 additions and 33 deletions

View File

@ -3,7 +3,7 @@ NoteDiscovery - Self-Hosted Markdown Knowledge Base
Main FastAPI application Main FastAPI application
""" """
from fastapi import FastAPI, HTTPException, UploadFile, File, Request, Form, Depends from fastapi import FastAPI, HTTPException, UploadFile, File, Request, Form, Depends, APIRouter
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@ -200,11 +200,27 @@ async def favicon():
# ============================================================================ # ============================================================================
# Application Routes # Routers with Authentication
# ============================================================================ # ============================================================================
@app.get("/", response_class=HTMLResponse) # Create API router with authentication dependency applied globally
async def root(request: Request, _auth: None = Depends(require_auth)): api_router = APIRouter(
prefix="/api",
dependencies=[Depends(require_auth)] # Apply auth to ALL routes in this router
)
# Create pages router with authentication dependency applied globally
pages_router = APIRouter(
dependencies=[Depends(require_auth)] # Apply auth to ALL routes in this router
)
# ============================================================================
# Application Routes (with auth via router dependencies)
# ============================================================================
@pages_router.get("/", response_class=HTMLResponse)
async def root(request: Request):
"""Serve the main application page""" """Serve the main application page"""
index_path = static_path / "index.html" index_path = static_path / "index.html"
async with aiofiles.open(index_path, 'r', encoding='utf-8') as f: async with aiofiles.open(index_path, 'r', encoding='utf-8') as f:
@ -212,7 +228,7 @@ async def root(request: Request, _auth: None = Depends(require_auth)):
return content return content
@app.get("/api") @api_router.get("")
async def api_documentation(): async def api_documentation():
"""API Documentation - List all available endpoints""" """API Documentation - List all available endpoints"""
return { return {
@ -364,7 +380,7 @@ async def api_documentation():
} }
@app.get("/api/config") @api_router.get("/config")
async def get_config(): async def get_config():
"""Get app configuration for frontend""" """Get app configuration for frontend"""
return { return {
@ -378,7 +394,7 @@ async def get_config():
} }
@app.get("/api/themes") @api_router.get("/themes")
async def list_themes(): async def list_themes():
"""Get all available themes""" """Get all available themes"""
themes_dir = Path(__file__).parent.parent / "themes" themes_dir = Path(__file__).parent.parent / "themes"
@ -398,8 +414,8 @@ async def get_theme(theme_id: str):
return {"css": css, "theme_id": theme_id} return {"css": css, "theme_id": theme_id}
@app.post("/api/folders") @api_router.post("/folders")
async def create_new_folder(data: dict, _auth: None = Depends(require_auth)): async def create_new_folder(data: dict):
"""Create a new folder""" """Create a new folder"""
try: try:
folder_path = data.get('path', '') folder_path = data.get('path', '')
@ -420,8 +436,8 @@ async def create_new_folder(data: dict, _auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/notes/move") @api_router.post("/notes/move")
async def move_note_endpoint(data: dict, _auth: None = Depends(require_auth)): async def move_note_endpoint(data: dict):
"""Move a note to a different folder""" """Move a note to a different folder"""
try: try:
old_path = data.get('oldPath', '') old_path = data.get('oldPath', '')
@ -448,8 +464,8 @@ async def move_note_endpoint(data: dict, _auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/folders/move") @api_router.post("/folders/move")
async def move_folder_endpoint(data: dict, _auth: None = Depends(require_auth)): async def move_folder_endpoint(data: dict):
"""Move a folder to a different location""" """Move a folder to a different location"""
try: try:
old_path = data.get('oldPath', '') old_path = data.get('oldPath', '')
@ -473,8 +489,8 @@ async def move_folder_endpoint(data: dict, _auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/folders/rename") @api_router.post("/folders/rename")
async def rename_folder_endpoint(data: dict, _auth: None = Depends(require_auth)): async def rename_folder_endpoint(data: dict):
"""Rename a folder""" """Rename a folder"""
try: try:
old_path = data.get('oldPath', '') old_path = data.get('oldPath', '')
@ -498,8 +514,8 @@ async def rename_folder_endpoint(data: dict, _auth: None = Depends(require_auth)
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.delete("/api/folders/{folder_path:path}") @api_router.delete("/folders/{folder_path:path}")
async def delete_folder_endpoint(folder_path: str, _auth: None = Depends(require_auth)): async def delete_folder_endpoint(folder_path: str):
"""Delete a folder and all its contents""" """Delete a folder and all its contents"""
try: try:
if not folder_path: if not folder_path:
@ -519,7 +535,7 @@ async def delete_folder_endpoint(folder_path: str, _auth: None = Depends(require
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/notes") @api_router.get("/notes")
async def list_notes(_auth: None = Depends(require_auth)): async def list_notes(_auth: None = Depends(require_auth)):
"""List all notes with metadata""" """List all notes with metadata"""
try: try:
@ -530,8 +546,8 @@ async def list_notes(_auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/notes/{note_path:path}") @api_router.get("/notes/{note_path:path}")
async def get_note(note_path: str, _auth: None = Depends(require_auth)): async def get_note(note_path: str):
"""Get a specific note's content""" """Get a specific note's content"""
try: try:
content = get_note_content(config['storage']['notes_dir'], note_path) content = get_note_content(config['storage']['notes_dir'], note_path)
@ -558,8 +574,8 @@ async def get_note(note_path: str, _auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/notes/{note_path:path}") @api_router.post("/notes/{note_path:path}")
async def create_or_update_note(note_path: str, content: dict, _auth: None = Depends(require_auth)): async def create_or_update_note(note_path: str, content: dict):
"""Create or update a note""" """Create or update a note"""
try: try:
note_content = content.get('content', '') note_content = content.get('content', '')
@ -596,8 +612,8 @@ async def create_or_update_note(note_path: str, content: dict, _auth: None = Dep
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.delete("/api/notes/{note_path:path}") @api_router.delete("/notes/{note_path:path}")
async def remove_note(note_path: str, _auth: None = Depends(require_auth)): async def remove_note(note_path: str):
"""Delete a note""" """Delete a note"""
try: try:
success = delete_note(config['storage']['notes_dir'], note_path) success = delete_note(config['storage']['notes_dir'], note_path)
@ -616,8 +632,8 @@ async def remove_note(note_path: str, _auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/search") @api_router.get("/search")
async def search(q: str, _auth: None = Depends(require_auth)): async def search(q: str):
"""Search notes by content""" """Search notes by content"""
try: try:
if not config['search']['enabled']: if not config['search']['enabled']:
@ -635,7 +651,7 @@ async def search(q: str, _auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/graph") @api_router.get("/graph")
async def get_graph(_auth: None = Depends(require_auth)): async def get_graph(_auth: None = Depends(require_auth)):
"""Get graph data for visualization""" """Get graph data for visualization"""
try: try:
@ -665,14 +681,14 @@ async def get_graph(_auth: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/plugins") @api_router.get("/plugins")
async def list_plugins(_auth: None = Depends(require_auth)): async def list_plugins(_auth: None = Depends(require_auth)):
"""List all available plugins""" """List all available plugins"""
return {"plugins": plugin_manager.list_plugins()} return {"plugins": plugin_manager.list_plugins()}
@app.get("/api/plugins/note_stats/calculate") @app.get("/api/plugins/note_stats/calculate")
async def calculate_note_stats(content: str, _auth: None = Depends(require_auth)): async def calculate_note_stats(content: str):
"""Calculate statistics for note content (if plugin enabled)""" """Calculate statistics for note content (if plugin enabled)"""
try: try:
plugin = plugin_manager.plugins.get('note_stats') plugin = plugin_manager.plugins.get('note_stats')
@ -685,8 +701,8 @@ async def calculate_note_stats(content: str, _auth: None = Depends(require_auth)
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/plugins/{plugin_name}/toggle") @api_router.post("/plugins/{plugin_name}/toggle")
async def toggle_plugin(plugin_name: str, enabled: dict, _auth: None = Depends(require_auth)): async def toggle_plugin(plugin_name: str, enabled: dict):
"""Enable or disable a plugin""" """Enable or disable a plugin"""
try: try:
is_enabled = enabled.get('enabled', False) is_enabled = enabled.get('enabled', False)
@ -716,8 +732,8 @@ async def health_check():
# Catch-all route for SPA (Single Page Application) routing # Catch-all route for SPA (Single Page Application) routing
# This allows URLs like /folder/note to work for direct navigation # This allows URLs like /folder/note to work for direct navigation
@app.get("/{full_path:path}", response_class=HTMLResponse) @pages_router.get("/{full_path:path}", response_class=HTMLResponse)
async def catch_all(full_path: str, request: Request, _auth: None = Depends(require_auth)): async def catch_all(full_path: str, request: Request):
""" """
Serve index.html for all non-API routes. Serve index.html for all non-API routes.
This enables client-side routing (e.g., /folder/note) This enables client-side routing (e.g., /folder/note)
@ -733,6 +749,16 @@ async def catch_all(full_path: str, request: Request, _auth: None = Depends(requ
return content return content
# ============================================================================
# Register Routers
# ============================================================================
# Register routers with the main app
# Authentication is applied via router dependencies
app.include_router(api_router)
app.include_router(pages_router)
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run( uvicorn.run(