148 lines
3.9 KiB
TypeScript
148 lines
3.9 KiB
TypeScript
import { dialog } from "electron";
|
|
import fs from "fs/promises";
|
|
import path from "path";
|
|
import { createLoggedHandler } from "./safe_handle";
|
|
import log from "electron-log";
|
|
import { getDyadAppPath } from "../../paths/paths";
|
|
import { apps } from "@/db/schema";
|
|
import { db } from "@/db";
|
|
import { chats } from "@/db/schema";
|
|
import { eq } from "drizzle-orm";
|
|
import git from "isomorphic-git";
|
|
import { getGitAuthor } from "../utils/git_author";
|
|
import { ImportAppParams, ImportAppResult } from "../ipc_types";
|
|
|
|
const logger = log.scope("import-handlers");
|
|
const handle = createLoggedHandler(logger);
|
|
|
|
export function registerImportHandlers() {
|
|
// Handler for selecting an app folder
|
|
handle("select-app-folder", async () => {
|
|
const result = await dialog.showOpenDialog({
|
|
properties: ["openDirectory"],
|
|
title: "Select App Folder to Import",
|
|
});
|
|
|
|
if (result.canceled) {
|
|
return { path: null, name: null };
|
|
}
|
|
|
|
const selectedPath = result.filePaths[0];
|
|
const folderName = path.basename(selectedPath);
|
|
|
|
return { path: selectedPath, name: folderName };
|
|
});
|
|
|
|
// Handler for checking if AI_RULES.md exists
|
|
handle("check-ai-rules", async (_, { path: appPath }: { path: string }) => {
|
|
try {
|
|
await fs.access(path.join(appPath, "AI_RULES.md"));
|
|
return { exists: true };
|
|
} catch {
|
|
return { exists: false };
|
|
}
|
|
});
|
|
|
|
// Handler for checking if an app name is already taken
|
|
handle("check-app-name", async (_, { appName }: { appName: string }) => {
|
|
// Check filesystem
|
|
const appPath = getDyadAppPath(appName);
|
|
try {
|
|
await fs.access(appPath);
|
|
return { exists: true };
|
|
} catch {
|
|
// Path doesn't exist, continue checking database
|
|
}
|
|
|
|
// Check database
|
|
const existingApp = await db.query.apps.findFirst({
|
|
where: eq(apps.name, appName),
|
|
});
|
|
|
|
return { exists: !!existingApp };
|
|
});
|
|
|
|
// Handler for importing an app
|
|
handle(
|
|
"import-app",
|
|
async (
|
|
_,
|
|
{ path: sourcePath, appName }: ImportAppParams,
|
|
): Promise<ImportAppResult> => {
|
|
// Validate the source path exists
|
|
try {
|
|
await fs.access(sourcePath);
|
|
} catch {
|
|
throw new Error("Source folder does not exist");
|
|
}
|
|
|
|
const destPath = getDyadAppPath(appName);
|
|
|
|
// Check if the app already exists
|
|
const errorMessage = "An app with this name already exists";
|
|
try {
|
|
await fs.access(destPath);
|
|
throw new Error(errorMessage);
|
|
} catch (error: any) {
|
|
if (error.message === errorMessage) {
|
|
throw error;
|
|
}
|
|
}
|
|
// Copy the app folder to the Dyad apps directory, excluding node_modules
|
|
await fs.cp(sourcePath, destPath, {
|
|
recursive: true,
|
|
filter: (source) => !source.includes("node_modules"),
|
|
});
|
|
|
|
const isGitRepo = await fs
|
|
.access(path.join(destPath, ".git"))
|
|
.then(() => true)
|
|
.catch(() => false);
|
|
if (!isGitRepo) {
|
|
// Initialize git repo and create first commit
|
|
await git.init({
|
|
fs: fs,
|
|
dir: destPath,
|
|
defaultBranch: "main",
|
|
});
|
|
|
|
// Stage all files
|
|
await git.add({
|
|
fs: fs,
|
|
dir: destPath,
|
|
filepath: ".",
|
|
});
|
|
|
|
// Create initial commit
|
|
await git.commit({
|
|
fs: fs,
|
|
dir: destPath,
|
|
message: "Init Dyad app",
|
|
author: await getGitAuthor(),
|
|
});
|
|
}
|
|
|
|
// Create a new app
|
|
const [app] = await db
|
|
.insert(apps)
|
|
.values({
|
|
name: appName,
|
|
// Use the name as the path for now
|
|
path: appName,
|
|
})
|
|
.returning();
|
|
|
|
// Create an initial chat for this app
|
|
const [chat] = await db
|
|
.insert(chats)
|
|
.values({
|
|
appId: app.id,
|
|
})
|
|
.returning();
|
|
return { appId: app.id, chatId: chat.id };
|
|
},
|
|
);
|
|
|
|
logger.debug("Registered import IPC handlers");
|
|
}
|