feat: allow projects to have external data path for posts and media
This commit is contained in:
@@ -11,54 +11,72 @@ import { taskManager, TaskProgress } from '../engine/TaskManager';
|
||||
import { getDatabase } from '../database';
|
||||
import { media } from '../database/schema';
|
||||
|
||||
/**
|
||||
* Wrap an IPC handler so that "Database is closing" errors during shutdown
|
||||
* are silently swallowed instead of being logged as scary red error messages.
|
||||
*/
|
||||
function safeHandle(channel: string, handler: (...args: any[]) => Promise<any>): void {
|
||||
ipcMain.handle(channel, async (...args) => {
|
||||
try {
|
||||
return await handler(...args);
|
||||
} catch (error: any) {
|
||||
if (error?.message === 'Database is closing') {
|
||||
return null; // Silently ignore during shutdown
|
||||
}
|
||||
throw error; // Re-throw all other errors
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function registerIpcHandlers(): void {
|
||||
// ============ Project Handlers ============
|
||||
|
||||
ipcMain.handle('projects:create', async (_, data: { name: string; description?: string; slug?: string }) => {
|
||||
safeHandle('projects:create', async (_, data: { name: string; description?: string; slug?: string }) => {
|
||||
const engine = getProjectEngine();
|
||||
return engine.createProject(data);
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:update', async (_, id: string, data: Partial<ProjectData>) => {
|
||||
safeHandle('projects:update', async (_, id: string, data: Partial<ProjectData>) => {
|
||||
const engine = getProjectEngine();
|
||||
return engine.updateProject(id, data);
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:delete', async (_, id: string) => {
|
||||
safeHandle('projects:delete', async (_, id: string) => {
|
||||
const engine = getProjectEngine();
|
||||
return engine.deleteProject(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:deleteWithData', async (_, id: string) => {
|
||||
safeHandle('projects:deleteWithData', async (_, id: string) => {
|
||||
const engine = getProjectEngine();
|
||||
return engine.deleteProjectWithData(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:get', async (_, id: string) => {
|
||||
safeHandle('projects:get', async (_, id: string) => {
|
||||
const engine = getProjectEngine();
|
||||
return engine.getProject(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:getAll', async () => {
|
||||
safeHandle('projects:getAll', async () => {
|
||||
const engine = getProjectEngine();
|
||||
return engine.getAllProjects();
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:getActive', async () => {
|
||||
safeHandle('projects:getActive', async () => {
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.getActiveProject();
|
||||
|
||||
// Ensure all engines have the correct project context
|
||||
if (project) {
|
||||
const baseDir = projectEngine.getProjectBaseDir(project.id, project.dataPath);
|
||||
const internalDir = projectEngine.getInternalBaseDir(project.id);
|
||||
const dataDir = projectEngine.getDataDir(project.id, project.dataPath);
|
||||
const postEngine = getPostEngine();
|
||||
const mediaEngine = getMediaEngine();
|
||||
const metaEngine = getMetaEngine();
|
||||
const tagEngine = getTagEngine();
|
||||
postEngine.setProjectContext(project.id, baseDir);
|
||||
mediaEngine.setProjectContext(project.id, baseDir);
|
||||
metaEngine.setProjectContext(project.id, baseDir);
|
||||
tagEngine.setProjectContext(project.id, baseDir);
|
||||
postEngine.setProjectContext(project.id, dataDir);
|
||||
mediaEngine.setProjectContext(project.id, dataDir, internalDir);
|
||||
metaEngine.setProjectContext(project.id);
|
||||
tagEngine.setProjectContext(project.id);
|
||||
|
||||
// Sync meta on startup
|
||||
await metaEngine.syncOnStartup();
|
||||
@@ -67,21 +85,22 @@ export function registerIpcHandlers(): void {
|
||||
return project;
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:setActive', async (_, id: string) => {
|
||||
safeHandle('projects:setActive', async (_, id: string) => {
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.setActiveProject(id);
|
||||
|
||||
// Update all engines to use the new project context
|
||||
if (project) {
|
||||
const baseDir = projectEngine.getProjectBaseDir(project.id, project.dataPath);
|
||||
const internalDir = projectEngine.getInternalBaseDir(project.id);
|
||||
const dataDir = projectEngine.getDataDir(project.id, project.dataPath);
|
||||
const postEngine = getPostEngine();
|
||||
const mediaEngine = getMediaEngine();
|
||||
const metaEngine = getMetaEngine();
|
||||
const tagEngine = getTagEngine();
|
||||
postEngine.setProjectContext(project.id, baseDir);
|
||||
mediaEngine.setProjectContext(project.id, baseDir);
|
||||
metaEngine.setProjectContext(project.id, baseDir);
|
||||
tagEngine.setProjectContext(project.id, baseDir);
|
||||
postEngine.setProjectContext(project.id, dataDir);
|
||||
mediaEngine.setProjectContext(project.id, dataDir, internalDir);
|
||||
metaEngine.setProjectContext(project.id);
|
||||
tagEngine.setProjectContext(project.id);
|
||||
|
||||
// Sync meta on project switch
|
||||
await metaEngine.syncOnStartup();
|
||||
@@ -92,68 +111,69 @@ export function registerIpcHandlers(): void {
|
||||
|
||||
// ============ Post Handlers ============
|
||||
|
||||
ipcMain.handle('posts:create', async (_, data: Partial<PostData>) => {
|
||||
safeHandle('posts:create', async (_, data: Partial<PostData>) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.createPost(data);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:isSlugAvailable', async (_, slug: string, excludePostId?: string) => {
|
||||
safeHandle('posts:isSlugAvailable', async (_, slug: string, excludePostId?: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.isSlugAvailable(slug, excludePostId);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:generateUniqueSlug', async (_, title: string, excludePostId?: string) => {
|
||||
safeHandle('posts:generateUniqueSlug', async (_, title: string, excludePostId?: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.generateUniqueSlug(title, excludePostId);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:update', async (_, id: string, data: Partial<PostData>) => {
|
||||
safeHandle('posts:update', async (_, id: string, data: Partial<PostData>) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.updatePost(id, data);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:delete', async (_, id: string) => {
|
||||
safeHandle('posts:delete', async (_, id: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.deletePost(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:get', async (_, id: string) => {
|
||||
safeHandle('posts:get', async (_, id: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getPost(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getAll', async (_, options?: PaginationOptions) => {
|
||||
safeHandle('posts:getAll', async (_, options?: PaginationOptions) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getAllPosts(options);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getByStatus', async (_, status: 'draft' | 'published' | 'archived') => {
|
||||
safeHandle('posts:getByStatus', async (_, status: 'draft' | 'published' | 'archived') => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getPostsByStatus(status);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:publish', async (_, id: string) => {
|
||||
safeHandle('posts:publish', async (_, id: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.publishPost(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:discard', async (_, id: string) => {
|
||||
safeHandle('posts:discard', async (_, id: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.discardChanges(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:hasPublishedVersion', async (_, id: string) => {
|
||||
safeHandle('posts:hasPublishedVersion', async (_, id: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.hasPublishedVersion(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:rebuildFromFiles', async () => {
|
||||
safeHandle('posts:rebuildFromFiles', async () => {
|
||||
// Ensure project context is current before rebuilding
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.getActiveProject();
|
||||
const engine = getPostEngine();
|
||||
if (project) {
|
||||
engine.setProjectContext(project.id);
|
||||
const dataDir = projectEngine.getDataDir(project.id, project.dataPath);
|
||||
engine.setProjectContext(project.id, dataDir);
|
||||
}
|
||||
// Fire and forget - don't await, let it run in background
|
||||
engine.rebuildDatabaseFromFiles().catch(err => {
|
||||
@@ -161,67 +181,68 @@ export function registerIpcHandlers(): void {
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:search', async (_, query: string) => {
|
||||
safeHandle('posts:search', async (_, query: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.searchPosts(query);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:filter', async (_, filter: PostFilter) => {
|
||||
safeHandle('posts:filter', async (_, filter: PostFilter) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getPostsFiltered(filter);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getTags', async () => {
|
||||
safeHandle('posts:getTags', async () => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getAvailableTags();
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getCategories', async () => {
|
||||
safeHandle('posts:getCategories', async () => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getAvailableCategories();
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getByYearMonth', async () => {
|
||||
safeHandle('posts:getByYearMonth', async () => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getPostsByYearMonth();
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getTagsWithCounts', async () => {
|
||||
safeHandle('posts:getTagsWithCounts', async () => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getTagsWithCounts();
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getCategoriesWithCounts', async () => {
|
||||
safeHandle('posts:getCategoriesWithCounts', async () => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getCategoriesWithCounts();
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getDashboardStats', async () => {
|
||||
safeHandle('posts:getDashboardStats', async () => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getDashboardStats();
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getLinksTo', async (_, id: string) => {
|
||||
safeHandle('posts:getLinksTo', async (_, id: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getLinksTo(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:getLinkedBy', async (_, id: string) => {
|
||||
safeHandle('posts:getLinkedBy', async (_, id: string) => {
|
||||
const engine = getPostEngine();
|
||||
return engine.getLinkedBy(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:rebuildLinks', async () => {
|
||||
safeHandle('posts:rebuildLinks', async () => {
|
||||
const engine = getPostEngine();
|
||||
return engine.rebuildAllPostLinks();
|
||||
});
|
||||
|
||||
ipcMain.handle('posts:reindexText', async () => {
|
||||
safeHandle('posts:reindexText', async () => {
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.getActiveProject();
|
||||
const engine = getPostEngine();
|
||||
if (project) {
|
||||
engine.setProjectContext(project.id);
|
||||
const dataDir = projectEngine.getDataDir(project.id, project.dataPath);
|
||||
engine.setProjectContext(project.id, dataDir);
|
||||
}
|
||||
// Fire and forget - let it run as a background task
|
||||
engine.reindexText().catch(err => {
|
||||
@@ -231,12 +252,12 @@ export function registerIpcHandlers(): void {
|
||||
|
||||
// ============ Media Handlers ============
|
||||
|
||||
ipcMain.handle('media:import', async (_, sourcePath: string, metadata?: Partial<MediaData>) => {
|
||||
safeHandle('media:import', async (_, sourcePath: string, metadata?: Partial<MediaData>) => {
|
||||
const engine = getMediaEngine();
|
||||
return engine.importMedia(sourcePath, metadata);
|
||||
});
|
||||
|
||||
ipcMain.handle('media:importDialog', async () => {
|
||||
safeHandle('media:importDialog', async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
title: 'Import Media',
|
||||
filters: [
|
||||
@@ -265,45 +286,47 @@ export function registerIpcHandlers(): void {
|
||||
return imported;
|
||||
});
|
||||
|
||||
ipcMain.handle('media:update', async (_, id: string, data: Partial<MediaData>) => {
|
||||
safeHandle('media:update', async (_, id: string, data: Partial<MediaData>) => {
|
||||
const engine = getMediaEngine();
|
||||
return engine.updateMedia(id, data);
|
||||
});
|
||||
|
||||
ipcMain.handle('media:delete', async (_, id: string) => {
|
||||
safeHandle('media:delete', async (_, id: string) => {
|
||||
const engine = getMediaEngine();
|
||||
return engine.deleteMedia(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('media:get', async (_, id: string) => {
|
||||
safeHandle('media:get', async (_, id: string) => {
|
||||
const engine = getMediaEngine();
|
||||
return engine.getMedia(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('media:getUrl', async (_, id: string) => {
|
||||
safeHandle('media:getUrl', async (_, id: string) => {
|
||||
// Returns the bds-media:// protocol URL for a media item
|
||||
return `bds-media://${id}`;
|
||||
});
|
||||
|
||||
ipcMain.handle('media:getFilePath', async (_, id: string) => {
|
||||
safeHandle('media:getFilePath', async (_, id: string) => {
|
||||
// Returns the actual file path for a media item (for debugging/advanced use)
|
||||
const db = getDatabase().getLocal();
|
||||
const mediaItem = await db.select().from(media).where(eq(media.id, id)).get();
|
||||
return mediaItem?.filePath ?? null;
|
||||
});
|
||||
|
||||
ipcMain.handle('media:getAll', async () => {
|
||||
safeHandle('media:getAll', async () => {
|
||||
const engine = getMediaEngine();
|
||||
return engine.getAllMedia();
|
||||
});
|
||||
|
||||
ipcMain.handle('media:rebuildFromFiles', async () => {
|
||||
safeHandle('media:rebuildFromFiles', async () => {
|
||||
// Ensure project context is current before rebuilding
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.getActiveProject();
|
||||
const engine = getMediaEngine();
|
||||
if (project) {
|
||||
engine.setProjectContext(project.id);
|
||||
const internalDir = projectEngine.getInternalBaseDir(project.id);
|
||||
const dataDir = projectEngine.getDataDir(project.id, project.dataPath);
|
||||
engine.setProjectContext(project.id, dataDir, internalDir);
|
||||
}
|
||||
// Fire and forget - don't await, let it run in background
|
||||
engine.rebuildDatabaseFromFiles().catch(err => {
|
||||
@@ -311,12 +334,12 @@ export function registerIpcHandlers(): void {
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle('media:getThumbnail', async (_, id: string, size?: 'small' | 'medium' | 'large') => {
|
||||
safeHandle('media:getThumbnail', async (_, id: string, size?: 'small' | 'medium' | 'large') => {
|
||||
const engine = getMediaEngine();
|
||||
return engine.getThumbnailDataUrl(id, size || 'small');
|
||||
});
|
||||
|
||||
ipcMain.handle('media:regenerateThumbnails', async (_, id: string) => {
|
||||
safeHandle('media:regenerateThumbnails', async (_, id: string) => {
|
||||
const engine = getMediaEngine();
|
||||
const mediaItem = await engine.getMedia(id);
|
||||
if (mediaItem && mediaItem.mimeType.startsWith('image/')) {
|
||||
@@ -329,56 +352,58 @@ export function registerIpcHandlers(): void {
|
||||
return null;
|
||||
});
|
||||
|
||||
ipcMain.handle('media:regenerateMissingThumbnails', async () => {
|
||||
safeHandle('media:regenerateMissingThumbnails', async () => {
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.getActiveProject();
|
||||
const engine = getMediaEngine();
|
||||
if (project) {
|
||||
engine.setProjectContext(project.id);
|
||||
const internalDir = projectEngine.getInternalBaseDir(project.id);
|
||||
const dataDir = projectEngine.getDataDir(project.id, project.dataPath);
|
||||
engine.setProjectContext(project.id, dataDir, internalDir);
|
||||
}
|
||||
return engine.regenerateMissingThumbnails();
|
||||
});
|
||||
|
||||
// ============ Sync Handlers ============
|
||||
|
||||
ipcMain.handle('sync:configure', async (_, config: SyncConfig) => {
|
||||
safeHandle('sync:configure', async (_, config: SyncConfig) => {
|
||||
const engine = getSyncEngine();
|
||||
return engine.configure(config);
|
||||
});
|
||||
|
||||
ipcMain.handle('sync:start', async (_, direction: SyncDirection = 'bidirectional') => {
|
||||
safeHandle('sync:start', async (_, direction: SyncDirection = 'bidirectional') => {
|
||||
const engine = getSyncEngine();
|
||||
return engine.fullSync(direction);
|
||||
});
|
||||
|
||||
ipcMain.handle('sync:getStatus', async () => {
|
||||
safeHandle('sync:getStatus', async () => {
|
||||
const engine = getSyncEngine();
|
||||
return engine.getSyncStatus();
|
||||
});
|
||||
|
||||
ipcMain.handle('sync:isConfigured', async () => {
|
||||
safeHandle('sync:isConfigured', async () => {
|
||||
const engine = getSyncEngine();
|
||||
return engine.isConfigured();
|
||||
});
|
||||
|
||||
ipcMain.handle('sync:getPendingCount', async () => {
|
||||
safeHandle('sync:getPendingCount', async () => {
|
||||
const engine = getSyncEngine();
|
||||
return engine.getPendingChangesCount();
|
||||
});
|
||||
|
||||
ipcMain.handle('sync:getLog', async (_, limit?: number) => {
|
||||
safeHandle('sync:getLog', async (_, limit?: number) => {
|
||||
const engine = getSyncEngine();
|
||||
return engine.getSyncLog(limit);
|
||||
});
|
||||
|
||||
ipcMain.handle('sync:stopAutoSync', async () => {
|
||||
safeHandle('sync:stopAutoSync', async () => {
|
||||
const engine = getSyncEngine();
|
||||
return engine.stopAutoSync();
|
||||
});
|
||||
|
||||
// ============ Dropbox Sync Handlers ============
|
||||
|
||||
ipcMain.handle('dropbox:configure', async (_, config: Partial<DropboxSyncConfig>) => {
|
||||
safeHandle('dropbox:configure', async (_, config: Partial<DropboxSyncConfig>) => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
|
||||
// Inject local project paths so the engine knows where files live
|
||||
@@ -402,47 +427,47 @@ export function registerIpcHandlers(): void {
|
||||
return engine.configure(fullConfig);
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:isConfigured', async () => {
|
||||
safeHandle('dropbox:isConfigured', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
return engine.isConfigured();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:getStatus', async () => {
|
||||
safeHandle('dropbox:getStatus', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
return engine.getStatus();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:syncAll', async () => {
|
||||
safeHandle('dropbox:syncAll', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
return engine.syncAll();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:startWatching', async () => {
|
||||
safeHandle('dropbox:startWatching', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
engine.startWatching();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:stopWatching', async () => {
|
||||
safeHandle('dropbox:stopWatching', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
engine.stopWatching();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:startPolling', async () => {
|
||||
safeHandle('dropbox:startPolling', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
engine.startPolling();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:stopPolling', async () => {
|
||||
safeHandle('dropbox:stopPolling', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
engine.stopPolling();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:getConflicts', async () => {
|
||||
safeHandle('dropbox:getConflicts', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
return engine.getPendingConflicts();
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:resolveConflict', async (_, conflictId: string, resolution: ConflictResolution) => {
|
||||
safeHandle('dropbox:resolveConflict', async (_, conflictId: string, resolution: ConflictResolution) => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
const conflicts = engine.getPendingConflicts();
|
||||
const conflict = conflicts.find(c => c.id === conflictId);
|
||||
@@ -452,32 +477,32 @@ export function registerIpcHandlers(): void {
|
||||
return engine.resolveConflict(conflict, resolution);
|
||||
});
|
||||
|
||||
ipcMain.handle('dropbox:getLastSyncTime', async () => {
|
||||
safeHandle('dropbox:getLastSyncTime', async () => {
|
||||
const engine = getDropboxSyncEngine();
|
||||
return engine.getLastSyncTime();
|
||||
});
|
||||
|
||||
// ============ Task Handlers ============
|
||||
|
||||
ipcMain.handle('tasks:getAll', async () => {
|
||||
safeHandle('tasks:getAll', async () => {
|
||||
return taskManager.getAllTasks();
|
||||
});
|
||||
|
||||
ipcMain.handle('tasks:getRunning', async () => {
|
||||
safeHandle('tasks:getRunning', async () => {
|
||||
return taskManager.getRunningTasks();
|
||||
});
|
||||
|
||||
ipcMain.handle('tasks:cancel', async (_, taskId: string) => {
|
||||
safeHandle('tasks:cancel', async (_, taskId: string) => {
|
||||
return taskManager.cancelTask(taskId);
|
||||
});
|
||||
|
||||
ipcMain.handle('tasks:clearCompleted', async () => {
|
||||
safeHandle('tasks:clearCompleted', async () => {
|
||||
return taskManager.clearCompletedTasks();
|
||||
});
|
||||
|
||||
// ============ App Handlers ============
|
||||
|
||||
ipcMain.handle('app:getDataPaths', async () => {
|
||||
safeHandle('app:getDataPaths', async () => {
|
||||
// Get paths for the active project
|
||||
const projectEngine = getProjectEngine();
|
||||
const activeProject = await projectEngine.getActiveProject();
|
||||
@@ -490,11 +515,11 @@ export function registerIpcHandlers(): void {
|
||||
};
|
||||
});
|
||||
|
||||
ipcMain.handle('app:openFolder', async (_, folderPath: string) => {
|
||||
safeHandle('app:openFolder', async (_, folderPath: string) => {
|
||||
return shell.openPath(folderPath);
|
||||
});
|
||||
|
||||
ipcMain.handle('app:selectFolder', async (_, title?: string) => {
|
||||
safeHandle('app:selectFolder', async (_, title?: string) => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
title: title || 'Select Folder',
|
||||
properties: ['openDirectory', 'createDirectory'],
|
||||
@@ -505,52 +530,52 @@ export function registerIpcHandlers(): void {
|
||||
return result.filePaths[0];
|
||||
});
|
||||
|
||||
ipcMain.handle('app:getDefaultProjectPath', async (_, projectId: string) => {
|
||||
safeHandle('app:getDefaultProjectPath', async (_, projectId: string) => {
|
||||
const projectEngine = getProjectEngine();
|
||||
return projectEngine.getDefaultProjectBaseDir(projectId);
|
||||
});
|
||||
|
||||
ipcMain.handle('app:showItemInFolder', async (_, itemPath: string) => {
|
||||
safeHandle('app:showItemInFolder', async (_, itemPath: string) => {
|
||||
return shell.showItemInFolder(itemPath);
|
||||
});
|
||||
|
||||
// ============ Meta Handlers ============
|
||||
|
||||
ipcMain.handle('meta:getTags', async () => {
|
||||
safeHandle('meta:getTags', async () => {
|
||||
const engine = getMetaEngine();
|
||||
return engine.getTags();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:getCategories', async () => {
|
||||
safeHandle('meta:getCategories', async () => {
|
||||
const engine = getMetaEngine();
|
||||
return engine.getCategories();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:addTag', async (_, tag: string) => {
|
||||
safeHandle('meta:addTag', async (_, tag: string) => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.addTag(tag);
|
||||
return engine.getTags();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:removeTag', async (_, tag: string) => {
|
||||
safeHandle('meta:removeTag', async (_, tag: string) => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.removeTag(tag);
|
||||
return engine.getTags();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:addCategory', async (_, category: string) => {
|
||||
safeHandle('meta:addCategory', async (_, category: string) => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.addCategory(category);
|
||||
return engine.getCategories();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:removeCategory', async (_, category: string) => {
|
||||
safeHandle('meta:removeCategory', async (_, category: string) => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.removeCategory(category);
|
||||
return engine.getCategories();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:syncOnStartup', async () => {
|
||||
safeHandle('meta:syncOnStartup', async () => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.syncOnStartup();
|
||||
return {
|
||||
@@ -560,18 +585,18 @@ export function registerIpcHandlers(): void {
|
||||
};
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:getProjectMetadata', async () => {
|
||||
safeHandle('meta:getProjectMetadata', async () => {
|
||||
const engine = getMetaEngine();
|
||||
return engine.getProjectMetadata();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:setProjectMetadata', async (_, metadata: { name: string; description?: string }) => {
|
||||
safeHandle('meta:setProjectMetadata', async (_, metadata: { name: string; description?: string }) => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.setProjectMetadata(metadata);
|
||||
return engine.getProjectMetadata();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:updateProjectMetadata', async (_, updates: { name?: string; description?: string; dataPath?: string }) => {
|
||||
safeHandle('meta:updateProjectMetadata', async (_, updates: { name?: string; description?: string; dataPath?: string }) => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.updateProjectMetadata(updates);
|
||||
return engine.getProjectMetadata();
|
||||
@@ -579,57 +604,57 @@ export function registerIpcHandlers(): void {
|
||||
|
||||
// ============ Tag Management Handlers ============
|
||||
|
||||
ipcMain.handle('tags:getAll', async () => {
|
||||
safeHandle('tags:getAll', async () => {
|
||||
const engine = getTagEngine();
|
||||
return engine.getAllTags();
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:getWithCounts', async () => {
|
||||
safeHandle('tags:getWithCounts', async () => {
|
||||
const engine = getTagEngine();
|
||||
return engine.getTagsWithCounts();
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:get', async (_, id: string) => {
|
||||
safeHandle('tags:get', async (_, id: string) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.getTag(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:getByName', async (_, name: string) => {
|
||||
safeHandle('tags:getByName', async (_, name: string) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.getTagByName(name);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:create', async (_, data: { name: string; color?: string }) => {
|
||||
safeHandle('tags:create', async (_, data: { name: string; color?: string }) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.createTag(data);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:update', async (_, id: string, data: { name?: string; color?: string | null }) => {
|
||||
safeHandle('tags:update', async (_, id: string, data: { name?: string; color?: string | null }) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.updateTag(id, data);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:delete', async (_, id: string) => {
|
||||
safeHandle('tags:delete', async (_, id: string) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.deleteTag(id);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:merge', async (_, sourceTagIds: string[], targetTagId: string) => {
|
||||
safeHandle('tags:merge', async (_, sourceTagIds: string[], targetTagId: string) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.mergeTags(sourceTagIds, targetTagId);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:rename', async (_, id: string, newName: string) => {
|
||||
safeHandle('tags:rename', async (_, id: string, newName: string) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.renameTag(id, newName);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:getPostsWithTag', async (_, tagId: string) => {
|
||||
safeHandle('tags:getPostsWithTag', async (_, tagId: string) => {
|
||||
const engine = getTagEngine();
|
||||
return engine.getPostsWithTag(tagId);
|
||||
});
|
||||
|
||||
ipcMain.handle('tags:syncFromPosts', async () => {
|
||||
safeHandle('tags:syncFromPosts', async () => {
|
||||
const engine = getTagEngine();
|
||||
return engine.syncTagsFromPosts();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user