feat: New Chat, Search Chat and Master Slave DB Architecture for CRM and Oracle Canvas

This commit is contained in:
Sagnik
2026-04-24 04:18:33 +05:30
parent f04571bd7b
commit eabecf7a25
7 changed files with 1117 additions and 520 deletions

View File

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