mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
feat: implement sync notifications for Obsidian plugin
- Added functionality to create and update notifications during the Obsidian sync process. - Improved handling of sync completion and failure notifications. - Updated connector naming convention in various locations for consistency.
This commit is contained in:
parent
3b7f27cff9
commit
4a75603d4f
4 changed files with 134 additions and 8 deletions
|
|
@ -41,6 +41,7 @@ from app.schemas.obsidian_plugin import (
|
|||
SyncAckItem,
|
||||
SyncBatchRequest,
|
||||
)
|
||||
from app.services.notification_service import NotificationService
|
||||
from app.services.obsidian_plugin_indexer import (
|
||||
delete_note,
|
||||
get_manifest,
|
||||
|
|
@ -68,6 +69,103 @@ def _build_handshake() -> dict[str, object]:
|
|||
return {"capabilities": list(OBSIDIAN_CAPABILITIES)}
|
||||
|
||||
|
||||
def _connector_type_value(connector: SearchSourceConnector) -> str:
|
||||
connector_type = connector.connector_type
|
||||
if hasattr(connector_type, "value"):
|
||||
return str(connector_type.value)
|
||||
return str(connector_type)
|
||||
|
||||
|
||||
async def _start_obsidian_sync_notification(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
user: User,
|
||||
connector: SearchSourceConnector,
|
||||
total_count: int,
|
||||
):
|
||||
"""Create/update the rolling inbox item for Obsidian plugin sync.
|
||||
|
||||
Obsidian sync is continuous and batched, so we keep one stable
|
||||
operation_id per connector instead of creating a new notification per batch.
|
||||
"""
|
||||
handler = NotificationService.connector_indexing
|
||||
operation_id = f"obsidian_sync_connector_{connector.id}"
|
||||
connector_name = connector.name or "Obsidian"
|
||||
notification = await handler.find_or_create_notification(
|
||||
session=session,
|
||||
user_id=user.id,
|
||||
operation_id=operation_id,
|
||||
title=f"Syncing: {connector_name}",
|
||||
message="Syncing from Obsidian plugin",
|
||||
search_space_id=connector.search_space_id,
|
||||
initial_metadata={
|
||||
"connector_id": connector.id,
|
||||
"connector_name": connector_name,
|
||||
"connector_type": _connector_type_value(connector),
|
||||
"sync_stage": "processing",
|
||||
"indexed_count": 0,
|
||||
"failed_count": 0,
|
||||
"total_count": total_count,
|
||||
"source": "obsidian_plugin",
|
||||
},
|
||||
)
|
||||
return await handler.update_notification(
|
||||
session=session,
|
||||
notification=notification,
|
||||
status="in_progress",
|
||||
metadata_updates={
|
||||
"sync_stage": "processing",
|
||||
"total_count": total_count,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def _finish_obsidian_sync_notification(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
notification,
|
||||
indexed: int,
|
||||
failed: int,
|
||||
):
|
||||
"""Mark the rolling Obsidian sync inbox item complete or failed."""
|
||||
handler = NotificationService.connector_indexing
|
||||
connector_name = notification.notification_metadata.get("connector_name", "Obsidian")
|
||||
if failed > 0 and indexed == 0:
|
||||
title = f"Failed: {connector_name}"
|
||||
message = (
|
||||
f"Sync failed: {failed} file(s) failed"
|
||||
if failed > 1
|
||||
else "Sync failed: 1 file failed"
|
||||
)
|
||||
status_value = "failed"
|
||||
stage = "failed"
|
||||
else:
|
||||
title = f"Ready: {connector_name}"
|
||||
if failed > 0:
|
||||
message = f"Partially synced: {indexed} file(s) synced, {failed} failed."
|
||||
elif indexed == 0:
|
||||
message = "Already up to date!"
|
||||
elif indexed == 1:
|
||||
message = "Now searchable! 1 file synced."
|
||||
else:
|
||||
message = f"Now searchable! {indexed} files synced."
|
||||
status_value = "completed"
|
||||
stage = "completed"
|
||||
|
||||
await handler.update_notification(
|
||||
session=session,
|
||||
notification=notification,
|
||||
title=title,
|
||||
message=message,
|
||||
status=status_value,
|
||||
metadata_updates={
|
||||
"indexed_count": indexed,
|
||||
"failed_count": failed,
|
||||
"sync_stage": stage,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def _resolve_vault_connector(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
|
|
@ -188,7 +286,7 @@ def _build_config(
|
|||
|
||||
|
||||
def _display_name(vault_name: str) -> str:
|
||||
return f"Obsidian \u2014 {vault_name}"
|
||||
return f"Obsidian - {vault_name}"
|
||||
|
||||
|
||||
@router.post("/connect", response_model=ConnectResponse)
|
||||
|
|
@ -335,6 +433,18 @@ async def obsidian_sync(
|
|||
connector = await _resolve_vault_connector(
|
||||
session, user=user, vault_id=payload.vault_id
|
||||
)
|
||||
notification = None
|
||||
try:
|
||||
notification = await _start_obsidian_sync_notification(
|
||||
session, user=user, connector=connector, total_count=len(payload.notes)
|
||||
)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"obsidian sync notification start failed connector=%s user=%s",
|
||||
connector.id,
|
||||
user.id,
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
items: list[SyncAckItem] = []
|
||||
indexed = 0
|
||||
|
|
@ -362,6 +472,22 @@ async def obsidian_sync(
|
|||
SyncAckItem(path=note.path, status="error", error=str(exc)[:300])
|
||||
)
|
||||
|
||||
if notification is not None:
|
||||
try:
|
||||
await _finish_obsidian_sync_notification(
|
||||
session,
|
||||
notification=notification,
|
||||
indexed=indexed,
|
||||
failed=failed,
|
||||
)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"obsidian sync notification finish failed connector=%s user=%s",
|
||||
connector.id,
|
||||
user.id,
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
return SyncAck(
|
||||
vault_id=payload.vault_id,
|
||||
indexed=indexed,
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ class TestConnectRace:
|
|||
async with AsyncSession(async_engine) as s:
|
||||
s.add(
|
||||
SearchSourceConnector(
|
||||
name="Obsidian \u2014 First",
|
||||
name="Obsidian - First",
|
||||
connector_type=SearchSourceConnectorType.OBSIDIAN_CONNECTOR,
|
||||
is_indexable=False,
|
||||
config={
|
||||
|
|
@ -202,7 +202,7 @@ class TestConnectRace:
|
|||
async with AsyncSession(async_engine) as s:
|
||||
s.add(
|
||||
SearchSourceConnector(
|
||||
name="Obsidian \u2014 Second",
|
||||
name="Obsidian - Second",
|
||||
connector_type=SearchSourceConnectorType.OBSIDIAN_CONNECTOR,
|
||||
is_indexable=False,
|
||||
config={
|
||||
|
|
@ -228,7 +228,7 @@ class TestConnectRace:
|
|||
async with AsyncSession(async_engine) as s:
|
||||
s.add(
|
||||
SearchSourceConnector(
|
||||
name="Obsidian \u2014 Desktop",
|
||||
name="Obsidian - Desktop",
|
||||
connector_type=SearchSourceConnectorType.OBSIDIAN_CONNECTOR,
|
||||
is_indexable=False,
|
||||
config={
|
||||
|
|
@ -247,7 +247,7 @@ class TestConnectRace:
|
|||
async with AsyncSession(async_engine) as s:
|
||||
s.add(
|
||||
SearchSourceConnector(
|
||||
name="Obsidian \u2014 Mobile",
|
||||
name="Obsidian - Mobile",
|
||||
connector_type=SearchSourceConnectorType.OBSIDIAN_CONNECTOR,
|
||||
is_indexable=False,
|
||||
config={
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ export const OTHER_CONNECTORS = [
|
|||
{
|
||||
id: "obsidian-connector",
|
||||
title: "Obsidian",
|
||||
description: "Sync your Obsidian vault on desktop or mobile via the SurfSense plugin",
|
||||
description: "Sync your Obsidian vault on desktop or mobile",
|
||||
connectorType: EnumConnectorName.OBSIDIAN_CONNECTOR,
|
||||
},
|
||||
] as const;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ This works for cloud and self-hosted deployments, including desktop and mobile c
|
|||
4. Paste your SurfSense API token from the user settings section.
|
||||
5. Paste your Server URL in the plugin setting: either your SurfSense main domain (if `/api/v1` rewrites are enabled) or your direct backend URL.
|
||||
6. Choose the Search Space in the plugin, then the first sync should run automatically.
|
||||
7. Confirm the connector appears as **Obsidian — <vault>** in SurfSense.
|
||||
7. Confirm the connector appears as **Obsidian - <vault>** in SurfSense.
|
||||
|
||||
## Migrating from the legacy connector
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ If you previously used the legacy Obsidian connector architecture, migrate to th
|
|||
|
||||
1. Delete the old legacy Obsidian connector from SurfSense.
|
||||
2. Install and configure the SurfSense Obsidian plugin using the quick start above.
|
||||
3. Run the first plugin sync and verify the new **Obsidian — <vault>** connector is active.
|
||||
3. Run the first plugin sync and verify the new **Obsidian - <vault>** connector is active.
|
||||
|
||||
<Callout type="warn">
|
||||
Deleting the legacy connector also deletes all documents that were indexed by that connector. Always finish and verify plugin sync before deleting the old connector.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue