fix: next round of cleanups
This commit is contained in:
56
src/main/engine/AppApiAdapter.ts
Normal file
56
src/main/engine/AppApiAdapter.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import * as path from 'path';
|
||||
import * as fsPromises from 'fs/promises';
|
||||
import { app } from 'electron';
|
||||
import { getProjectEngine } from './ProjectEngine';
|
||||
import { getDatabase } from '../database';
|
||||
|
||||
/**
|
||||
* Adapter that wraps app-level IPC handler logic for use by the Python API layer.
|
||||
* Provides safe, read-only app methods without requiring Electron UI facilities.
|
||||
*/
|
||||
export class AppApiAdapter {
|
||||
async getDataPaths(): Promise<{ database: string; posts: string; media: string }> {
|
||||
const projectEngine = getProjectEngine();
|
||||
const activeProject = await projectEngine.getActiveProject();
|
||||
const projectId = activeProject?.id || 'default';
|
||||
const paths = projectEngine.getProjectPaths(projectId, activeProject?.dataPath);
|
||||
return {
|
||||
database: getDatabase().getDataPaths().database,
|
||||
posts: paths.posts,
|
||||
media: paths.media,
|
||||
};
|
||||
}
|
||||
|
||||
async getSystemLanguage(): Promise<string> {
|
||||
return app.getLocale();
|
||||
}
|
||||
|
||||
async getDefaultProjectPath(projectId: string): Promise<string> {
|
||||
return getProjectEngine().getDefaultProjectBaseDir(projectId);
|
||||
}
|
||||
|
||||
async readProjectMetadata(folderPath: string): Promise<{ name?: string; description?: string; publicUrl?: string; mainLanguage?: string } | null> {
|
||||
const metaPath = path.join(folderPath, 'meta', 'project.json');
|
||||
try {
|
||||
const content = await fsPromises.readFile(metaPath, 'utf-8');
|
||||
const metadata = JSON.parse(content);
|
||||
return {
|
||||
name: metadata.name || undefined,
|
||||
description: metadata.description || undefined,
|
||||
publicUrl: metadata.publicUrl || undefined,
|
||||
mainLanguage: metadata.mainLanguage || undefined,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let instance: AppApiAdapter | null = null;
|
||||
|
||||
export function getAppApiAdapter(): AppApiAdapter {
|
||||
if (!instance) {
|
||||
instance = new AppApiAdapter();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
80
src/main/engine/GitApiAdapter.ts
Normal file
80
src/main/engine/GitApiAdapter.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { getGitEngine } from './GitEngine';
|
||||
import { getProjectEngine } from './ProjectEngine';
|
||||
import type {
|
||||
GitAvailability,
|
||||
RepoState,
|
||||
GitStatusDto,
|
||||
GitHistoryEntry,
|
||||
GitRemoteStateDto,
|
||||
GitActionResult,
|
||||
} from './GitEngine';
|
||||
|
||||
export type { GitAvailability, RepoState, GitStatusDto, GitHistoryEntry, GitRemoteStateDto, GitActionResult };
|
||||
|
||||
/**
|
||||
* Adapter that wraps GitEngine for use by the Python API layer.
|
||||
* Auto-resolves projectPath from the active project so Python scripts
|
||||
* don't need to pass it.
|
||||
*/
|
||||
export class GitApiAdapter {
|
||||
private async resolveProjectPath(): Promise<string> {
|
||||
const project = await getProjectEngine().getActiveProject();
|
||||
if (!project?.dataPath) {
|
||||
throw new Error('No active project with a data path');
|
||||
}
|
||||
return project.dataPath;
|
||||
}
|
||||
|
||||
async checkAvailability(): Promise<GitAvailability> {
|
||||
return getGitEngine().checkAvailability();
|
||||
}
|
||||
|
||||
async getRepoState(): Promise<RepoState> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().getRepoState(projectPath);
|
||||
}
|
||||
|
||||
async getStatus(): Promise<GitStatusDto> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().getStatus(projectPath);
|
||||
}
|
||||
|
||||
async getHistory(limit?: number): Promise<GitHistoryEntry[]> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().getHistory(projectPath, limit);
|
||||
}
|
||||
|
||||
async getRemoteState(): Promise<GitRemoteStateDto> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().getRemoteState(projectPath);
|
||||
}
|
||||
|
||||
async fetch(): Promise<GitActionResult> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().fetch(projectPath);
|
||||
}
|
||||
|
||||
async pull(): Promise<GitActionResult> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().pull(projectPath);
|
||||
}
|
||||
|
||||
async push(): Promise<GitActionResult> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().push(projectPath);
|
||||
}
|
||||
|
||||
async commitAll(message: string): Promise<GitActionResult> {
|
||||
const projectPath = await this.resolveProjectPath();
|
||||
return getGitEngine().commitAll(projectPath, message);
|
||||
}
|
||||
}
|
||||
|
||||
let instance: GitApiAdapter | null = null;
|
||||
|
||||
export function getGitApiAdapter(): GitApiAdapter {
|
||||
if (!instance) {
|
||||
instance = new GitApiAdapter();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
73
src/main/engine/PublishApiAdapter.ts
Normal file
73
src/main/engine/PublishApiAdapter.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { getProjectEngine } from './ProjectEngine';
|
||||
import { getPublishEngine, type PublishCredentials } from './PublishEngine';
|
||||
import { taskManager } from './TaskManager';
|
||||
|
||||
export interface PublishSiteResult {
|
||||
htmlFilesUploaded: number;
|
||||
thumbnailFilesUploaded: number;
|
||||
mediaFilesUploaded: number;
|
||||
filesSkipped: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter that wraps PublishEngine for use by the Python API layer.
|
||||
* Mirrors the orchestration logic from publishHandlers.ts: sets project
|
||||
* context, launches three parallel upload tasks, and returns aggregate results.
|
||||
*/
|
||||
export class PublishApiAdapter {
|
||||
async uploadSite(credentials: PublishCredentials): Promise<PublishSiteResult> {
|
||||
const project = await getProjectEngine().getActiveProject();
|
||||
if (!project) {
|
||||
throw new Error('No active project');
|
||||
}
|
||||
|
||||
const publishEngine = getPublishEngine();
|
||||
publishEngine.setProjectContext(project.id, project.dataPath!);
|
||||
|
||||
const ts = Date.now();
|
||||
const groupId = `publish-${ts}`;
|
||||
const groupName = 'Site Publishing';
|
||||
|
||||
const htmlTask = taskManager.runTask({
|
||||
id: `publish-html-${ts}`,
|
||||
name: 'Upload HTML',
|
||||
groupId,
|
||||
groupName,
|
||||
execute: (onProgress) => publishEngine.uploadHtml(credentials, onProgress),
|
||||
});
|
||||
|
||||
const thumbsTask = taskManager.runTask({
|
||||
id: `publish-thumbnails-${ts}`,
|
||||
name: 'Upload Thumbnails',
|
||||
groupId,
|
||||
groupName,
|
||||
execute: (onProgress) => publishEngine.uploadThumbnails(credentials, onProgress),
|
||||
});
|
||||
|
||||
const mediaTask = taskManager.runTask({
|
||||
id: `publish-media-${ts}`,
|
||||
name: 'Upload Media',
|
||||
groupId,
|
||||
groupName,
|
||||
execute: (onProgress) => publishEngine.uploadMedia(credentials, onProgress),
|
||||
});
|
||||
|
||||
const [html, thumbnails, media] = await Promise.all([htmlTask, thumbsTask, mediaTask]);
|
||||
|
||||
return {
|
||||
htmlFilesUploaded: html.filesUploaded,
|
||||
thumbnailFilesUploaded: thumbnails.filesUploaded,
|
||||
mediaFilesUploaded: media.filesUploaded,
|
||||
filesSkipped: html.filesSkipped + thumbnails.filesSkipped + media.filesSkipped,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let instance: PublishApiAdapter | null = null;
|
||||
|
||||
export function getPublishApiAdapter(): PublishApiAdapter {
|
||||
if (!instance) {
|
||||
instance = new PublishApiAdapter();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
@@ -94,6 +94,18 @@ export const ENGINE_MAP: Record<string, EngineGetter> = {
|
||||
const { taskManager } = require('../engine/TaskManager');
|
||||
return taskManager;
|
||||
},
|
||||
sync: () => {
|
||||
const { getGitApiAdapter } = require('../engine/GitApiAdapter');
|
||||
return getGitApiAdapter();
|
||||
},
|
||||
publish: () => {
|
||||
const { getPublishApiAdapter } = require('../engine/PublishApiAdapter');
|
||||
return getPublishApiAdapter();
|
||||
},
|
||||
app: () => {
|
||||
const { getAppApiAdapter } = require('../engine/AppApiAdapter');
|
||||
return getAppApiAdapter();
|
||||
},
|
||||
};
|
||||
|
||||
// Map API method names to engine method names where they differ
|
||||
@@ -199,10 +211,7 @@ export async function invokeMainProcessPythonApi(method: string, args: Record<st
|
||||
'media.importDialog', 'media.replaceFileDialog', 'media.getFilePath',
|
||||
'app.openFolder', 'app.selectFolder', 'app.showItemInFolder',
|
||||
'app.getTitleBarMetrics', 'app.notifyRendererReady', 'app.triggerMenuAction',
|
||||
'app.getBlogmarkBookmarklet', 'app.copyToClipboard',
|
||||
'chat.sendMessage', 'chat.abortMessage', 'chat.analyzeTaxonomy',
|
||||
'chat.analyzeMediaImage',
|
||||
'sync.configure', 'sync.start', 'sync.stopAutoSync',
|
||||
'app.getBlogmarkBookmarklet', 'app.copyToClipboard', 'app.setPreviewPostTarget',
|
||||
]);
|
||||
|
||||
if (unsafeMethods.has(method)) {
|
||||
|
||||
Reference in New Issue
Block a user