feat: New Chat, Search Chat and Master Slave DB Architecture for CRM and Oracle Canvas
This commit is contained in:
@@ -86,6 +86,8 @@ def _json_array(value: Any) -> list[Any]:
|
||||
def _json_safe(value: Any) -> Any:
|
||||
if isinstance(value, datetime):
|
||||
return value.isoformat()
|
||||
if isinstance(value, uuid.UUID):
|
||||
return str(value)
|
||||
if isinstance(value, dict):
|
||||
return {str(key): _json_safe(val) for key, val in value.items()}
|
||||
if isinstance(value, list):
|
||||
@@ -173,6 +175,54 @@ def _deserialize_page_row(row: Any, components: list[dict[str, Any]]) -> dict[st
|
||||
|
||||
|
||||
class CanvasService:
|
||||
async def list_pages(
|
||||
self,
|
||||
*,
|
||||
tenant_id: str,
|
||||
owner_id: str,
|
||||
search: str | None = None,
|
||||
limit: int = 50,
|
||||
) -> list[dict[str, Any]]:
|
||||
_ensure_ready()
|
||||
safe_limit = max(1, min(limit, 100))
|
||||
search_term = (search or "").strip().lower()
|
||||
if _is_demo():
|
||||
candidates = [
|
||||
page
|
||||
for page in _DEMO_PAGES.values()
|
||||
if page["tenantId"] == tenant_id and page["ownerId"] == owner_id
|
||||
]
|
||||
if search_term:
|
||||
candidates = [page for page in candidates if search_term in page.get("title", "").lower()]
|
||||
candidates.sort(key=lambda page: page.get("updatedAt", ""), reverse=True)
|
||||
return [{**page, "components": deepcopy(_DEMO_COMPONENTS.get(page["pageId"], []))} for page in candidates[:safe_limit]]
|
||||
|
||||
assert asyncpg is not None
|
||||
conn = await asyncpg.connect(_DB_URL)
|
||||
try:
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
SELECT *
|
||||
FROM oracle_canvas_pages
|
||||
WHERE tenant_id = $1
|
||||
AND owner_id = $2
|
||||
AND ($3 = '' OR lower(title) LIKE '%' || $3 || '%')
|
||||
ORDER BY updated_at DESC, created_at DESC
|
||||
LIMIT $4
|
||||
""",
|
||||
tenant_id,
|
||||
owner_id,
|
||||
search_term,
|
||||
safe_limit,
|
||||
)
|
||||
pages: list[dict[str, Any]] = []
|
||||
for row in rows:
|
||||
components = await self._pg_fetch_components(conn, _stringify(row["page_id"]), tenant_id)
|
||||
pages.append(_deserialize_page_row(row, components))
|
||||
return pages
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
async def create_page(
|
||||
self,
|
||||
*,
|
||||
@@ -310,6 +360,80 @@ class CanvasService:
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
async def update_page_title(
|
||||
self,
|
||||
*,
|
||||
page_id: str,
|
||||
tenant_id: str,
|
||||
owner_id: str,
|
||||
title: str,
|
||||
) -> dict[str, Any]:
|
||||
_ensure_ready()
|
||||
clean_title = (title or "").strip() or "Untitled Canvas"
|
||||
if _is_demo():
|
||||
page = _DEMO_PAGES.get(page_id)
|
||||
if not page or page["tenantId"] != tenant_id or page["ownerId"] != owner_id:
|
||||
raise ValueError(f"Page {page_id} not found for tenant {tenant_id}")
|
||||
page["title"] = clean_title
|
||||
page["updatedAt"] = _now()
|
||||
return {**page, "components": deepcopy(_DEMO_COMPONENTS.get(page_id, []))}
|
||||
|
||||
assert asyncpg is not None
|
||||
conn = await asyncpg.connect(_DB_URL)
|
||||
try:
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
UPDATE oracle_canvas_pages
|
||||
SET title = $4, updated_at = NOW()
|
||||
WHERE page_id = $1::uuid AND tenant_id = $2 AND owner_id = $3
|
||||
RETURNING *
|
||||
""",
|
||||
page_id,
|
||||
tenant_id,
|
||||
owner_id,
|
||||
clean_title,
|
||||
)
|
||||
if not row:
|
||||
raise ValueError(f"Page {page_id} not found for tenant {tenant_id}")
|
||||
components = await self._pg_fetch_components(conn, page_id, tenant_id)
|
||||
return _deserialize_page_row(row, components)
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
async def delete_page(
|
||||
self,
|
||||
*,
|
||||
page_id: str,
|
||||
tenant_id: str,
|
||||
owner_id: str,
|
||||
) -> None:
|
||||
_ensure_ready()
|
||||
if _is_demo():
|
||||
page = _DEMO_PAGES.get(page_id)
|
||||
if not page or page["tenantId"] != tenant_id or page["ownerId"] != owner_id:
|
||||
raise ValueError(f"Page {page_id} not found for tenant {tenant_id}")
|
||||
del _DEMO_PAGES[page_id]
|
||||
_DEMO_COMPONENTS.pop(page_id, None)
|
||||
_DEMO_REVISIONS.pop(page_id, None)
|
||||
return
|
||||
|
||||
assert asyncpg is not None
|
||||
conn = await asyncpg.connect(_DB_URL)
|
||||
try:
|
||||
result = await conn.execute(
|
||||
"""
|
||||
DELETE FROM oracle_canvas_pages
|
||||
WHERE page_id = $1::uuid AND tenant_id = $2 AND owner_id = $3
|
||||
""",
|
||||
page_id,
|
||||
tenant_id,
|
||||
owner_id,
|
||||
)
|
||||
if result.endswith("0"):
|
||||
raise ValueError(f"Page {page_id} not found for tenant {tenant_id}")
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
async def commit_revision(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user