mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-20 07:00:09 +08:00
feat: improve content protection during file operations (#10378)
* feat: improve content protection during file operations - Add validation for knowledge base configuration before saving - Enhance error handling for note content reading - Implement content backup and restoration during file rename - Add content verification after rename operations - Improve user feedback with specific error messages * fix: format check --------- Co-authored-by: 自由的世界人 <3196812536@qq.com>
This commit is contained in:
parent
52a980f751
commit
4aa9c9f225
@ -253,12 +253,39 @@ const PopupContainer: React.FC<Props> = ({ source, title, resolve }) => {
|
|||||||
let savedCount = 0
|
let savedCount = 0
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Validate knowledge base configuration before proceeding
|
||||||
|
if (!selectedBaseId) {
|
||||||
|
throw new Error('No knowledge base selected')
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedBase = bases.find((base) => base.id === selectedBaseId)
|
||||||
|
if (!selectedBase) {
|
||||||
|
throw new Error('Selected knowledge base not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedBase.version) {
|
||||||
|
throw new Error('Knowledge base is not properly configured. Please check the knowledge base settings.')
|
||||||
|
}
|
||||||
|
|
||||||
if (isNoteMode) {
|
if (isNoteMode) {
|
||||||
const note = source.data as NotesTreeNode
|
const note = source.data as NotesTreeNode
|
||||||
const content = note.externalPath
|
if (!note.externalPath) {
|
||||||
? await window.api.file.readExternal(note.externalPath)
|
throw new Error('Note external path is required for export')
|
||||||
: await window.api.file.read(note.id + '.md')
|
}
|
||||||
logger.debug('Note content:', content)
|
|
||||||
|
let content = ''
|
||||||
|
try {
|
||||||
|
content = await window.api.file.readExternal(note.externalPath)
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to read note file:', error as Error)
|
||||||
|
throw new Error('Failed to read note content. Please ensure the file exists and is accessible.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content || content.trim() === '') {
|
||||||
|
throw new Error('Note content is empty. Cannot export empty notes to knowledge base.')
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Note content loaded', { contentLength: content.length })
|
||||||
await addNote(content)
|
await addNote(content)
|
||||||
savedCount = 1
|
savedCount = 1
|
||||||
} else {
|
} else {
|
||||||
@ -283,9 +310,23 @@ const PopupContainer: React.FC<Props> = ({ source, title, resolve }) => {
|
|||||||
resolve({ success: true, savedCount })
|
resolve({ success: true, savedCount })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('save failed:', error as Error)
|
logger.error('save failed:', error as Error)
|
||||||
window.toast.error(
|
|
||||||
t(isTopicMode ? 'chat.save.topic.knowledge.error.save_failed' : 'chat.save.knowledge.error.save_failed')
|
// Provide more specific error messages
|
||||||
|
let errorMessage = t(
|
||||||
|
isTopicMode ? 'chat.save.topic.knowledge.error.save_failed' : 'chat.save.knowledge.error.save_failed'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
if (error.message.includes('not properly configured')) {
|
||||||
|
errorMessage = error.message
|
||||||
|
} else if (error.message.includes('empty')) {
|
||||||
|
errorMessage = error.message
|
||||||
|
} else if (error.message.includes('read note content')) {
|
||||||
|
errorMessage = error.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.toast.error(errorMessage)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -492,10 +492,42 @@ const NotesPage: FC = () => {
|
|||||||
|
|
||||||
if (node && node.name !== newName) {
|
if (node && node.name !== newName) {
|
||||||
const oldExternalPath = node.externalPath
|
const oldExternalPath = node.externalPath
|
||||||
|
let currentContent = ''
|
||||||
|
|
||||||
|
// Save current content before rename to prevent content loss
|
||||||
|
if (node.type === 'file' && activeFilePath === oldExternalPath) {
|
||||||
|
// Get content from editor or current cache
|
||||||
|
currentContent = editorRef.current?.getMarkdown() || lastContentRef.current || currentContent
|
||||||
|
|
||||||
|
// Save current content to the file before renaming
|
||||||
|
if (currentContent.trim()) {
|
||||||
|
try {
|
||||||
|
await saveCurrentNote(currentContent, oldExternalPath)
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('Failed to save content before rename:', error as Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const renamedNode = await renameNode(nodeId, newName)
|
const renamedNode = await renameNode(nodeId, newName)
|
||||||
|
|
||||||
if (renamedNode.type === 'file' && activeFilePath === oldExternalPath) {
|
if (renamedNode.type === 'file' && activeFilePath === oldExternalPath) {
|
||||||
|
// Restore content to the new file path if content was lost during rename
|
||||||
|
if (currentContent.trim()) {
|
||||||
|
try {
|
||||||
|
const newFileContent = await window.api.file.readExternal(renamedNode.externalPath)
|
||||||
|
if (!newFileContent || newFileContent.trim() === '') {
|
||||||
|
await window.api.file.write(renamedNode.externalPath, currentContent)
|
||||||
|
logger.info('Restored content to renamed file')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to restore content after rename:', error as Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(setActiveFilePath(renamedNode.externalPath))
|
dispatch(setActiveFilePath(renamedNode.externalPath))
|
||||||
|
// Invalidate cache for the new path to ensure content is loaded correctly
|
||||||
|
invalidateFileContent(renamedNode.externalPath)
|
||||||
} else if (
|
} else if (
|
||||||
renamedNode.type === 'folder' &&
|
renamedNode.type === 'folder' &&
|
||||||
activeFilePath &&
|
activeFilePath &&
|
||||||
@ -504,6 +536,8 @@ const NotesPage: FC = () => {
|
|||||||
const relativePath = activeFilePath.substring(oldExternalPath.length)
|
const relativePath = activeFilePath.substring(oldExternalPath.length)
|
||||||
const newFilePath = renamedNode.externalPath + relativePath
|
const newFilePath = renamedNode.externalPath + relativePath
|
||||||
dispatch(setActiveFilePath(newFilePath))
|
dispatch(setActiveFilePath(newFilePath))
|
||||||
|
// Invalidate cache for the new file path after folder rename
|
||||||
|
invalidateFileContent(newFilePath)
|
||||||
}
|
}
|
||||||
await sortAllLevels(sortType)
|
await sortAllLevels(sortType)
|
||||||
if (renamedNode.name !== newName) {
|
if (renamedNode.name !== newName) {
|
||||||
@ -518,7 +552,7 @@ const NotesPage: FC = () => {
|
|||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[activeFilePath, dispatch, findNodeById, sortType, t]
|
[activeFilePath, dispatch, findNodeById, sortType, t, invalidateFileContent, saveCurrentNote]
|
||||||
)
|
)
|
||||||
|
|
||||||
// 处理文件上传
|
// 处理文件上传
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user