fix: fixed notifications table and Electric SQL replication setup. Removed redirects for upload button

This commit is contained in:
Anish Sarkar 2026-01-14 02:33:43 +05:30
parent 5bd6bd3d67
commit fede7413fb
6 changed files with 54 additions and 67 deletions

View file

@ -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."""

View file

@ -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]:

View file

@ -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]);

View file

@ -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 (

View file

@ -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 <Loader2 className="h-4 w-4 text-blue-500 animate-spin" />;
return <Loader2 className="h-4 w-4 text-white animate-spin" />;
case "completed":
return <CheckCircle2 className="h-4 w-4 text-green-500" />;
case "failed":

View file

@ -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<File[]>([]);
const [uploadProgress, setUploadProgress] = useState(0);
const [accordionValue, setAccordionValue] = useState<string>("");
@ -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);