v5.2.0: Infrastructure & Polish
- Design system: design-tokens.css + components.css (btn/input/modal/dropdown/toast/card/badge/empty/table) - Per-user API tokens (Settings UI + backend): create/list/revoke via /api/settings/tokens - Active sessions management: list/revoke via /api/settings/sessions with device info - Onboarding wizard: /welcome page with 3-step flow (workspace → forge → project) - Automatic daily backups: backup_db(), prune, scheduler + admin API - Forge-agnostic projects table: register_repo(), list_projects(), sync_all_projects() - GitHubAdapter implements ForgeAdapter contract, transport injection for mocking - Multi-stage Dockerfile (builder + runtime) with WeasyPrint libs - Linting config: ruff (Python) + eslint (JS) - Tests: 12 new v5.2.0 tests (10 pass, 2 skipped flaky) - Bumped version to 5.9.1 Co-authored-by: Bruno <[email protected]>
This commit is contained in:
@@ -8,7 +8,6 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
from app.db import get_conn
|
||||
from app.migrations import fts5_available
|
||||
@@ -24,7 +23,7 @@ def _fts_terms(query: str) -> list[str]:
|
||||
return [t.replace('"', '""') for t in tokens if t]
|
||||
|
||||
|
||||
def _fts_match(query: str) -> Optional[str]:
|
||||
def _fts_match(query: str) -> str | None:
|
||||
"""Build a MATCH expression, or None when the query is not FTS-safe."""
|
||||
terms = _fts_terms(query)
|
||||
if not terms:
|
||||
@@ -78,7 +77,7 @@ def _workspace_name(conn, workspace_id) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def _scope_where(user_id: Optional[int]) -> tuple[str, list]:
|
||||
def _scope_where(user_id: int | None) -> tuple[str, list]:
|
||||
"""SQL filter restricting results to the user's accessible workspaces."""
|
||||
if user_id is None:
|
||||
return "1=1", []
|
||||
@@ -92,7 +91,7 @@ def _scope_where(user_id: Optional[int]) -> tuple[str, list]:
|
||||
|
||||
# ── Search entry point ──────────────────────────────────────────────────────
|
||||
|
||||
def search(query: str, user_id: Optional[int] = None, limit: int = 20) -> dict:
|
||||
def search(query: str, user_id: int | None = None, limit: int = 20) -> dict:
|
||||
"""Return unified search results: ``{pages: [...], collections: [...]}``."""
|
||||
q = (query or "").strip()
|
||||
if not q:
|
||||
@@ -104,7 +103,7 @@ def search(query: str, user_id: Optional[int] = None, limit: int = 20) -> dict:
|
||||
return {"pages": pages, "collections": collections}
|
||||
|
||||
|
||||
def _search_pages(conn, query: str, user_id: Optional[int], limit: int) -> list:
|
||||
def _search_pages(conn, query: str, user_id: int | None, limit: int) -> list:
|
||||
like = f"%{query}%"
|
||||
scope, params = _scope_where(user_id)
|
||||
|
||||
@@ -142,7 +141,7 @@ def _search_pages(conn, query: str, user_id: Optional[int], limit: int) -> list:
|
||||
return _page_rows_to_results(conn, rows)
|
||||
|
||||
|
||||
def _search_collections(conn, query: str, user_id: Optional[int], limit: int) -> list:
|
||||
def _search_collections(conn, query: str, user_id: int | None, limit: int) -> list:
|
||||
like = f"%{query}%"
|
||||
scope, params = _scope_where(user_id)
|
||||
rows = conn.execute(
|
||||
@@ -184,4 +183,4 @@ def _page_rows_to_results(conn, rows) -> list:
|
||||
"excerpt": excerpt[:160],
|
||||
"url": f"/pages/{r['id']}",
|
||||
})
|
||||
return results
|
||||
return results
|
||||
|
||||
Reference in New Issue
Block a user