feat: add frontend views and file management functionality, enhance user response structure, and update configuration guide
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
Frontend views/pages for the application.
|
||||
This module combines all the individual view routers into a single router.
|
||||
"""
|
||||
from fastapi import APIRouter
|
||||
|
||||
# Import routers from view modules
|
||||
from app.views.general import router as general_router
|
||||
from app.views.files import router as files_router
|
||||
from app.views.status import router as status_router
|
||||
from app.views.dropbox import router as dropbox_router
|
||||
from app.views.onedrive import router as onedrive_router
|
||||
|
||||
# Create a combined router
|
||||
router = APIRouter()
|
||||
|
||||
# Include all view routers
|
||||
router.include_router(general_router)
|
||||
router.include_router(files_router)
|
||||
router.include_router(status_router)
|
||||
router.include_router(dropbox_router)
|
||||
router.include_router(onedrive_router)
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Base setup for views, containing shared functionality and imports.
|
||||
"""
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from pathlib import Path
|
||||
from sqlalchemy.orm import Session
|
||||
import logging
|
||||
|
||||
from app.auth import require_login
|
||||
from app.database import SessionLocal
|
||||
from app.config import settings
|
||||
|
||||
# Set up Jinja2 templates
|
||||
templates_dir = Path(__file__).parent.parent.parent / "frontend" / "templates"
|
||||
templates = Jinja2Templates(directory=str(templates_dir))
|
||||
|
||||
# Set up logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_db():
|
||||
"""
|
||||
Dependency to get a database session.
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Dropbox integration views for setup and OAuth callback.
|
||||
"""
|
||||
from fastapi import Request
|
||||
|
||||
from app.views.base import APIRouter, templates, require_login, settings
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/dropbox-setup")
|
||||
@require_login
|
||||
async def dropbox_setup_page(request: Request):
|
||||
"""
|
||||
Setup page for the Dropbox integration.
|
||||
Shows configuration status and setup instructions.
|
||||
"""
|
||||
# Check Dropbox configuration
|
||||
is_configured = bool(settings.dropbox_app_key and
|
||||
settings.dropbox_app_secret and
|
||||
settings.dropbox_refresh_token)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"dropbox.html",
|
||||
{
|
||||
"request": request,
|
||||
"is_configured": is_configured,
|
||||
"app_key_value": settings.dropbox_app_key or "",
|
||||
"app_secret_value": settings.dropbox_app_secret if settings.dropbox_app_secret else "",
|
||||
"refresh_token_value": settings.dropbox_refresh_token if settings.dropbox_refresh_token else "",
|
||||
"folder_path": settings.dropbox_folder or "/Documents/Uploads" # Default folder path
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/dropbox-callback")
|
||||
@require_login
|
||||
async def dropbox_callback(request: Request, code: str = None, error: str = None):
|
||||
"""
|
||||
Callback endpoint for Dropbox OAuth flow.
|
||||
Automatically exchanges the code for a token and saves it to the configuration.
|
||||
"""
|
||||
if error:
|
||||
return templates.TemplateResponse(
|
||||
"dropbox_callback_error.html",
|
||||
{"request": request, "error": error}
|
||||
)
|
||||
|
||||
if not code:
|
||||
return templates.TemplateResponse(
|
||||
"dropbox_callback_error.html",
|
||||
{"request": request, "error": "No authorization code received from Dropbox"}
|
||||
)
|
||||
|
||||
# Display the processing page with automatic token exchange
|
||||
# Note: We provide empty strings for app_key_value and app_secret_value
|
||||
# to prevent overriding what's in sessionStorage
|
||||
return templates.TemplateResponse(
|
||||
"dropbox_callback.html",
|
||||
{
|
||||
"request": request,
|
||||
"code": code,
|
||||
"app_key_value": "", # The callback will prioritize sessionStorage values
|
||||
"app_secret_value": "", # The callback will prioritize sessionStorage values
|
||||
"folder_path": "" # The callback will prioritize sessionStorage values
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
File management views for displaying and managing files.
|
||||
"""
|
||||
from fastapi import Request, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.views.base import APIRouter, templates, require_login, get_db, logger
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/files")
|
||||
@require_login
|
||||
def files_page(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Return the 'files.html' template with files from the database
|
||||
"""
|
||||
try:
|
||||
# Import the model here to avoid circular imports
|
||||
from app.models import FileRecord
|
||||
|
||||
# Fetch all files from the database
|
||||
files = db.query(FileRecord).order_by(FileRecord.created_at.desc()).all()
|
||||
|
||||
# Debug output
|
||||
logger.info(f"Retrieved {len(files)} files from database")
|
||||
|
||||
return templates.TemplateResponse("files.html", {
|
||||
"request": request,
|
||||
"files": files
|
||||
})
|
||||
except Exception as e:
|
||||
# Log any errors
|
||||
logger.error(f"Error retrieving files: {str(e)}")
|
||||
# Return error message to template
|
||||
return templates.TemplateResponse("files.html", {
|
||||
"request": request,
|
||||
"files": [],
|
||||
"error": str(e)
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
General routes for the application homepage and basic pages.
|
||||
"""
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from pathlib import Path
|
||||
|
||||
from app.views.base import APIRouter, templates, require_login
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/", include_in_schema=False)
|
||||
async def serve_index(request: Request):
|
||||
"""Serve the index/home page."""
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
@router.get("/about", include_in_schema=False)
|
||||
async def serve_about(request: Request):
|
||||
"""Serve the about page."""
|
||||
return templates.TemplateResponse("about.html", {"request": request})
|
||||
|
||||
@router.get("/upload", include_in_schema=False)
|
||||
@require_login
|
||||
async def serve_upload(request: Request):
|
||||
"""Serve the upload page."""
|
||||
return templates.TemplateResponse("upload.html", {"request": request})
|
||||
|
||||
@router.get("/favicon.ico", include_in_schema=False)
|
||||
def favicon():
|
||||
"""Serve the favicon."""
|
||||
favicon_path = Path(__file__).parent.parent.parent / "frontend" / "static" / "favicon.ico"
|
||||
if not favicon_path.exists():
|
||||
# If favicon doesn't exist, return a 404
|
||||
raise HTTPException(status_code=404, detail="Favicon not found")
|
||||
return FileResponse(favicon_path)
|
||||
@@ -0,0 +1,68 @@
|
||||
"""
|
||||
OneDrive integration views for setup and OAuth callback.
|
||||
"""
|
||||
from fastapi import Request
|
||||
|
||||
from app.views.base import APIRouter, templates, require_login, settings
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/onedrive-setup")
|
||||
@require_login
|
||||
async def onedrive_setup_page(request: Request):
|
||||
"""
|
||||
Setup page for the OneDrive integration.
|
||||
Shows configuration status and setup instructions.
|
||||
"""
|
||||
# Check OneDrive configuration
|
||||
is_configured = bool(settings.onedrive_client_id and
|
||||
settings.onedrive_client_secret and
|
||||
settings.onedrive_refresh_token)
|
||||
|
||||
# Get configuration values to display status (hide sensitive values)
|
||||
return templates.TemplateResponse(
|
||||
"onedrive.html",
|
||||
{
|
||||
"request": request,
|
||||
"is_configured": is_configured,
|
||||
"client_id": bool(settings.onedrive_client_id),
|
||||
"client_id_value": settings.onedrive_client_id or "", # Pass the actual value for the form
|
||||
"client_secret": bool(settings.onedrive_client_secret),
|
||||
"client_secret_value": settings.onedrive_client_secret if settings.onedrive_client_secret else "",
|
||||
"tenant_id": settings.onedrive_tenant_id,
|
||||
"refresh_token": bool(settings.onedrive_refresh_token),
|
||||
"refresh_token_value": settings.onedrive_refresh_token if settings.onedrive_refresh_token else "",
|
||||
"folder_path": settings.onedrive_folder_path or "Documents/Uploads" # Default folder path
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/onedrive-callback")
|
||||
@require_login
|
||||
async def onedrive_callback(request: Request, code: str = None, error: str = None):
|
||||
"""
|
||||
Callback endpoint for OneDrive OAuth flow.
|
||||
Now automatically exchanges the code for a token and saves it to the configuration.
|
||||
"""
|
||||
if error:
|
||||
return templates.TemplateResponse(
|
||||
"onedrive_callback_error.html",
|
||||
{"request": request, "error": error}
|
||||
)
|
||||
|
||||
if not code:
|
||||
return templates.TemplateResponse(
|
||||
"onedrive_callback_error.html",
|
||||
{"request": request, "error": "No authorization code received from Microsoft"}
|
||||
)
|
||||
|
||||
# Display the processing page with automatic token exchange
|
||||
return templates.TemplateResponse(
|
||||
"onedrive_callback.html",
|
||||
{
|
||||
"request": request,
|
||||
"code": code,
|
||||
"client_id_value": settings.onedrive_client_id or "",
|
||||
"client_secret_value": settings.onedrive_client_secret or "",
|
||||
"tenant_id": settings.onedrive_tenant_id or "common"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
Status and configuration views for the application.
|
||||
"""
|
||||
from fastapi import Request
|
||||
from datetime import datetime
|
||||
|
||||
from app.views.base import APIRouter, templates, require_login, settings
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/status")
|
||||
@require_login
|
||||
async def status_dashboard(request: Request):
|
||||
"""
|
||||
Status dashboard showing all configured integration targets
|
||||
"""
|
||||
from app.utils.config_validator import get_provider_status
|
||||
|
||||
# Get provider status
|
||||
providers = get_provider_status()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"status_dashboard.html",
|
||||
{
|
||||
"request": request,
|
||||
"providers": providers,
|
||||
"app_version": settings.version,
|
||||
"debug_enabled": getattr(settings, 'debug', False),
|
||||
"last_check": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/env")
|
||||
@require_login
|
||||
async def env_debug(request: Request):
|
||||
"""
|
||||
Debug endpoint to view environment variables and settings
|
||||
Uses actual debug setting from config
|
||||
"""
|
||||
# Use the actual debug setting from configuration
|
||||
debug_enabled = settings.debug
|
||||
|
||||
# Get settings data
|
||||
from app.utils.config_validator import get_settings_for_display
|
||||
settings_data = get_settings_for_display(show_values=debug_enabled)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"env_debug.html",
|
||||
{
|
||||
"request": request,
|
||||
"settings": settings_data,
|
||||
"debug_enabled": debug_enabled,
|
||||
"app_version": settings.version
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user