mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-15 18:25:18 +02:00
feat: enhance error handling in local folder indexing by adding rollback and refresh on IntegrityError
This commit is contained in:
parent
9a65163fe4
commit
e2ba509314
5 changed files with 27 additions and 10 deletions
|
|
@ -19,7 +19,7 @@ from datetime import UTC, datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.config import config
|
from app.config import config
|
||||||
|
|
@ -732,7 +732,12 @@ async def index_local_folder(
|
||||||
document.folder_id = folder_mapping.get(
|
document.folder_id = folder_mapping.get(
|
||||||
parent_dir, folder_mapping.get("")
|
parent_dir, folder_mapping.get("")
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
except IntegrityError:
|
||||||
|
await session.rollback()
|
||||||
|
for document in documents:
|
||||||
|
await session.refresh(document)
|
||||||
|
|
||||||
llm = await get_user_long_context_llm(session, user_id, search_space_id)
|
llm = await get_user_long_context_llm(session, user_id, search_space_id)
|
||||||
|
|
||||||
|
|
@ -905,10 +910,14 @@ async def _index_single_file(
|
||||||
# Assign folder_id before indexing so the doc appears in the
|
# Assign folder_id before indexing so the doc appears in the
|
||||||
# correct folder while still pending/processing.
|
# correct folder while still pending/processing.
|
||||||
if root_folder_id:
|
if root_folder_id:
|
||||||
|
try:
|
||||||
db_doc.folder_id = await _resolve_folder_for_file(
|
db_doc.folder_id = await _resolve_folder_for_file(
|
||||||
session, rel_path, root_folder_id, search_space_id, user_id
|
session, rel_path, root_folder_id, search_space_id, user_id
|
||||||
)
|
)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
except IntegrityError:
|
||||||
|
await session.rollback()
|
||||||
|
await session.refresh(db_doc)
|
||||||
|
|
||||||
await pipeline.index(db_doc, connector_doc, llm)
|
await pipeline.index(db_doc, connector_doc, llm)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ export const DocumentNode = React.memo(function DocumentNode({
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{onExport && (
|
{onExport && (
|
||||||
<DropdownMenuSub>
|
<DropdownMenuSub>
|
||||||
<DropdownMenuSubTrigger>
|
<DropdownMenuSubTrigger disabled={isProcessing}>
|
||||||
<Download className="mr-2 h-4 w-4" />
|
<Download className="mr-2 h-4 w-4" />
|
||||||
Export
|
Export
|
||||||
</DropdownMenuSubTrigger>
|
</DropdownMenuSubTrigger>
|
||||||
|
|
@ -277,7 +277,7 @@ export const DocumentNode = React.memo(function DocumentNode({
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
{onExport && (
|
{onExport && (
|
||||||
<ContextMenuSub>
|
<ContextMenuSub>
|
||||||
<ContextMenuSubTrigger>
|
<ContextMenuSubTrigger disabled={isProcessing}>
|
||||||
<Download className="mr-2 h-4 w-4" />
|
<Download className="mr-2 h-4 w-4" />
|
||||||
Export
|
Export
|
||||||
</ContextMenuSubTrigger>
|
</ContextMenuSubTrigger>
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,14 @@ export function DocumentsSidebar({
|
||||||
const handleDeleteFolder = useCallback(async (folder: FolderDisplay) => {
|
const handleDeleteFolder = useCallback(async (folder: FolderDisplay) => {
|
||||||
if (!confirm(`Delete folder "${folder.name}" and all its contents?`)) return;
|
if (!confirm(`Delete folder "${folder.name}" and all its contents?`)) return;
|
||||||
try {
|
try {
|
||||||
|
const api = window.electronAPI;
|
||||||
|
if (api) {
|
||||||
|
const watchedFolders = await api.getWatchedFolders();
|
||||||
|
const matched = watchedFolders.find((wf) => wf.rootFolderId === folder.id);
|
||||||
|
if (matched) {
|
||||||
|
await api.removeWatchedFolder(matched.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
await foldersApiService.deleteFolder(folder.id);
|
await foldersApiService.deleteFolder(folder.id);
|
||||||
toast.success("Folder deleted");
|
toast.success("Folder deleted");
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ function ContextMenuSubTrigger({
|
||||||
data-slot="context-menu-sub-trigger"
|
data-slot="context-menu-sub-trigger"
|
||||||
data-inset={inset}
|
data-inset={inset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
|
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ function DropdownMenuSubTrigger({
|
||||||
data-slot="dropdown-menu-sub-trigger"
|
data-slot="dropdown-menu-sub-trigger"
|
||||||
data-inset={inset}
|
data-inset={inset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-neutral-200 focus:text-accent-foreground dark:focus:bg-neutral-700 data-[state=open]:bg-neutral-200 data-[state=open]:text-accent-foreground dark:data-[state=open]:bg-neutral-700 [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"focus:bg-neutral-200 focus:text-accent-foreground dark:focus:bg-neutral-700 data-[state=open]:bg-neutral-200 data-[state=open]:text-accent-foreground dark:data-[state=open]:bg-neutral-700 [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue