diff --git a/app/api.py b/app/api.py index f768e26a..c336a6c3 100644 --- a/app/api.py +++ b/app/api.py @@ -1,17 +1,26 @@ -from fastapi import APIRouter, Request, HTTPException, status +# app/api.py +from fastapi import APIRouter, Request, HTTPException, status, Depends from hashlib import md5 +from sqlalchemy.orm import Session +from typing import List + +from app.auth import require_login +from app.database import SessionLocal +from app.models import FileRecord router = APIRouter() +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + @router.get("/whoami") async def whoami(request: Request): """ Returns user info if logged in, else 401. - Example response: - { - "email": "someone@example.com", - "picture": "https://www.gravatar.com/avatar/..." - } """ user = request.session.get("user") if not user: @@ -22,11 +31,46 @@ async def whoami(request: Request): raise HTTPException(status_code=400, detail="User has no email in session") # Generate Gravatar URL from email - # For more options, see: https://en.gravatar.com/site/implement/images/ email_hash = md5(email.strip().lower().encode()).hexdigest() gravatar_url = f"https://www.gravatar.com/avatar/{email_hash}?d=identicon" return { "email": email, "picture": gravatar_url - } \ No newline at end of file + } + +@router.get("/files") +@require_login +def list_files_api(request: Request, db: Session = Depends(get_db)): + """ + Returns a JSON list of all FileRecord entries. + Protected by `@require_login`, so only logged-in sessions can access. + + Example response: + [ + { + "id": 123, + "filehash": "abc123...", + "original_filename": "example.pdf", + "local_filename": "/workdir/tmp/.pdf", + "file_size": 1048576, + "mime_type": "application/pdf", + "created_at": "2025-05-01T12:34:56.789000" + }, + ... + ] + """ + files = db.query(FileRecord).order_by(FileRecord.created_at.desc()).all() + # Return a simple list of dicts + result = [] + for f in files: + result.append({ + "id": f.id, + "filehash": f.filehash, + "original_filename": f.original_filename, + "local_filename": f.local_filename, + "file_size": f.file_size, + "mime_type": f.mime_type, + "created_at": f.created_at.isoformat() if f.created_at else None + }) + return result diff --git a/app/auth.py b/app/auth.py index c6a9f18b..5cbf9cf5 100644 --- a/app/auth.py +++ b/app/auth.py @@ -1,5 +1,5 @@ -# app/auth.py import os +import inspect from functools import wraps from authlib.integrations.starlette_client import OAuth @@ -37,7 +37,11 @@ def require_login(func): if not request.session.get("user"): request.session["redirect_after_login"] = str(request.url) return RedirectResponse(url="/login", status_code=status.HTTP_302_FOUND) - return await func(request, *args, **kwargs) + # Check if the wrapped function is a coroutine function + if inspect.iscoroutinefunction(func): + return await func(request, *args, **kwargs) + else: + return func(request, *args, **kwargs) return wrapper diff --git a/app/frontend.py b/app/frontend.py index 19e9be93..f938ea0d 100644 --- a/app/frontend.py +++ b/app/frontend.py @@ -1,16 +1,37 @@ # app/frontend.py import os from pathlib import Path -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Depends from fastapi.templating import Jinja2Templates +from sqlalchemy.orm import Session + from app.auth import require_login +from app.database import SessionLocal router = APIRouter() -# Point templates_dir to "frontend/templates" templates_dir = Path(__file__).parent.parent / "frontend" / "templates" templates = Jinja2Templates(directory=str(templates_dir)) +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +@router.get("/files") +@require_login +def files_page(request: Request): + """ + Return the 'files.html' template. + The actual file data is fetched via XHR from /api/files in the template. + """ + return templates.TemplateResponse("files.html", {"request": request}) + +# ... existing routes for /, /upload, /about, etc. ... + + @router.get("/", include_in_schema=False) async def serve_index(request: Request): return templates.TemplateResponse("index.html", {"request": request}) diff --git a/app/main.py b/app/main.py index 16c6c6e3..9dc08856 100644 --- a/app/main.py +++ b/app/main.py @@ -180,6 +180,20 @@ async def custom_404_handler(request: Request, exc: HTTPException): status_code=status.HTTP_404_NOT_FOUND ) +@app.exception_handler(500) +async def custom_500_handler(request: Request, exc: Exception): + templates = Jinja2Templates(directory=str(frontend_static_dir.parent / "templates")) + # Option 1: Keep it simple, just show a funny 500 message: + return templates.TemplateResponse( + "500.html", + {"request": request, "exc": exc}, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + +@app.get("/test-500") +def test_500(): + raise RuntimeError("Testing forced 500 error!") + # Include the frontend and auth routers app.include_router(frontend_router) app.include_router(auth_router) diff --git a/frontend/static/explosion.jpg b/frontend/static/explosion.jpg new file mode 100644 index 00000000..4c62522d Binary files /dev/null and b/frontend/static/explosion.jpg differ diff --git a/frontend/templates/500.html b/frontend/templates/500.html new file mode 100644 index 00000000..6f981584 --- /dev/null +++ b/frontend/templates/500.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} +{% block title %}Server Error - DocuNova{% endblock %} + +{% block content %} +
+ +
+ Server Explosion +
+ +
+

500

+

+ Oops! Something Went Wrong. +

+

+ Our servers encountered a mishap and need a moment. Maybe the hamsters spilled their coffee? +

+

+ We're already on it—sorting files, rebooting servers, and recalibrating flux capacitors. +

+ + {% if exc %} +
+ + Show Debug Info + +
{{ exc }}
+
+ {% endif %} + + +
+
+{% endblock %} diff --git a/frontend/templates/files.html b/frontend/templates/files.html new file mode 100644 index 00000000..c975e2dc --- /dev/null +++ b/frontend/templates/files.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} +{% block title %}File Records{% endblock %} + +{% block head_extra %} + + +{% endblock %} + +{% block content %} +
+

File Records

+ +
+
+{% endblock %} + +{% block scripts %} + + + +{% endblock %}