chore: fix linting

This commit is contained in:
Anish Sarkar 2026-06-25 04:31:22 +05:30
parent 7d4c994900
commit d6bffa6f07
61 changed files with 167 additions and 218 deletions

View file

@ -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")

View file

@ -7,6 +7,7 @@ Revises: 168
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "169"

View file

@ -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.

View file

@ -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"]

View file

@ -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",

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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:

View file

@ -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:

View file

@ -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(

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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:

View file

@ -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,

View file

@ -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:

View file

@ -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(

View file

@ -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.

View file

@ -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}

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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 (

View file

@ -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)

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -242,9 +242,9 @@ __all__ = [
"SearchSourceConnectorCreate",
"SearchSourceConnectorRead",
"SearchSourceConnectorUpdate",
"SearchSpaceApiAccessUpdate",
# Search space schemas
"SearchSpaceBase",
"SearchSpaceApiAccessUpdate",
"SearchSpaceCreate",
"SearchSpaceRead",
"SearchSpaceUpdate",

View file

@ -401,4 +401,3 @@ async def require_session_context(
detail="This action requires an interactive session",
)
return auth

View file

@ -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,

View file

@ -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():

View file

@ -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(

View file

@ -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

View file

@ -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)

View file

@ -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:

View file

@ -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) == ""
assert extract_text_content(True) == ""

View file

@ -23,8 +23,7 @@ function validateZeroCacheRequest(request: Request): NextResponse | null {
async function authenticateRequest(
request: Request
): Promise<
| { ctx: Exclude<Context, undefined>; error?: never }
| { ctx?: never; error: NextResponse }
{ ctx: Exclude<Context, undefined>; error?: never } | { ctx?: never; error: NextResponse }
> {
const authHeader = request.headers.get("Authorization");
const cookieHeader = request.headers.get("Cookie");

View file

@ -105,9 +105,7 @@ export function ApiKeyContent() {
<p className="font-mono text-xs text-muted-foreground">{token.prefix}...</p>
<p className="text-xs text-muted-foreground">
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"}
</p>
</div>
<Button
@ -175,8 +173,7 @@ export function ApiKeyContent() {
<DialogHeader>
<DialogTitle>Copy your token now</DialogTitle>
<DialogDescription>
This token is shown only once. Store it somewhere secure before closing this
dialog.
This token is shown only once. Store it somewhere secure before closing this dialog.
</DialogDescription>
</DialogHeader>
<div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 p-2">

View file

@ -9,13 +9,7 @@ import { useEffect, useState } from "react";
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { CreateSearchSpaceDialog } from "@/components/layout";
import { Button } from "@/components/ui/button";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { useGlobalLoadingEffect } from "@/hooks/use-global-loading";
function ErrorScreen({ message }: { message: string }) {

View file

@ -18,13 +18,10 @@ import {
import { ThemeProvider } from "@/components/theme/theme-provider";
import { Toaster } from "@/components/ui/sonner";
import { LocaleProvider } from "@/contexts/LocaleContext";
import { BUILD_TIME_AUTH_TYPE } from "@/lib/env-config";
import { PlatformProvider } from "@/contexts/platform-context";
import { BUILD_TIME_AUTH_TYPE } from "@/lib/env-config";
import { ReactQueryClientProvider } from "@/lib/query-client/query-client.provider";
import {
getRuntimeAuthInitScript,
resolveRuntimeAuthUiMode,
} from "@/lib/runtime-auth-config";
import { getRuntimeAuthInitScript, resolveRuntimeAuthUiMode } from "@/lib/runtime-auth-config";
import { cn } from "@/lib/utils";
const roboto = Roboto({

View file

@ -100,9 +100,7 @@ export const ObsidianConnectForm: FC<ConnectFormProps> = ({ onBack }) => {
<div className="flex size-7 items-center justify-center rounded-md border border-slate-400/30 text-xs font-medium">
2
</div>
<h3 className="text-sm font-medium sm:text-base">
Create a personal access token
</h3>
<h3 className="text-sm font-medium sm:text-base">Create a personal access token</h3>
</header>
<p className="mb-3 text-[11px] text-muted-foreground sm:text-xs">
Create a token and paste it into the plugin's{" "}

View file

@ -28,11 +28,7 @@ function initializeMermaid() {
mermaidInitialized = true;
}
function MermaidDiagramComponent({
source,
isDarkMode,
fallback,
}: MermaidDiagramProps) {
function MermaidDiagramComponent({ source, isDarkMode, fallback }: MermaidDiagramProps) {
const id = useId();
const [svg, setSvg] = useState<string | null>(null);
const [hasError, setHasError] = useState(false);
@ -107,11 +103,7 @@ function MermaidDiagramComponent({
aria-label={hasCopied ? "Copied Mermaid source" : "Copy Mermaid source"}
>
<span className="sr-only">Copy Source</span>
{hasCopied ? (
<CheckIcon className="!size-3" />
) : (
<CopyIcon className="!size-3" />
)}
{hasCopied ? <CheckIcon className="!size-3" /> : <CopyIcon className="!size-3" />}
</Button>
</div>
@ -131,4 +123,4 @@ function MermaidDiagramComponent({
);
}
export const MermaidDiagram = memo(MermaidDiagramComponent);
export const MermaidDiagram = memo(MermaidDiagramComponent);

View file

@ -274,9 +274,9 @@ export function ImageModelSelector({
<ImagePlus className="size-4 shrink-0" />
)}
{showIconOnlyTrigger ? null : (
<span className="min-w-0 flex-1 truncate text-sm">
{selected ? modelName(selected) : "Auto"}
</span>
<span className="min-w-0 flex-1 truncate text-sm">
{selected ? modelName(selected) : "Auto"}
</span>
)}
<ChevronDown className="h-3.5 w-3.5 shrink-0" />
</Button>

View file

@ -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();
}

View file

@ -210,8 +210,8 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager
<div className="space-y-1">
<Label htmlFor="api-access-enabled">Programmatic API access</Label>
<p className="text-xs text-muted-foreground">
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.
</p>
</div>
<Switch

View file

@ -3,8 +3,8 @@
import { useCallback, useEffect, useState } from "react";
import { toast } from "sonner";
import type {
CreatePatRequest,
CreatedPat,
CreatePatRequest,
PersonalAccessToken,
} from "@/contracts/types/pat.types";
import { patsApiService } from "@/lib/apis/pats-api.service";

View file

@ -50,11 +50,7 @@ export type RequestOptions = {
};
class BaseApiService {
noAuthEndpoints: string[] = [
"/auth/jwt/login",
"/auth/register",
"/auth/jwt/refresh",
];
noAuthEndpoints: string[] = ["/auth/jwt/login", "/auth/register", "/auth/jwt/refresh"];
// Prefixes that don't require auth (checked with startsWith)
noAuthPrefixes: string[] = ["/api/v1/public/"];

View file

@ -13,11 +13,11 @@ import {
getSearchSpacesRequest,
getSearchSpacesResponse,
leaveSearchSpaceResponse,
type UpdateSearchSpaceRequest,
type UpdateSearchSpaceApiAccessRequest,
updateSearchSpaceRequest,
type UpdateSearchSpaceRequest,
updateSearchSpaceApiAccessRequest,
updateSearchSpaceApiAccessResponse,
updateSearchSpaceRequest,
updateSearchSpaceResponse,
} from "@/contracts/types/search-space.types";
import { ValidationError } from "../error";

View file

@ -4,11 +4,7 @@ let desktopAccessToken: string | null = null;
let didSubscribeToDesktopAuth = false;
function subscribeToDesktopAuth(): void {
if (
didSubscribeToDesktopAuth ||
typeof window === "undefined" ||
!window.electronAPI
) {
if (didSubscribeToDesktopAuth || typeof window === "undefined" || !window.electronAPI) {
return;
}
didSubscribeToDesktopAuth = true;

View file

@ -1,9 +1,6 @@
import { NextResponse, type NextRequest } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
import { BUILD_TIME_AUTH_TYPE } from "@/lib/env-config";
import {
RUNTIME_AUTH_TYPE_COOKIE_NAME,
resolveRuntimeAuthUiMode,
} from "@/lib/runtime-auth-config";
import { RUNTIME_AUTH_TYPE_COOKIE_NAME, resolveRuntimeAuthUiMode } from "@/lib/runtime-auth-config";
export function proxy(request: NextRequest) {
const response = NextResponse.next();

View file

@ -20,7 +20,13 @@ export function constrainToAllowedSpaces<T extends SpaceScopedQuery>(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;
}

View file

@ -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")
),
};

View file

@ -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");
}
),
};

View file

@ -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");
}
),
};

View file

@ -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()
),