From fede7413fb7212096e92b6c84d5a620d8a2d4dce Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Wed, 14 Jan 2026 02:33:43 +0530
Subject: [PATCH] fix: fixed notifications table and Electric SQL replication
setup. Removed redirects for upload button
---
.../versions/62_add_notifications_table.py | 54 +++++++++++++++++--
surfsense_backend/app/db.py | 54 -------------------
.../documents/(manage)/page.tsx | 2 +-
.../assistant-ui/document-upload-popup.tsx | 3 --
.../notifications/NotificationPopup.tsx | 4 +-
.../components/sources/DocumentUploadTab.tsx | 4 +-
6 files changed, 54 insertions(+), 67 deletions(-)
diff --git a/surfsense_backend/alembic/versions/62_add_notifications_table.py b/surfsense_backend/alembic/versions/62_add_notifications_table.py
index 5af37d2f8..cf3407415 100644
--- a/surfsense_backend/alembic/versions/62_add_notifications_table.py
+++ b/surfsense_backend/alembic/versions/62_add_notifications_table.py
@@ -1,10 +1,11 @@
-"""Add notifications table
+"""Add notifications table and Electric SQL replication
Revision ID: 62
Revises: 61
-Note: Electric SQL replication setup (REPLICA IDENTITY FULL and publication)
-is handled in app/db.py setup_electric_replication() which runs on app startup.
+Creates notifications table and sets up Electric SQL replication
+(REPLICA IDENTITY FULL and publication) for notifications,
+search_source_connectors, and documents tables.
"""
from collections.abc import Sequence
@@ -19,7 +20,7 @@ depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
- """Upgrade schema - add notifications table."""
+ """Upgrade schema - add notifications table and Electric SQL replication."""
# Create notifications table
op.execute(
"""
@@ -44,6 +45,51 @@ def upgrade() -> None:
op.create_index("ix_notifications_created_at", "notifications", ["created_at"])
op.create_index("ix_notifications_user_read", "notifications", ["user_id", "read"])
+ # Set up Electric SQL replication for real-time sync tables
+ # Set REPLICA IDENTITY FULL (required by Electric SQL for replication)
+ # This logs full row data for UPDATE/DELETE operations in the WAL
+ op.execute("ALTER TABLE notifications REPLICA IDENTITY FULL;")
+ op.execute("ALTER TABLE search_source_connectors REPLICA IDENTITY FULL;")
+ op.execute("ALTER TABLE documents REPLICA IDENTITY FULL;")
+
+ # Add tables to Electric SQL publication for replication if publication exists
+ op.execute(
+ """
+ DO $$
+ BEGIN
+ IF EXISTS (SELECT 1 FROM pg_publication WHERE pubname = 'electric_publication_default') THEN
+ -- Add notifications if not already added
+ IF NOT EXISTS (
+ SELECT 1 FROM pg_publication_tables
+ WHERE pubname = 'electric_publication_default'
+ AND tablename = 'notifications'
+ ) THEN
+ ALTER PUBLICATION electric_publication_default ADD TABLE notifications;
+ END IF;
+
+ -- Add search_source_connectors if not already added
+ IF NOT EXISTS (
+ SELECT 1 FROM pg_publication_tables
+ WHERE pubname = 'electric_publication_default'
+ AND tablename = 'search_source_connectors'
+ ) THEN
+ ALTER PUBLICATION electric_publication_default ADD TABLE search_source_connectors;
+ END IF;
+
+ -- Add documents if not already added
+ IF NOT EXISTS (
+ SELECT 1 FROM pg_publication_tables
+ WHERE pubname = 'electric_publication_default'
+ AND tablename = 'documents'
+ ) THEN
+ ALTER PUBLICATION electric_publication_default ADD TABLE documents;
+ END IF;
+ END IF;
+ END
+ $$;
+ """
+ )
+
def downgrade() -> None:
"""Downgrade schema - remove notifications table."""
diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py
index 7af55eaa3..699da9c38 100644
--- a/surfsense_backend/app/db.py
+++ b/surfsense_backend/app/db.py
@@ -988,60 +988,6 @@ async def create_db_and_tables():
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS vector"))
await conn.run_sync(Base.metadata.create_all)
await setup_indexes()
- await setup_electric_replication()
-
-
-async def setup_electric_replication():
- """Set up Electric SQL replication for real-time sync tables."""
- async with engine.begin() as conn:
- # Set REPLICA IDENTITY FULL (required by Electric SQL for replication)
- # This logs full row data for UPDATE/DELETE operations in the WAL
- await conn.execute(text("ALTER TABLE notifications REPLICA IDENTITY FULL;"))
- await conn.execute(
- text("ALTER TABLE search_source_connectors REPLICA IDENTITY FULL;")
- )
- await conn.execute(text("ALTER TABLE documents REPLICA IDENTITY FULL;"))
-
- # Add tables to Electric SQL publication for replication
- # Only add if publication exists and table not already in it
- await conn.execute(
- text(
- """
- DO $$
- BEGIN
- IF EXISTS (SELECT 1 FROM pg_publication WHERE pubname = 'electric_publication_default') THEN
- -- Add notifications if not already added
- IF NOT EXISTS (
- SELECT 1 FROM pg_publication_tables
- WHERE pubname = 'electric_publication_default'
- AND tablename = 'notifications'
- ) THEN
- ALTER PUBLICATION electric_publication_default ADD TABLE notifications;
- END IF;
-
- -- Add search_source_connectors if not already added
- IF NOT EXISTS (
- SELECT 1 FROM pg_publication_tables
- WHERE pubname = 'electric_publication_default'
- AND tablename = 'search_source_connectors'
- ) THEN
- ALTER PUBLICATION electric_publication_default ADD TABLE search_source_connectors;
- END IF;
-
- -- Add documents if not already added
- IF NOT EXISTS (
- SELECT 1 FROM pg_publication_tables
- WHERE pubname = 'electric_publication_default'
- AND tablename = 'documents'
- ) THEN
- ALTER PUBLICATION electric_publication_default ADD TABLE documents;
- END IF;
- END IF;
- END
- $$;
- """
- )
- )
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
index 59593ed8e..01891f05b 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
@@ -148,7 +148,7 @@ export default function DocumentsTable() {
document_type: "SURFSENSE_DOCS",
document_metadata: { source: doc.source },
content: doc.content,
- created_at: doc.created_at || doc.updated_at || new Date().toISOString(),
+ created_at: new Date().toISOString(),
search_space_id: -1, // Special value for global docs
}));
}, [surfsenseDocsResponse]);
diff --git a/surfsense_web/components/assistant-ui/document-upload-popup.tsx b/surfsense_web/components/assistant-ui/document-upload-popup.tsx
index 29f633ebf..58fcc12a0 100644
--- a/surfsense_web/components/assistant-ui/document-upload-popup.tsx
+++ b/surfsense_web/components/assistant-ui/document-upload-popup.tsx
@@ -2,7 +2,6 @@
import { useAtomValue } from "jotai";
import { Upload } from "lucide-react";
-import { useRouter } from "next/navigation";
import {
createContext,
type FC,
@@ -85,13 +84,11 @@ const DocumentUploadPopupContent: FC<{
onOpenChange: (open: boolean) => void;
}> = ({ isOpen, onOpenChange }) => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
- const router = useRouter();
if (!searchSpaceId) return null;
const handleSuccess = () => {
onOpenChange(false);
- router.push(`/dashboard/${searchSpaceId}/documents`);
};
return (
diff --git a/surfsense_web/components/notifications/NotificationPopup.tsx b/surfsense_web/components/notifications/NotificationPopup.tsx
index f194aa394..3355b62e6 100644
--- a/surfsense_web/components/notifications/NotificationPopup.tsx
+++ b/surfsense_web/components/notifications/NotificationPopup.tsx
@@ -1,6 +1,6 @@
"use client";
-import { Bell, Check, CheckCheck, Loader2, AlertCircle, CheckCircle2 } from "lucide-react";
+import { Bell, CheckCheck, Loader2, AlertCircle, CheckCircle2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
@@ -44,7 +44,7 @@ export function NotificationPopup({
switch (status) {
case "in_progress":
- return ;
+ return ;
case "completed":
return ;
case "failed":
diff --git a/surfsense_web/components/sources/DocumentUploadTab.tsx b/surfsense_web/components/sources/DocumentUploadTab.tsx
index 0b7f7b51f..a261fc949 100644
--- a/surfsense_web/components/sources/DocumentUploadTab.tsx
+++ b/surfsense_web/components/sources/DocumentUploadTab.tsx
@@ -3,7 +3,6 @@
import { useAtom } from "jotai";
import { CheckCircle2, FileType, Info, Loader2, Tag, Upload, X } from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
-import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useCallback, useMemo, useRef, useState } from "react";
import { useDropzone } from "react-dropzone";
@@ -116,7 +115,6 @@ export function DocumentUploadTab({
onAccordionStateChange,
}: DocumentUploadTabProps) {
const t = useTranslations("upload_documents");
- const router = useRouter();
const [files, setFiles] = useState([]);
const [uploadProgress, setUploadProgress] = useState(0);
const [accordionValue, setAccordionValue] = useState("");
@@ -185,7 +183,7 @@ export function DocumentUploadTab({
setUploadProgress(100);
trackDocumentUploadSuccess(Number(searchSpaceId), files.length);
toast(t("upload_initiated"), { description: t("upload_initiated_desc") });
- onSuccess?.() || router.push(`/dashboard/${searchSpaceId}/documents`);
+ onSuccess?.();
},
onError: (error: unknown) => {
clearInterval(progressInterval);