From d6bffa6f0783f4ce4509bd5aa7297d63e4c8891d Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Thu, 25 Jun 2026 04:31:22 +0530 Subject: [PATCH] chore: fix linting --- .../versions/166_add_pat_and_api_access.py | 4 +-- ...migrate_google_oauth_account_ids_to_sub.py | 1 + .../main_agent/tools/automation/create.py | 1 + .../deliverables/tools/generate_image.py | 1 + surfsense_backend/app/app.py | 8 ++--- surfsense_backend/app/auth/csrf.py | 5 ++- surfsense_backend/app/auth/session_cookies.py | 4 ++- .../app/automations/services/automation.py | 2 +- .../app/automations/services/run.py | 2 +- .../app/automations/services/trigger.py | 2 +- surfsense_backend/app/config/__init__.py | 4 +-- .../app/routes/agent_action_log_route.py | 4 +-- .../app/routes/agent_permissions_route.py | 4 +-- surfsense_backend/app/routes/auth_routes.py | 33 ++++++++++++++----- .../app/routes/documents_routes.py | 14 +------- surfsense_backend/app/routes/editor_routes.py | 5 +-- surfsense_backend/app/routes/export_routes.py | 3 +- .../app/routes/folders_routes.py | 12 +------ .../app/routes/gateway_webhook_routes.py | 2 -- .../routes/gateway_whatsapp_baileys_routes.py | 1 - .../app/routes/image_generation_routes.py | 4 +-- surfsense_backend/app/routes/logs_routes.py | 6 ---- .../app/routes/model_connections_routes.py | 4 ++- surfsense_backend/app/routes/notes_routes.py | 4 +-- surfsense_backend/app/routes/rbac_routes.py | 12 ------- .../app/routes/reports_routes.py | 2 -- .../app/routes/sandbox_routes.py | 3 +- .../routes/search_source_connectors_routes.py | 10 ------ .../app/routes/search_spaces_routes.py | 4 ++- .../app/routes/team_memory_routes.py | 5 +-- surfsense_backend/app/routes/users_routes.py | 11 +++++-- .../app/routes/video_presentations_routes.py | 4 --- .../app/routes/zero_context_routes.py | 2 +- surfsense_backend/app/schemas/__init__.py | 2 +- surfsense_backend/app/users.py | 1 - surfsense_backend/app/utils/refresh_tokens.py | 4 ++- .../test_auth_transport_invariant.py | 4 ++- .../gateway/test_byo_long_poll_lifespan.py | 16 ++++++--- .../unit/routes/test_revert_turn_route.py | 4 ++- .../tasks/chat/streaming/test_llm_bundle.py | 4 ++- .../tests/unit/test_pat_fail_closed_static.py | 6 +--- .../tests/unit/utils/test_content_utils.py | 2 +- surfsense_web/app/api/zero/query/route.ts | 3 +- .../components/ApiKeyContent.tsx | 7 ++-- surfsense_web/app/dashboard/page.tsx | 8 +---- surfsense_web/app/layout.tsx | 7 ++-- .../components/obsidian-connect-form.tsx | 4 +-- .../assistant-ui/mermaid-diagram.tsx | 14 ++------ .../new-chat/image-model-selector.tsx | 6 ++-- .../public-chat/public-chat-footer.tsx | 7 +--- .../settings/general-settings-manager.tsx | 4 +-- surfsense_web/hooks/use-pats.ts | 2 +- surfsense_web/lib/apis/base-api.service.ts | 6 +--- .../lib/apis/search-spaces-api.service.ts | 4 +-- surfsense_web/lib/auth-fetch.ts | 6 +--- surfsense_web/proxy.ts | 7 ++-- surfsense_web/zero/queries/authz.ts | 10 ++++-- surfsense_web/zero/queries/automations.ts | 12 ++++--- surfsense_web/zero/queries/documents.ts | 26 +++++++++------ surfsense_web/zero/queries/folders.ts | 13 +++++--- surfsense_web/zero/queries/podcasts.ts | 13 +++++--- 61 files changed, 167 insertions(+), 218 deletions(-) diff --git a/surfsense_backend/alembic/versions/166_add_pat_and_api_access.py b/surfsense_backend/alembic/versions/166_add_pat_and_api_access.py index b49b099a6..fc2526492 100644 --- a/surfsense_backend/alembic/versions/166_add_pat_and_api_access.py +++ b/surfsense_backend/alembic/versions/166_add_pat_and_api_access.py @@ -77,7 +77,5 @@ def upgrade() -> None: def downgrade() -> None: - op.execute( - "ALTER TABLE searchspaces DROP COLUMN IF EXISTS api_access_enabled" - ) + op.execute("ALTER TABLE searchspaces DROP COLUMN IF EXISTS api_access_enabled") op.execute("DROP TABLE IF EXISTS personal_access_tokens") diff --git a/surfsense_backend/alembic/versions/169_migrate_google_oauth_account_ids_to_sub.py b/surfsense_backend/alembic/versions/169_migrate_google_oauth_account_ids_to_sub.py index 76be1fb66..65e29c422 100644 --- a/surfsense_backend/alembic/versions/169_migrate_google_oauth_account_ids_to_sub.py +++ b/surfsense_backend/alembic/versions/169_migrate_google_oauth_account_ids_to_sub.py @@ -7,6 +7,7 @@ Revises: 168 from collections.abc import Sequence import sqlalchemy as sa + from alembic import op revision: str = "169" diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/tools/automation/create.py b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/tools/automation/create.py index fe42410ed..c1122b681 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/tools/automation/create.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/tools/automation/create.py @@ -58,6 +58,7 @@ def create_create_automation_tool( ``AsyncSession`` is opened per call to avoid stale sessions on compiled-agent cache hits (same pattern as the Notion / memory tools). """ + @tool async def create_automation(intent: str, runtime: ToolRuntime) -> dict[str, Any]: """Draft + save an automation from a natural-language intent. diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py index ae7e33428..c481c6c3d 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py @@ -242,6 +242,7 @@ def create_generate_image_tool( # Update all image URLs in response_dict to be absolute (for the serving endpoint) from urllib.parse import urlparse + for image in images: if image.get("url"): raw_url: str = image["url"] diff --git a/surfsense_backend/app/app.py b/surfsense_backend/app/app.py index 3247ee131..1c81c8c29 100644 --- a/surfsense_backend/app/app.py +++ b/surfsense_backend/app/app.py @@ -949,9 +949,7 @@ if config.AUTH_TYPE == "GOOGLE": ) async def google_authorize(request: Request, response: Response): """Return Google's authorization URL, matching fastapi-users' shape.""" - return { - "authorization_url": await _google_authorization_url(request, response) - } + return {"authorization_url": await _google_authorization_url(request, response)} @app.get( "/auth/google/callback", @@ -1015,7 +1013,9 @@ if config.AUTH_TYPE == "GOOGLE": data=token_payload, ) if token_response.status_code >= 400: - _error_logger.warning("Web Google OAuth exchange failed: %s", token_response.text) + _error_logger.warning( + "Web Google OAuth exchange failed: %s", token_response.text + ) raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="OAuth exchange failed", diff --git a/surfsense_backend/app/auth/csrf.py b/surfsense_backend/app/auth/csrf.py index 3c4320a79..4f1b6db4a 100644 --- a/surfsense_backend/app/auth/csrf.py +++ b/surfsense_backend/app/auth/csrf.py @@ -43,7 +43,10 @@ class CsrfOriginMiddleware(BaseHTTPMiddleware): # PAT/Bearer credentials are not ambient browser credentials and are not # CSRF-able. Enforce only when the web session cookie is the credential. - if request.headers.get("Authorization") or config.SESSION_COOKIE_NAME not in request.cookies: + if ( + request.headers.get("Authorization") + or config.SESSION_COOKIE_NAME not in request.cookies + ): return await call_next(request) origin = request.headers.get("Origin") or _origin_from_url( diff --git a/surfsense_backend/app/auth/session_cookies.py b/surfsense_backend/app/auth/session_cookies.py index 024e4a0a9..835db0ac1 100644 --- a/surfsense_backend/app/auth/session_cookies.py +++ b/surfsense_backend/app/auth/session_cookies.py @@ -88,7 +88,9 @@ def clear_session(response: Response, request: Request | None = None) -> None: ) -def read_refresh(request: Request, body: Any | None = None) -> tuple[str | None, TransportMode]: +def read_refresh( + request: Request, body: Any | None = None +) -> tuple[str | None, TransportMode]: cookie = request.cookies.get(config.REFRESH_COOKIE_NAME) if cookie: return cookie, TransportMode.COOKIE diff --git a/surfsense_backend/app/automations/services/automation.py b/surfsense_backend/app/automations/services/automation.py index 261d41bfc..ed748fb7c 100644 --- a/surfsense_backend/app/automations/services/automation.py +++ b/surfsense_backend/app/automations/services/automation.py @@ -10,6 +10,7 @@ from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload +from app.auth.context import AuthContext from app.automations.persistence.enums.trigger_type import TriggerType from app.automations.persistence.models.automation import Automation from app.automations.persistence.models.trigger import AutomationTrigger @@ -27,7 +28,6 @@ from app.automations.services.model_policy import ( ) from app.automations.triggers import get_trigger from app.automations.triggers.builtin.schedule import compute_next_fire_at -from app.auth.context import AuthContext from app.db import Permission, SearchSpace, get_async_session from app.users import get_auth_context from app.utils.rbac import check_permission diff --git a/surfsense_backend/app/automations/services/run.py b/surfsense_backend/app/automations/services/run.py index 8ef763e5e..9bcd1393e 100644 --- a/surfsense_backend/app/automations/services/run.py +++ b/surfsense_backend/app/automations/services/run.py @@ -6,9 +6,9 @@ from fastapi import Depends, HTTPException from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession +from app.auth.context import AuthContext from app.automations.persistence.models.automation import Automation from app.automations.persistence.models.run import AutomationRun -from app.auth.context import AuthContext from app.db import Permission, get_async_session from app.users import get_auth_context from app.utils.rbac import check_permission diff --git a/surfsense_backend/app/automations/services/trigger.py b/surfsense_backend/app/automations/services/trigger.py index 7ff6e56fa..52c827c67 100644 --- a/surfsense_backend/app/automations/services/trigger.py +++ b/surfsense_backend/app/automations/services/trigger.py @@ -8,13 +8,13 @@ from fastapi import Depends, HTTPException from pydantic import ValidationError from sqlalchemy.ext.asyncio import AsyncSession +from app.auth.context import AuthContext from app.automations.persistence.enums.trigger_type import TriggerType from app.automations.persistence.models.automation import Automation from app.automations.persistence.models.trigger import AutomationTrigger from app.automations.schemas.api import TriggerCreate, TriggerUpdate from app.automations.triggers import get_trigger from app.automations.triggers.builtin.schedule import compute_next_fire_at -from app.auth.context import AuthContext from app.db import Permission, get_async_session from app.users import get_auth_context from app.utils.rbac import check_permission diff --git a/surfsense_backend/app/config/__init__.py b/surfsense_backend/app/config/__init__.py index f4f44a385..47e529741 100644 --- a/surfsense_backend/app/config/__init__.py +++ b/surfsense_backend/app/config/__init__.py @@ -948,9 +948,7 @@ class Config: if origin.strip() ] _PAT_MAX_EXPIRY_DAYS = os.getenv("PAT_MAX_EXPIRY_DAYS", "").strip() - PAT_MAX_EXPIRY_DAYS = ( - int(_PAT_MAX_EXPIRY_DAYS) if _PAT_MAX_EXPIRY_DAYS else None - ) + PAT_MAX_EXPIRY_DAYS = int(_PAT_MAX_EXPIRY_DAYS) if _PAT_MAX_EXPIRY_DAYS else None # ETL Service ETL_SERVICE = os.getenv("ETL_SERVICE") diff --git a/surfsense_backend/app/routes/agent_action_log_route.py b/surfsense_backend/app/routes/agent_action_log_route.py index bf94ae3b4..72086b8ae 100644 --- a/surfsense_backend/app/routes/agent_action_log_route.py +++ b/surfsense_backend/app/routes/agent_action_log_route.py @@ -28,13 +28,12 @@ from pydantic import BaseModel from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession -from app.auth.context import AuthContext from app.agents.chat.multi_agent_chat.shared.feature_flags import get_flags +from app.auth.context import AuthContext from app.db import ( AgentActionLog, NewChatThread, Permission, - User, get_async_session, ) from app.users import get_auth_context @@ -114,7 +113,6 @@ async def list_thread_actions( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ) -> AgentActionListResponse: - user = auth.user """List agent actions for a thread, newest first. Authorization: diff --git a/surfsense_backend/app/routes/agent_permissions_route.py b/surfsense_backend/app/routes/agent_permissions_route.py index 521adfb03..1eb8b1a37 100644 --- a/surfsense_backend/app/routes/agent_permissions_route.py +++ b/surfsense_backend/app/routes/agent_permissions_route.py @@ -30,14 +30,13 @@ from sqlalchemy import select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession -from app.auth.context import AuthContext from app.agents.chat.multi_agent_chat.shared.feature_flags import get_flags +from app.auth.context import AuthContext from app.db import ( AgentPermissionRule, NewChatThread, Permission, SearchSpace, - User, get_async_session, ) from app.users import get_auth_context @@ -136,7 +135,6 @@ def _to_read(row: AgentPermissionRule) -> AgentPermissionRuleRead: async def _ensure_search_space_membership_admin( session: AsyncSession, auth: AuthContext, search_space_id: int ) -> None: - user = auth.user """Curating agent rules == "settings" administration on the space.""" space = await session.get(SearchSpace, search_space_id) if space is None: diff --git a/surfsense_backend/app/routes/auth_routes.py b/surfsense_backend/app/routes/auth_routes.py index 9649fda4e..5674f4d12 100644 --- a/surfsense_backend/app/routes/auth_routes.py +++ b/surfsense_backend/app/routes/auth_routes.py @@ -75,7 +75,10 @@ async def resolve_google_user( account id regardless of the current email claim. """ if not claims.get("sub") or not claims.get("email"): - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid Google identity token") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid Google identity token", + ) sub = claims["sub"] email_verified = bool(claims.get("email_verified")) @@ -202,7 +205,9 @@ async def logout_all_devices( """ user: User | None = None try: - auth = await get_auth_context(request, session=session, user_manager=user_manager) + auth = await get_auth_context( + request, session=session, user_manager=user_manager + ) if auth.is_session: user = auth.user except HTTPException: @@ -210,7 +215,9 @@ async def logout_all_devices( if user is None: refresh_token, _mode = read_refresh(request, body) - token_record = await validate_refresh_token(refresh_token) if refresh_token else None + token_record = ( + await validate_refresh_token(refresh_token) if refresh_token else None + ) if token_record: user = await _load_user(token_record.user_id) @@ -243,7 +250,9 @@ async def get_session( access_token = token if access_token is None: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized" + ) return SessionResponse(access_expires_at=access_expires_at(access_token)) @@ -298,7 +307,9 @@ async def create_desktop_session( and redirect_port is not None and parsed_redirect.path == "/callback" ): - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid redirect URI") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid redirect URI" + ) if not config.GOOGLE_DESKTOP_CLIENT_ID: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, @@ -316,15 +327,21 @@ async def create_desktop_session( token_payload["client_secret"] = config.GOOGLE_DESKTOP_CLIENT_SECRET async with httpx.AsyncClient(timeout=10) as client: - token_response = await client.post("https://oauth2.googleapis.com/token", data=token_payload) + token_response = await client.post( + "https://oauth2.googleapis.com/token", data=token_payload + ) if token_response.status_code >= 400: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="OAuth exchange failed") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="OAuth exchange failed" + ) token_data = token_response.json() id_token = token_data.get("id_token") access_token = token_data.get("access_token") if not id_token or not access_token: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="OAuth exchange failed") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="OAuth exchange failed" + ) try: claims = google_id_token.verify_oauth2_token( diff --git a/surfsense_backend/app/routes/documents_routes.py b/surfsense_backend/app/routes/documents_routes.py index 9d908f4a1..accf3b18f 100644 --- a/surfsense_backend/app/routes/documents_routes.py +++ b/surfsense_backend/app/routes/documents_routes.py @@ -7,8 +7,8 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from sqlalchemy.orm import selectinload -from app.auth.context import AuthContext from app.agents.chat.runtime.path_resolver import virtual_path_to_doc +from app.auth.context import AuthContext from app.db import ( Chunk, Document, @@ -18,7 +18,6 @@ from app.db import ( Permission, SearchSpace, SearchSpaceMembership, - User, get_async_session, ) from app.schemas import ( @@ -684,7 +683,6 @@ async def search_document_titles( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Lightweight document title search optimized for mention picker (@mentions). @@ -789,7 +787,6 @@ async def get_document_by_virtual_path( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Resolve a knowledge-base document by its agent-facing virtual path. The agent renders every document under ``/documents/...`` with a @@ -847,7 +844,6 @@ async def get_documents_status( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Batch status endpoint for documents in a search space. @@ -1071,7 +1067,6 @@ async def get_watched_folders( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Return root folders that are marked as watched (metadata->>'watched' = 'true').""" await check_permission( session, @@ -1113,7 +1108,6 @@ async def get_document_chunks_paginated( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Paginated chunk loading for a document. Supports both page-based and offset-based access. @@ -1175,7 +1169,6 @@ async def read_document( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get a specific document by ID. Requires DOCUMENTS_READ permission for the search space. @@ -1230,7 +1223,6 @@ async def update_document( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Update a document. Requires DOCUMENTS_UPDATE permission for the search space. @@ -1290,7 +1282,6 @@ async def delete_document( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Delete a document. Requires DOCUMENTS_DELETE permission for the search space. @@ -1536,7 +1527,6 @@ async def folder_mtime_check( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Pre-upload optimization: check which files need uploading based on mtime. Returns the subset of relative paths where the file is new or has a @@ -1754,7 +1744,6 @@ async def folder_unlink( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Handle file deletion events from the desktop watcher. For each relative path, find the matching document and delete it. @@ -1809,7 +1798,6 @@ async def folder_sync_finalize( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Finalize a full folder scan by deleting orphaned documents. The client sends the complete list of relative paths currently in the diff --git a/surfsense_backend/app/routes/editor_routes.py b/surfsense_backend/app/routes/editor_routes.py index fe00995ea..0bc1dd45f 100644 --- a/surfsense_backend/app/routes/editor_routes.py +++ b/surfsense_backend/app/routes/editor_routes.py @@ -19,7 +19,7 @@ from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.auth.context import AuthContext -from app.db import Chunk, Document, DocumentType, Permission, User, get_async_session +from app.db import Chunk, Document, DocumentType, Permission, get_async_session from app.routes.reports_routes import ( _FILE_EXTENSIONS, _MEDIA_TYPES, @@ -50,7 +50,6 @@ async def get_editor_content( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get document content for editing. @@ -182,7 +181,6 @@ async def download_document_markdown( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Download the full document content as a .md file. Reconstructs markdown from source_markdown or chunks. @@ -337,7 +335,6 @@ async def export_document( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Export a document in the requested format (reuses the report export pipeline).""" await check_permission( session, diff --git a/surfsense_backend/app/routes/export_routes.py b/surfsense_backend/app/routes/export_routes.py index 8e419157f..70df33b2e 100644 --- a/surfsense_backend/app/routes/export_routes.py +++ b/surfsense_backend/app/routes/export_routes.py @@ -8,7 +8,7 @@ from fastapi.responses import StreamingResponse from sqlalchemy.ext.asyncio import AsyncSession from app.auth.context import AuthContext -from app.db import Permission, User, get_async_session +from app.db import Permission, get_async_session from app.services.export_service import build_export_zip from app.users import get_auth_context from app.utils.rbac import check_permission @@ -27,7 +27,6 @@ async def export_knowledge_base( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Export documents as a ZIP of markdown files preserving folder structure.""" await check_permission( session, diff --git a/surfsense_backend/app/routes/folders_routes.py b/surfsense_backend/app/routes/folders_routes.py index 8a5dfcb73..1da0c9b0e 100644 --- a/surfsense_backend/app/routes/folders_routes.py +++ b/surfsense_backend/app/routes/folders_routes.py @@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from app.auth.context import AuthContext -from app.db import Document, Folder, Permission, User, get_async_session +from app.db import Document, Folder, Permission, get_async_session from app.schemas import ( BulkDocumentMove, DocumentMove, @@ -95,7 +95,6 @@ async def list_folders( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """List all folders in a search space (flat). Requires DOCUMENTS_READ permission.""" try: await check_permission( @@ -127,7 +126,6 @@ async def get_folder( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Get a single folder. Requires DOCUMENTS_READ permission.""" try: folder = await session.get(Folder, folder_id) @@ -158,7 +156,6 @@ async def get_folder_breadcrumb( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Get ancestor chain for breadcrumb display. Requires DOCUMENTS_READ permission.""" try: folder = await session.get(Folder, folder_id) @@ -203,7 +200,6 @@ async def stop_watching_folder( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Clear the watched flag from a folder's metadata.""" folder = await session.get(Folder, folder_id) if not folder: @@ -232,7 +228,6 @@ async def update_folder( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Rename a folder. Requires DOCUMENTS_UPDATE permission.""" try: folder = await session.get(Folder, folder_id) @@ -273,7 +268,6 @@ async def move_folder( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Move a folder to a new parent. Requires DOCUMENTS_UPDATE permission.""" try: folder = await session.get(Folder, folder_id) @@ -334,7 +328,6 @@ async def reorder_folder( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Reorder a folder among its siblings via fractional indexing. Requires DOCUMENTS_UPDATE.""" try: folder = await session.get(Folder, folder_id) @@ -376,7 +369,6 @@ async def delete_folder( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Mark documents for deletion and dispatch Celery to delete docs first, then folders.""" try: folder = await session.get(Folder, folder_id) @@ -451,7 +443,6 @@ async def move_document( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Move a document to a folder (or root). Requires DOCUMENTS_UPDATE permission.""" try: result = await session.execute( @@ -498,7 +489,6 @@ async def bulk_move_documents( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Move multiple documents to a folder (or root). Requires DOCUMENTS_UPDATE permission.""" try: if not request.document_ids: diff --git a/surfsense_backend/app/routes/gateway_webhook_routes.py b/surfsense_backend/app/routes/gateway_webhook_routes.py index 0d05f4baf..931794059 100644 --- a/surfsense_backend/app/routes/gateway_webhook_routes.py +++ b/surfsense_backend/app/routes/gateway_webhook_routes.py @@ -30,7 +30,6 @@ from app.db import ( ExternalChatHealthStatus, ExternalChatPeerKind, ExternalChatPlatform, - User, get_async_session, ) from app.gateway.accounts import ( @@ -979,7 +978,6 @@ async def list_platforms( async def get_gateway_config( auth: AuthContext = Depends(get_auth_context), ) -> dict[str, bool | str]: - user = auth.user if not config.GATEWAY_ENABLED: return { "enabled": False, diff --git a/surfsense_backend/app/routes/gateway_whatsapp_baileys_routes.py b/surfsense_backend/app/routes/gateway_whatsapp_baileys_routes.py index 370b1cc8d..95c8fe12b 100644 --- a/surfsense_backend/app/routes/gateway_whatsapp_baileys_routes.py +++ b/surfsense_backend/app/routes/gateway_whatsapp_baileys_routes.py @@ -101,7 +101,6 @@ async def request_pairing_code( async def bridge_health( auth: AuthContext = Depends(get_auth_context), ) -> dict[str, Any]: - user = auth.user _ensure_baileys_enabled() adapter = WhatsAppBaileysAdapter() try: diff --git a/surfsense_backend/app/routes/image_generation_routes.py b/surfsense_backend/app/routes/image_generation_routes.py index 9376c8f0f..96cb3825c 100644 --- a/surfsense_backend/app/routes/image_generation_routes.py +++ b/surfsense_backend/app/routes/image_generation_routes.py @@ -24,7 +24,6 @@ from app.db import ( Permission, SearchSpace, SearchSpaceMembership, - User, get_async_session, ) from app.schemas import ( @@ -224,6 +223,7 @@ async def _execute_image_generation( # Fix relative URLs in response data (for the serving endpoint) from urllib.parse import urlparse + images = response_dict.get("data", []) provider_base_url = resolved_kwargs.get("api_base") for image in images: @@ -422,7 +422,6 @@ async def get_image_generation( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Get a specific image generation by ID.""" try: result = await session.execute( @@ -455,7 +454,6 @@ async def delete_image_generation( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Delete an image generation record.""" try: result = await session.execute( diff --git a/surfsense_backend/app/routes/logs_routes.py b/surfsense_backend/app/routes/logs_routes.py index 16400ef0b..28c3e4fd1 100644 --- a/surfsense_backend/app/routes/logs_routes.py +++ b/surfsense_backend/app/routes/logs_routes.py @@ -13,7 +13,6 @@ from app.db import ( Permission, SearchSpace, SearchSpaceMembership, - User, get_async_session, ) from app.schemas import LogCreate, LogRead, LogUpdate @@ -29,7 +28,6 @@ async def create_log( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Create a new log entry. Note: This is typically called internally. Requires LOGS_READ permission (since logs are usually system-generated). @@ -141,7 +139,6 @@ async def read_log( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get a specific log by ID. Requires LOGS_READ permission for the search space. @@ -178,7 +175,6 @@ async def update_log( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Update a log entry. Requires LOGS_READ permission (logs are typically updated by system). @@ -222,7 +218,6 @@ async def delete_log( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Delete a log entry. Requires LOGS_DELETE permission for the search space. @@ -262,7 +257,6 @@ async def get_logs_summary( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get a summary of logs for a search space in the last X hours. Requires LOGS_READ permission for the search space. diff --git a/surfsense_backend/app/routes/model_connections_routes.py b/surfsense_backend/app/routes/model_connections_routes.py index d75e1de79..84e9b830d 100644 --- a/surfsense_backend/app/routes/model_connections_routes.py +++ b/surfsense_backend/app/routes/model_connections_routes.py @@ -325,7 +325,9 @@ async def _assert_connection_access( @router.get("/global-llm-config-status") -async def global_llm_config_status(auth: AuthContext = Depends(require_session_context)): +async def global_llm_config_status( + auth: AuthContext = Depends(require_session_context), +): del auth return {"exists": config.GLOBAL_LLM_CONFIG_FILE_EXISTS} diff --git a/surfsense_backend/app/routes/notes_routes.py b/surfsense_backend/app/routes/notes_routes.py index e5cca8700..eb3c66b5f 100644 --- a/surfsense_backend/app/routes/notes_routes.py +++ b/surfsense_backend/app/routes/notes_routes.py @@ -10,7 +10,7 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.auth.context import AuthContext -from app.db import Document, DocumentType, Permission, User, get_async_session +from app.db import Document, DocumentType, Permission, get_async_session from app.schemas import DocumentRead, PaginatedResponse from app.users import get_auth_context from app.utils.rbac import check_permission @@ -102,7 +102,6 @@ async def list_notes( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ List all notes in a search space. @@ -196,7 +195,6 @@ async def delete_note( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Delete a note. diff --git a/surfsense_backend/app/routes/rbac_routes.py b/surfsense_backend/app/routes/rbac_routes.py index 3d50d589d..e1122b2bb 100644 --- a/surfsense_backend/app/routes/rbac_routes.py +++ b/surfsense_backend/app/routes/rbac_routes.py @@ -125,7 +125,6 @@ PERMISSION_DESCRIPTIONS = { async def list_all_permissions( auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ List all available permissions that can be assigned to roles. """ @@ -162,7 +161,6 @@ async def create_role( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Create a new custom role in a search space. Requires ROLES_CREATE permission. @@ -244,7 +242,6 @@ async def list_roles( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ List all roles in a search space. Requires ROLES_READ permission. @@ -283,7 +280,6 @@ async def get_role( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get a specific role by ID. Requires ROLES_READ permission. @@ -329,7 +325,6 @@ async def update_role( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Update a role. Requires ROLES_UPDATE permission. @@ -427,7 +422,6 @@ async def delete_role( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Delete a custom role. Requires ROLES_DELETE permission. @@ -485,7 +479,6 @@ async def list_members( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ List all members of a search space. Requires MEMBERS_VIEW permission. @@ -551,7 +544,6 @@ async def update_member_role( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Update a member's role. Requires MEMBERS_MANAGE_ROLES permission. @@ -689,7 +681,6 @@ async def remove_member( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Remove a member from a search space. Requires MEMBERS_REMOVE permission. @@ -814,7 +805,6 @@ async def list_invites( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ List all invites for a search space. Requires MEMBERS_INVITE permission. @@ -854,7 +844,6 @@ async def update_invite( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Update an invite. Requires MEMBERS_INVITE permission. @@ -921,7 +910,6 @@ async def revoke_invite( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Revoke (delete) an invite. Requires MEMBERS_INVITE permission. diff --git a/surfsense_backend/app/routes/reports_routes.py b/surfsense_backend/app/routes/reports_routes.py index d5996485e..bdcf8a874 100644 --- a/surfsense_backend/app/routes/reports_routes.py +++ b/surfsense_backend/app/routes/reports_routes.py @@ -33,7 +33,6 @@ from app.db import ( Report, SearchSpace, SearchSpaceMembership, - User, get_async_session, ) from app.schemas import ReportContentRead, ReportContentUpdate, ReportRead @@ -161,7 +160,6 @@ async def _get_report_with_access( session: AsyncSession, auth: AuthContext, ) -> Report: - user = auth.user """Fetch a report and verify the user belongs to its search space. Raises HTTPException(404) if not found, HTTPException(403) if no access. diff --git a/surfsense_backend/app/routes/sandbox_routes.py b/surfsense_backend/app/routes/sandbox_routes.py index e7974b993..c04abe9ee 100644 --- a/surfsense_backend/app/routes/sandbox_routes.py +++ b/surfsense_backend/app/routes/sandbox_routes.py @@ -11,7 +11,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from app.auth.context import AuthContext -from app.db import NewChatThread, Permission, User, get_async_session +from app.db import NewChatThread, Permission, get_async_session from app.users import get_auth_context from app.utils.rbac import check_permission @@ -50,7 +50,6 @@ async def download_sandbox_file( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Download a file from the Daytona sandbox associated with a chat thread.""" from app.agents.chat.multi_agent_chat.shared.middleware.filesystem.sandbox import ( diff --git a/surfsense_backend/app/routes/search_source_connectors_routes.py b/surfsense_backend/app/routes/search_source_connectors_routes.py index fab79ab49..718b4b907 100644 --- a/surfsense_backend/app/routes/search_source_connectors_routes.py +++ b/surfsense_backend/app/routes/search_source_connectors_routes.py @@ -40,7 +40,6 @@ from app.db import ( Permission, SearchSourceConnector, SearchSourceConnectorType, - User, async_session_maker, get_async_session, ) @@ -286,7 +285,6 @@ async def read_search_source_connectors( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ List all search source connectors for a search space. Requires CONNECTORS_READ permission. @@ -330,7 +328,6 @@ async def read_search_source_connector( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get a specific search source connector by ID. Requires CONNECTORS_READ permission. @@ -565,7 +562,6 @@ async def delete_search_source_connector( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Delete a search source connector and all its associated documents. @@ -2735,7 +2731,6 @@ async def list_mcp_connectors( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ List all MCP connectors for a search space. @@ -2787,7 +2782,6 @@ async def get_mcp_connector( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get a specific MCP connector by ID. @@ -2841,7 +2835,6 @@ async def update_mcp_connector( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Update an MCP connector. @@ -2918,7 +2911,6 @@ async def delete_mcp_connector( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Delete an MCP connector. @@ -2977,7 +2969,6 @@ async def test_mcp_server_connection( server_config: dict = Body(...), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Test connection to an MCP server and fetch available tools. @@ -3058,7 +3049,6 @@ async def get_drive_picker_token( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """Return an OAuth access token + client ID for the Google Picker API.""" result = await session.execute( select(SearchSourceConnector).filter(SearchSourceConnector.id == connector_id) diff --git a/surfsense_backend/app/routes/search_spaces_routes.py b/surfsense_backend/app/routes/search_spaces_routes.py index e92f7dfc1..6eebaf201 100644 --- a/surfsense_backend/app/routes/search_spaces_routes.py +++ b/surfsense_backend/app/routes/search_spaces_routes.py @@ -279,7 +279,9 @@ async def update_search_space( ) from e -@router.put("/searchspaces/{search_space_id}/api-access", response_model=SearchSpaceRead) +@router.put( + "/searchspaces/{search_space_id}/api-access", response_model=SearchSpaceRead +) async def update_search_space_api_access( search_space_id: int, body: SearchSpaceApiAccessUpdate, diff --git a/surfsense_backend/app/routes/team_memory_routes.py b/surfsense_backend/app/routes/team_memory_routes.py index 3ded87d36..76d934cb2 100644 --- a/surfsense_backend/app/routes/team_memory_routes.py +++ b/surfsense_backend/app/routes/team_memory_routes.py @@ -7,7 +7,7 @@ from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from app.auth.context import AuthContext -from app.db import User, get_async_session +from app.db import get_async_session from app.services.memory import ( MemoryRead, MemoryScope, @@ -32,7 +32,6 @@ async def get_team_memory( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user await check_search_space_access(session, auth, search_space_id) memory_md = await read_memory( scope=MemoryScope.TEAM, @@ -49,7 +48,6 @@ async def update_team_memory( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user await check_search_space_access(session, auth, search_space_id) result = await save_memory( scope=MemoryScope.TEAM, @@ -68,7 +66,6 @@ async def reset_team_memory( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user await check_search_space_access(session, auth, search_space_id) result = await reset_memory( scope=MemoryScope.TEAM, diff --git a/surfsense_backend/app/routes/users_routes.py b/surfsense_backend/app/routes/users_routes.py index 540549119..dad8847af 100644 --- a/surfsense_backend/app/routes/users_routes.py +++ b/surfsense_backend/app/routes/users_routes.py @@ -4,7 +4,12 @@ from fastapi import APIRouter, Depends, Request from app.auth.context import AuthContext from app.schemas import UserRead, UserUpdate -from app.users import UserManager, get_auth_context, get_user_manager, require_session_context +from app.users import ( + UserManager, + get_auth_context, + get_user_manager, + require_session_context, +) router = APIRouter(prefix="/users", tags=["users"]) @@ -23,5 +28,7 @@ async def update_current_user_profile( auth: AuthContext = Depends(require_session_context), user_manager: UserManager = Depends(get_user_manager), ): - updated_user = await user_manager.update(update, auth.user, safe=True, request=request) + updated_user = await user_manager.update( + update, auth.user, safe=True, request=request + ) return updated_user diff --git a/surfsense_backend/app/routes/video_presentations_routes.py b/surfsense_backend/app/routes/video_presentations_routes.py index 189a050e4..e40ccb2f9 100644 --- a/surfsense_backend/app/routes/video_presentations_routes.py +++ b/surfsense_backend/app/routes/video_presentations_routes.py @@ -21,7 +21,6 @@ from app.db import ( Permission, SearchSpace, SearchSpaceMembership, - User, VideoPresentation, get_async_session, ) @@ -93,7 +92,6 @@ async def read_video_presentation( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Get a specific video presentation by ID. Requires authentication with VIDEO_PRESENTATIONS_READ permission. @@ -137,7 +135,6 @@ async def delete_video_presentation( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Delete a video presentation. Requires VIDEO_PRESENTATIONS_DELETE permission for the search space. @@ -181,7 +178,6 @@ async def stream_slide_audio( session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - user = auth.user """ Stream the audio file for a specific slide in a video presentation. The slide_number is 1-based. Audio path is read from the slides JSONB. diff --git a/surfsense_backend/app/routes/zero_context_routes.py b/surfsense_backend/app/routes/zero_context_routes.py index 48772c5d1..27d4d6ce0 100644 --- a/surfsense_backend/app/routes/zero_context_routes.py +++ b/surfsense_backend/app/routes/zero_context_routes.py @@ -1,7 +1,7 @@ """Zero sync authentication context routes.""" -from pydantic import BaseModel from fastapi import APIRouter, Depends +from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from app.auth.context import AuthContext diff --git a/surfsense_backend/app/schemas/__init__.py b/surfsense_backend/app/schemas/__init__.py index 1566310e1..f111f0226 100644 --- a/surfsense_backend/app/schemas/__init__.py +++ b/surfsense_backend/app/schemas/__init__.py @@ -242,9 +242,9 @@ __all__ = [ "SearchSourceConnectorCreate", "SearchSourceConnectorRead", "SearchSourceConnectorUpdate", + "SearchSpaceApiAccessUpdate", # Search space schemas "SearchSpaceBase", - "SearchSpaceApiAccessUpdate", "SearchSpaceCreate", "SearchSpaceRead", "SearchSpaceUpdate", diff --git a/surfsense_backend/app/users.py b/surfsense_backend/app/users.py index 524904ad7..bf9ec74d1 100644 --- a/surfsense_backend/app/users.py +++ b/surfsense_backend/app/users.py @@ -401,4 +401,3 @@ async def require_session_context( detail="This action requires an interactive session", ) return auth - diff --git a/surfsense_backend/app/utils/refresh_tokens.py b/surfsense_backend/app/utils/refresh_tokens.py index a1c5b658f..6a96dd803 100644 --- a/surfsense_backend/app/utils/refresh_tokens.py +++ b/surfsense_backend/app/utils/refresh_tokens.py @@ -51,7 +51,9 @@ async def create_refresh_token( token_hash = hash_token(token) now = datetime.now(UTC) if absolute_expiry is None: - absolute_expiry = now + timedelta(seconds=config.REFRESH_ABSOLUTE_LIFETIME_SECONDS) + absolute_expiry = now + timedelta( + seconds=config.REFRESH_ABSOLUTE_LIFETIME_SECONDS + ) expires_at = min( now + timedelta(seconds=config.REFRESH_TOKEN_LIFETIME_SECONDS), absolute_expiry, diff --git a/surfsense_backend/tests/integration/test_auth_transport_invariant.py b/surfsense_backend/tests/integration/test_auth_transport_invariant.py index f828f022e..386411d3b 100644 --- a/surfsense_backend/tests/integration/test_auth_transport_invariant.py +++ b/surfsense_backend/tests/integration/test_auth_transport_invariant.py @@ -56,7 +56,9 @@ def test_cookie_transport_re_stamps_access_without_refresh_body_or_cookie(): set_cookie_headers = response.headers.getlist("set-cookie") assert any(config.SESSION_COOKIE_NAME in header for header in set_cookie_headers) - assert not any(config.REFRESH_COOKIE_NAME in header for header in set_cookie_headers) + assert not any( + config.REFRESH_COOKIE_NAME in header for header in set_cookie_headers + ) def test_header_transport_returns_body_tokens_without_cookies(): diff --git a/surfsense_backend/tests/unit/gateway/test_byo_long_poll_lifespan.py b/surfsense_backend/tests/unit/gateway/test_byo_long_poll_lifespan.py index c184af601..38fde8a06 100644 --- a/surfsense_backend/tests/unit/gateway/test_byo_long_poll_lifespan.py +++ b/surfsense_backend/tests/unit/gateway/test_byo_long_poll_lifespan.py @@ -40,7 +40,9 @@ async def cleanup_supervisors(): async def test_start_byo_long_poll_noops_when_mode_is_webhook(monkeypatch): monkeypatch.setattr(byo_long_poll.config, "GATEWAY_ENABLED", True) monkeypatch.setattr(byo_long_poll.config, "GATEWAY_TELEGRAM_INTAKE_MODE", "webhook") - monkeypatch.setattr(byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled") + monkeypatch.setattr( + byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled" + ) await byo_long_poll.start_byo_long_poll_supervisors() @@ -53,7 +55,9 @@ async def test_start_byo_long_poll_noops_when_no_byo_accounts(mocker, monkeypatc monkeypatch.setattr( byo_long_poll.config, "GATEWAY_TELEGRAM_INTAKE_MODE", "longpoll" ) - monkeypatch.setattr(byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled") + monkeypatch.setattr( + byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled" + ) session = mocker.AsyncMock() session.execute.return_value = ScalarResult([]) monkeypatch.setattr( @@ -75,7 +79,9 @@ async def test_start_byo_long_poll_spawns_one_supervisor_per_account( monkeypatch.setattr( byo_long_poll.config, "GATEWAY_TELEGRAM_INTAKE_MODE", "longpoll" ) - monkeypatch.setattr(byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled") + monkeypatch.setattr( + byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled" + ) accounts = [mocker.Mock(id=1), mocker.Mock(id=2)] session = mocker.AsyncMock() session.execute.return_value = ScalarResult(accounts) @@ -125,7 +131,9 @@ async def test_shutdown_cancels_running_supervisors(mocker, monkeypatch): monkeypatch.setattr( byo_long_poll.config, "GATEWAY_TELEGRAM_INTAKE_MODE", "longpoll" ) - monkeypatch.setattr(byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled") + monkeypatch.setattr( + byo_long_poll.config, "GATEWAY_WHATSAPP_INTAKE_MODE", "disabled" + ) session = mocker.AsyncMock() session.execute.return_value = ScalarResult([mocker.Mock(id=1)]) monkeypatch.setattr( diff --git a/surfsense_backend/tests/unit/routes/test_revert_turn_route.py b/surfsense_backend/tests/unit/routes/test_revert_turn_route.py index 44fcfe042..09d913b9c 100644 --- a/surfsense_backend/tests/unit/routes/test_revert_turn_route.py +++ b/surfsense_backend/tests/unit/routes/test_revert_turn_route.py @@ -450,7 +450,9 @@ class TestRevertTurnDispatch: thread_id=1, chat_turn_id="ct-mixed-all", session=session, - auth=AuthContext.session(_FakeUser()), # only id=7 has a different user_id + auth=AuthContext.session( + _FakeUser() + ), # only id=7 has a different user_id ) assert response.total == len(rows) == 6 diff --git a/surfsense_backend/tests/unit/tasks/chat/streaming/test_llm_bundle.py b/surfsense_backend/tests/unit/tasks/chat/streaming/test_llm_bundle.py index cecf8be5d..7bb169496 100644 --- a/surfsense_backend/tests/unit/tasks/chat/streaming/test_llm_bundle.py +++ b/surfsense_backend/tests/unit/tasks/chat/streaming/test_llm_bundle.py @@ -32,7 +32,9 @@ def _patch_common_bundle_dependencies(monkeypatch: pytest.MonkeyPatch): _CapturedChatLiteLLM.calls = [] - async def _fake_search_space(_session: Any, _search_space_id: int) -> SimpleNamespace: + async def _fake_search_space( + _session: Any, _search_space_id: int + ) -> SimpleNamespace: return SimpleNamespace(id=42, user_id="user-1") monkeypatch.setattr(llm_bundle, "_load_search_space", _fake_search_space) diff --git a/surfsense_backend/tests/unit/test_pat_fail_closed_static.py b/surfsense_backend/tests/unit/test_pat_fail_closed_static.py index 01ecd918f..88b8f9151 100644 --- a/surfsense_backend/tests/unit/test_pat_fail_closed_static.py +++ b/surfsense_backend/tests/unit/test_pat_fail_closed_static.py @@ -32,11 +32,7 @@ CONNECTOR_LISTERS = [ def _python_files() -> list[Path]: - return [ - path - for path in APP_ROOT.rglob("*.py") - if "__pycache__" not in path.parts - ] + return [path for path in APP_ROOT.rglob("*.py") if "__pycache__" not in path.parts] def test_current_active_user_is_removed_from_app_tree() -> None: diff --git a/surfsense_backend/tests/unit/utils/test_content_utils.py b/surfsense_backend/tests/unit/utils/test_content_utils.py index db898f294..a8ad57714 100644 --- a/surfsense_backend/tests/unit/utils/test_content_utils.py +++ b/surfsense_backend/tests/unit/utils/test_content_utils.py @@ -290,4 +290,4 @@ class TestExtractTextContent: def test_boolean_returns_empty_string(self): from app.utils.content_utils import extract_text_content - assert extract_text_content(True) == "" \ No newline at end of file + assert extract_text_content(True) == "" diff --git a/surfsense_web/app/api/zero/query/route.ts b/surfsense_web/app/api/zero/query/route.ts index d765a3202..736647c96 100644 --- a/surfsense_web/app/api/zero/query/route.ts +++ b/surfsense_web/app/api/zero/query/route.ts @@ -23,8 +23,7 @@ function validateZeroCacheRequest(request: Request): NextResponse | null { async function authenticateRequest( request: Request ): Promise< - | { ctx: Exclude; error?: never } - | { ctx?: never; error: NextResponse } + { ctx: Exclude; error?: never } | { ctx?: never; error: NextResponse } > { const authHeader = request.headers.get("Authorization"); const cookieHeader = request.headers.get("Cookie"); diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx index 5ac7e83b8..e63812423 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx @@ -105,9 +105,7 @@ export function ApiKeyContent() {

{token.prefix}...

Expires: {expiresAt ? expiresAt.toLocaleDateString() : "Never"} ยท Last used:{" "} - {token.last_used_at - ? new Date(token.last_used_at).toLocaleString() - : "Never"} + {token.last_used_at ? new Date(token.last_used_at).toLocaleString() : "Never"}

@@ -131,4 +123,4 @@ function MermaidDiagramComponent({ ); } -export const MermaidDiagram = memo(MermaidDiagramComponent); \ No newline at end of file +export const MermaidDiagram = memo(MermaidDiagramComponent); diff --git a/surfsense_web/components/new-chat/image-model-selector.tsx b/surfsense_web/components/new-chat/image-model-selector.tsx index 5cd898afc..11de54f77 100644 --- a/surfsense_web/components/new-chat/image-model-selector.tsx +++ b/surfsense_web/components/new-chat/image-model-selector.tsx @@ -274,9 +274,9 @@ export function ImageModelSelector({ )} {showIconOnlyTrigger ? null : ( - - {selected ? modelName(selected) : "Auto"} - + + {selected ? modelName(selected) : "Auto"} + )} diff --git a/surfsense_web/components/public-chat/public-chat-footer.tsx b/surfsense_web/components/public-chat/public-chat-footer.tsx index 038ba37d5..5c775a2a1 100644 --- a/surfsense_web/components/public-chat/public-chat-footer.tsx +++ b/surfsense_web/components/public-chat/public-chat-footer.tsx @@ -43,12 +43,7 @@ export function PublicChatFooter({ shareToken }: PublicChatFooterProps) { const action = new URLSearchParams(window.location.search).get("action"); // Only auto-clone once, if authenticated and action=clone is present - if ( - action === "clone" && - session.authenticated && - !hasAutoCloned.current && - !isCloning - ) { + if (action === "clone" && session.authenticated && !hasAutoCloned.current && !isCloning) { hasAutoCloned.current = true; triggerClone(); } diff --git a/surfsense_web/components/settings/general-settings-manager.tsx b/surfsense_web/components/settings/general-settings-manager.tsx index 9205f3bbe..113d9d754 100644 --- a/surfsense_web/components/settings/general-settings-manager.tsx +++ b/surfsense_web/components/settings/general-settings-manager.tsx @@ -210,8 +210,8 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager

- Allow personal access tokens to use this search space. Web and desktop sessions are - not affected. + Allow personal access tokens to use this search space. Web and desktop sessions are not + affected.

(query: T, c if (allowedSpaceIds.length === 1) { return query.where("searchSpaceId", allowedSpaceIds[0]) as T; } - return query.where(({ cmp, or }: { cmp: (column: string, value: number) => unknown; or: (...args: unknown[]) => unknown }) => - or(...allowedSpaceIds.map((id) => cmp("searchSpaceId", id))) + return query.where( + ({ + cmp, + or, + }: { + cmp: (column: string, value: number) => unknown; + or: (...args: unknown[]) => unknown; + }) => or(...allowedSpaceIds.map((id) => cmp("searchSpaceId", id))) ) as T; } diff --git a/surfsense_web/zero/queries/automations.ts b/surfsense_web/zero/queries/automations.ts index 5d79a9f09..4f3bd451c 100644 --- a/surfsense_web/zero/queries/automations.ts +++ b/surfsense_web/zero/queries/automations.ts @@ -7,10 +7,12 @@ import { constrainToAllowedSpaces } from "./authz"; // authorizes via `automation_id -> search_space`. No search_space_id on the // table by design. export const automationRunQueries = { - byAutomation: defineQuery(z.object({ automationId: z.number() }), ({ args: { automationId }, ctx }) => - zql.automation_runs - .where("automationId", automationId) - .whereExists("automation", (q) => constrainToAllowedSpaces(q, ctx)) - .orderBy("createdAt", "desc") + byAutomation: defineQuery( + z.object({ automationId: z.number() }), + ({ args: { automationId }, ctx }) => + zql.automation_runs + .where("automationId", automationId) + .whereExists("automation", (q) => constrainToAllowedSpaces(q, ctx)) + .orderBy("createdAt", "desc") ), }; diff --git a/surfsense_web/zero/queries/documents.ts b/surfsense_web/zero/queries/documents.ts index a38fedc33..4e81a0491 100644 --- a/surfsense_web/zero/queries/documents.ts +++ b/surfsense_web/zero/queries/documents.ts @@ -4,17 +4,23 @@ import { zql } from "../schema/index"; import { canReadSpace, constrainToAllowedSpaces, denySpace } from "./authz"; export const documentQueries = { - bySpace: defineQuery(z.object({ searchSpaceId: z.number() }), ({ args: { searchSpaceId }, ctx }) => { - const query = zql.documents.where("searchSpaceId", searchSpaceId); - if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("createdAt", "desc"); - return constrainToAllowedSpaces(query, ctx).orderBy("createdAt", "desc"); - }), + bySpace: defineQuery( + z.object({ searchSpaceId: z.number() }), + ({ args: { searchSpaceId }, ctx }) => { + const query = zql.documents.where("searchSpaceId", searchSpaceId); + if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("createdAt", "desc"); + return constrainToAllowedSpaces(query, ctx).orderBy("createdAt", "desc"); + } + ), }; export const connectorQueries = { - bySpace: defineQuery(z.object({ searchSpaceId: z.number() }), ({ args: { searchSpaceId }, ctx }) => { - const query = zql.search_source_connectors.where("searchSpaceId", searchSpaceId); - if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("createdAt", "desc"); - return constrainToAllowedSpaces(query, ctx).orderBy("createdAt", "desc"); - }), + bySpace: defineQuery( + z.object({ searchSpaceId: z.number() }), + ({ args: { searchSpaceId }, ctx }) => { + const query = zql.search_source_connectors.where("searchSpaceId", searchSpaceId); + if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("createdAt", "desc"); + return constrainToAllowedSpaces(query, ctx).orderBy("createdAt", "desc"); + } + ), }; diff --git a/surfsense_web/zero/queries/folders.ts b/surfsense_web/zero/queries/folders.ts index 0d0868889..5cf712cda 100644 --- a/surfsense_web/zero/queries/folders.ts +++ b/surfsense_web/zero/queries/folders.ts @@ -4,9 +4,12 @@ import { zql } from "../schema/index"; import { canReadSpace, constrainToAllowedSpaces, denySpace } from "./authz"; export const folderQueries = { - bySpace: defineQuery(z.object({ searchSpaceId: z.number() }), ({ args: { searchSpaceId }, ctx }) => { - const query = zql.folders.where("searchSpaceId", searchSpaceId); - if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("position", "asc"); - return constrainToAllowedSpaces(query, ctx).orderBy("position", "asc"); - }), + bySpace: defineQuery( + z.object({ searchSpaceId: z.number() }), + ({ args: { searchSpaceId }, ctx }) => { + const query = zql.folders.where("searchSpaceId", searchSpaceId); + if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("position", "asc"); + return constrainToAllowedSpaces(query, ctx).orderBy("position", "asc"); + } + ), }; diff --git a/surfsense_web/zero/queries/podcasts.ts b/surfsense_web/zero/queries/podcasts.ts index 3dfefc9ab..0384c260a 100644 --- a/surfsense_web/zero/queries/podcasts.ts +++ b/surfsense_web/zero/queries/podcasts.ts @@ -4,11 +4,14 @@ import { zql } from "../schema/index"; import { canReadSpace, constrainToAllowedSpaces, denySpace } from "./authz"; export const podcastQueries = { - bySpace: defineQuery(z.object({ searchSpaceId: z.number() }), ({ args: { searchSpaceId }, ctx }) => { - const query = zql.podcasts.where("searchSpaceId", searchSpaceId); - if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("createdAt", "desc"); - return constrainToAllowedSpaces(query, ctx).orderBy("createdAt", "desc"); - }), + bySpace: defineQuery( + z.object({ searchSpaceId: z.number() }), + ({ args: { searchSpaceId }, ctx }) => { + const query = zql.podcasts.where("searchSpaceId", searchSpaceId); + if (!canReadSpace(ctx, searchSpaceId)) return denySpace(query).orderBy("createdAt", "desc"); + return constrainToAllowedSpaces(query, ctx).orderBy("createdAt", "desc"); + } + ), byId: defineQuery(z.object({ podcastId: z.number() }), ({ args: { podcastId }, ctx }) => constrainToAllowedSpaces(zql.podcasts.where("id", podcastId), ctx).one() ),