""" Dropbox integration views for setup and OAuth callback. """ import json from fastapi import Query, Request from sqlalchemy.orm import Session from app.models import UserIntegration from app.utils.user_scope import get_current_owner_id from app.views.base import APIRouter, Depends, get_db, require_login, settings, templates router = APIRouter() @router.get("/dropbox-setup") @require_login async def dropbox_setup_page( request: Request, integration_id: int | None = Query(None), db: Session = Depends(get_db), ): """ Setup page for the Dropbox integration. When ``integration_id`` is provided the page operates in **user mode**: the OAuth wizard saves credentials to the named per-user integration record rather than to the global application settings. Only the folder path from the integration's existing config is pre-populated; global admin credentials are never exposed in this mode. """ if integration_id is not None: owner_id = get_current_owner_id(request) integration = ( db.query(UserIntegration) .filter(UserIntegration.id == integration_id, UserIntegration.owner_id == owner_id) .first() ) if integration: cfg: dict = {} if integration.config: try: cfg = json.loads(integration.config) except (json.JSONDecodeError, TypeError): cfg = {} # Support both "folder" (DROPBOX destination) and "folder_path" (WATCH_FOLDER source) folder_path = cfg.get("folder", cfg.get("folder_path", "")) return templates.TemplateResponse( "dropbox.html", { "request": request, "user_mode": True, "is_configured": bool(integration.credentials), "integration_id": integration_id, "integration_name": integration.name, "integration_type": integration.integration_type, "folder_path": folder_path, "app_key_value": "", "app_secret_value": "", "refresh_token_value": "", }, ) # ── Admin / global mode ────────────────────────────────────────────────── is_configured = bool(settings.dropbox_app_key and settings.dropbox_app_secret and settings.dropbox_refresh_token) return templates.TemplateResponse( "dropbox.html", { "request": request, "user_mode": False, "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", "integration_id": integration_id, "integration_name": None, "integration_type": None, }, ) @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 }, )