Unverified 提交 b2eb05a1 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Simplify error handling for IPC handlers (#119)

上级 c203b1d0
import { ipcMain, IpcMainInvokeEvent } from "electron";
import log from "electron-log";
export function createSafeHandler(logger: log.LogFunctions) {
return (
channel: string,
fn: (event: IpcMainInvokeEvent, ...args: any[]) => Promise<any>,
) => {
ipcMain.handle(
channel,
async (event: IpcMainInvokeEvent, ...args: any[]) => {
try {
return await fn(event, ...args);
} catch (error) {
logger.error(
`Error in ${fn.name}: args: ${JSON.stringify(args)}`,
error,
);
throw new Error(`[${channel}] ${error}`);
}
},
);
};
}
import { ipcMain } from "electron";
import { db } from "../../db"; import { db } from "../../db";
import { apps, messages } from "../../db/schema"; import { apps, messages } from "../../db/schema";
import { desc, eq, and, gt } from "drizzle-orm"; import { desc, eq, and, gt } from "drizzle-orm";
...@@ -8,15 +7,17 @@ import path from "node:path"; ...@@ -8,15 +7,17 @@ import path from "node:path";
import { getDyadAppPath } from "../../paths/paths"; import { getDyadAppPath } from "../../paths/paths";
import git from "isomorphic-git"; import git from "isomorphic-git";
import { promises as fsPromises } from "node:fs"; import { promises as fsPromises } from "node:fs";
// Import our utility modules
import { withLock } from "../utils/lock_utils"; import { withLock } from "../utils/lock_utils";
import { getGitAuthor } from "../utils/git_author"; import { getGitAuthor } from "../utils/git_author";
import log from "electron-log"; import log from "electron-log";
import { createSafeHandler } from "./safe_handle";
const logger = log.scope("version_handlers"); const logger = log.scope("version_handlers");
const handle = createSafeHandler(logger);
export function registerVersionHandlers() { export function registerVersionHandlers() {
ipcMain.handle("list-versions", async (_, { appId }: { appId: number }) => { handle("list-versions", async (_, { appId }: { appId: number }) => {
const app = await db.query.apps.findFirst({ const app = await db.query.apps.findFirst({
where: eq(apps.id, appId), where: eq(apps.id, appId),
}); });
...@@ -32,26 +33,21 @@ export function registerVersionHandlers() { ...@@ -32,26 +33,21 @@ export function registerVersionHandlers() {
return []; return [];
} }
try { const commits = await git.log({
const commits = await git.log({ fs,
fs, dir: appPath,
dir: appPath, // KEEP UP TO DATE WITH ChatHeader.tsx
// KEEP UP TO DATE WITH ChatHeader.tsx depth: 10_000, // Limit to last 10_000 commits for performance
depth: 10_000, // Limit to last 10_000 commits for performance });
});
return commits.map((commit) => ({ return commits.map((commit) => ({
oid: commit.oid, oid: commit.oid,
message: commit.commit.message, message: commit.commit.message,
timestamp: commit.commit.author.timestamp, timestamp: commit.commit.author.timestamp,
})) satisfies Version[]; })) satisfies Version[];
} catch (error: any) {
logger.error(`Error listing versions for app ${appId}:`, error);
throw new Error(`Failed to list versions: ${error.message}`);
}
}); });
ipcMain.handle( handle(
"get-current-branch", "get-current-branch",
async (_, { appId }: { appId: number }): Promise<BranchResult> => { async (_, { appId }: { appId: number }): Promise<BranchResult> => {
const app = await db.query.apps.findFirst({ const app = await db.query.apps.findFirst({
...@@ -86,7 +82,7 @@ export function registerVersionHandlers() { ...@@ -86,7 +82,7 @@ export function registerVersionHandlers() {
}, },
); );
ipcMain.handle( handle(
"revert-version", "revert-version",
async ( async (
_, _,
...@@ -106,117 +102,109 @@ export function registerVersionHandlers() { ...@@ -106,117 +102,109 @@ export function registerVersionHandlers() {
const appPath = getDyadAppPath(app.path); const appPath = getDyadAppPath(app.path);
try { await git.checkout({
await git.checkout({ fs,
fs, dir: appPath,
dir: appPath, ref: "main",
ref: "main", force: true,
force: true, });
}); // Get status matrix comparing the target commit (previousVersionId as HEAD) with current working directory
// Get status matrix comparing the target commit (previousVersionId as HEAD) with current working directory const matrix = await git.statusMatrix({
const matrix = await git.statusMatrix({ fs,
fs, dir: appPath,
dir: appPath, ref: previousVersionId,
ref: previousVersionId, });
});
// Process each file to revert to the state in previousVersionId // Process each file to revert to the state in previousVersionId
for (const [filepath, headStatus, workdirStatus] of matrix) { for (const [filepath, headStatus, workdirStatus] of matrix) {
const fullPath = path.join(appPath, filepath); const fullPath = path.join(appPath, filepath);
// If file exists in HEAD (previous version) // If file exists in HEAD (previous version)
if (headStatus === 1) { if (headStatus === 1) {
// If file doesn't exist or has changed in working directory, restore it from the target commit // If file doesn't exist or has changed in working directory, restore it from the target commit
if (workdirStatus !== 1) { if (workdirStatus !== 1) {
const { blob } = await git.readBlob({ const { blob } = await git.readBlob({
fs, fs,
dir: appPath, dir: appPath,
oid: previousVersionId, oid: previousVersionId,
filepath, filepath,
}); });
await fsPromises.mkdir(path.dirname(fullPath), { await fsPromises.mkdir(path.dirname(fullPath), {
recursive: true, recursive: true,
}); });
await fsPromises.writeFile(fullPath, Buffer.from(blob)); await fsPromises.writeFile(fullPath, Buffer.from(blob));
}
} }
// If file doesn't exist in HEAD but exists in working directory, delete it }
else if (headStatus === 0 && workdirStatus !== 0) { // If file doesn't exist in HEAD but exists in working directory, delete it
if (fs.existsSync(fullPath)) { else if (headStatus === 0 && workdirStatus !== 0) {
await fsPromises.unlink(fullPath); if (fs.existsSync(fullPath)) {
await git.remove({ await fsPromises.unlink(fullPath);
fs, await git.remove({
dir: appPath, fs,
filepath: filepath, dir: appPath,
}); filepath: filepath,
} });
} }
} }
}
// Stage all changes // Stage all changes
await git.add({ await git.add({
fs, fs,
dir: appPath, dir: appPath,
filepath: ".", filepath: ".",
}); });
// Create a revert commit // Create a revert commit
await git.commit({ await git.commit({
fs, fs,
dir: appPath, dir: appPath,
message: `Reverted all changes back to version ${previousVersionId}`, message: `Reverted all changes back to version ${previousVersionId}`,
author: await getGitAuthor(), author: await getGitAuthor(),
}); });
// Find the chat and message associated with the commit hash // Find the chat and message associated with the commit hash
const messageWithCommit = await db.query.messages.findFirst({ const messageWithCommit = await db.query.messages.findFirst({
where: eq(messages.commitHash, previousVersionId), where: eq(messages.commitHash, previousVersionId),
with: { with: {
chat: true, chat: true,
}, },
});
// If we found a message with this commit hash, delete all subsequent messages (but keep this message)
if (messageWithCommit) {
const chatId = messageWithCommit.chatId;
// Find all messages in this chat with IDs > the one with our commit hash
const messagesToDelete = await db.query.messages.findMany({
where: and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id),
),
orderBy: desc(messages.id),
}); });
// If we found a message with this commit hash, delete all subsequent messages (but keep this message) logger.log(
if (messageWithCommit) { `Deleting ${messagesToDelete.length} messages after commit ${previousVersionId} from chat ${chatId}`,
const chatId = messageWithCommit.chatId;
// Find all messages in this chat with IDs > the one with our commit hash
const messagesToDelete = await db.query.messages.findMany({
where: and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id),
),
orderBy: desc(messages.id),
});
logger.log(
`Deleting ${messagesToDelete.length} messages after commit ${previousVersionId} from chat ${chatId}`,
);
// Delete the messages
if (messagesToDelete.length > 0) {
await db
.delete(messages)
.where(
and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id),
),
);
}
}
} catch (error: any) {
logger.error(
`Error reverting to version ${previousVersionId} for app ${appId}:`,
error,
); );
throw new Error(`Failed to revert version: ${error.message}`);
// Delete the messages
if (messagesToDelete.length > 0) {
await db
.delete(messages)
.where(
and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id),
),
);
}
} }
}); });
}, },
); );
ipcMain.handle( handle(
"checkout-version", "checkout-version",
async ( async (
_, _,
...@@ -233,21 +221,12 @@ export function registerVersionHandlers() { ...@@ -233,21 +221,12 @@ export function registerVersionHandlers() {
const appPath = getDyadAppPath(app.path); const appPath = getDyadAppPath(app.path);
try { await git.checkout({
// Checkout the target commit fs,
await git.checkout({ dir: appPath,
fs, ref: versionId,
dir: appPath, force: true,
ref: versionId, });
force: true,
});
} catch (error: any) {
logger.error(
`Error checking out version ${versionId} for app ${appId}:`,
error,
);
throw new Error(`Failed to checkout version: ${error.message}`);
}
}); });
}, },
); );
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论