mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-07 23:15:27 +02:00
refactor(patch-editor): remove repoPath dependency and streamline patch handling
- Eliminated the repoPath parameter from the PatchEditor component and related API calls, simplifying the patch management logic. - Updated the patch retrieval and saving processes to focus on filePath and content, enhancing clarity and maintainability. - Adjusted the handling of file content in the CodeEditor to ensure it retrieves the correct data, improving user experience.
This commit is contained in:
@@ -56,16 +56,10 @@ export const PatchEditor = ({ id, type, repoPath, onClose }: Props) => {
|
||||
{
|
||||
id,
|
||||
type,
|
||||
repoPath,
|
||||
filePath: selectedFile || "",
|
||||
},
|
||||
{
|
||||
enabled: !!selectedFile,
|
||||
onSuccess: (data) => {
|
||||
if (data.patchError) {
|
||||
toast.error(data.patchErrorMessage || "Failed to apply patch");
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -90,7 +84,6 @@ export const PatchEditor = ({ id, type, repoPath, onClose }: Props) => {
|
||||
saveAsPatch({
|
||||
id,
|
||||
type,
|
||||
repoPath,
|
||||
filePath: selectedFile,
|
||||
content: fileContent,
|
||||
})
|
||||
@@ -213,7 +206,7 @@ export const PatchEditor = ({ id, type, repoPath, onClose }: Props) => {
|
||||
</div>
|
||||
) : selectedFile ? (
|
||||
<CodeEditor
|
||||
value={fileData?.content || ""}
|
||||
value={fileData || ""}
|
||||
onChange={(value) => setFileContent(value || "")}
|
||||
className="h-full w-full"
|
||||
wrapperClassName="h-full"
|
||||
|
||||
@@ -186,7 +186,6 @@ export const patchRouter = createTRPCRouter({
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(["application", "compose"]),
|
||||
repoPath: z.string(),
|
||||
filePath: z.string(),
|
||||
}),
|
||||
)
|
||||
@@ -229,12 +228,10 @@ export const patchRouter = createTRPCRouter({
|
||||
input.type,
|
||||
);
|
||||
|
||||
return await readPatchRepoFile(
|
||||
input.repoPath,
|
||||
input.filePath,
|
||||
existingPatch?.enabled ? existingPatch?.content : undefined,
|
||||
serverId,
|
||||
);
|
||||
if (existingPatch) {
|
||||
return existingPatch.content;
|
||||
}
|
||||
return await readPatchRepoFile(input.id, input.type, input.filePath);
|
||||
}),
|
||||
|
||||
saveFileAsPatch: protectedProcedure
|
||||
@@ -242,14 +239,11 @@ export const patchRouter = createTRPCRouter({
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(["application", "compose"]),
|
||||
repoPath: z.string(),
|
||||
filePath: z.string(),
|
||||
content: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
let serverId: string | null = null;
|
||||
|
||||
if (input.type === "application") {
|
||||
const app = await findApplicationById(input.id);
|
||||
if (
|
||||
@@ -261,7 +255,6 @@ export const patchRouter = createTRPCRouter({
|
||||
message: "You are not authorized to access this application",
|
||||
});
|
||||
}
|
||||
serverId = app.serverId;
|
||||
} else if (input.type === "compose") {
|
||||
const compose = await findComposeById(input.id);
|
||||
if (
|
||||
@@ -273,7 +266,6 @@ export const patchRouter = createTRPCRouter({
|
||||
message: "You are not authorized to access this compose",
|
||||
});
|
||||
}
|
||||
serverId = compose.serverId;
|
||||
} else {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
@@ -281,14 +273,24 @@ export const patchRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
|
||||
const newPatch = await createPatch({
|
||||
filePath: input.filePath,
|
||||
content: input.content,
|
||||
applicationId: input.type === "application" ? input.id : undefined,
|
||||
composeId: input.type === "compose" ? input.id : undefined,
|
||||
});
|
||||
const existingPatch = await findPatchByFilePath(
|
||||
input.filePath,
|
||||
input.id,
|
||||
input.type,
|
||||
);
|
||||
|
||||
return newPatch;
|
||||
if (!existingPatch) {
|
||||
const newPatch = await createPatch({
|
||||
filePath: input.filePath,
|
||||
content: input.content,
|
||||
applicationId: input.type === "application" ? input.id : undefined,
|
||||
composeId: input.type === "compose" ? input.id : undefined,
|
||||
});
|
||||
} else {
|
||||
return await updatePatch(existingPatch.patchId, {
|
||||
content: input.content,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
// Cleanup
|
||||
|
||||
@@ -144,107 +144,39 @@ export const readPatchRepoDirectory = async (
|
||||
return root;
|
||||
};
|
||||
|
||||
interface ReadFileResult {
|
||||
content: string;
|
||||
patchError?: boolean;
|
||||
patchErrorMessage?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file content from patch repo, optionally with patch applied
|
||||
*/
|
||||
export const readPatchRepoFile = async (
|
||||
repoPath: string,
|
||||
id: string,
|
||||
type: "application" | "compose",
|
||||
filePath: string,
|
||||
patchContent?: string,
|
||||
serverId?: string | null,
|
||||
): Promise<ReadFileResult> => {
|
||||
) => {
|
||||
let serverId: string | null = null;
|
||||
|
||||
if (type === "application") {
|
||||
const application = await findApplicationById(id);
|
||||
serverId = application.buildServerId || application.serverId;
|
||||
} else {
|
||||
const compose = await findComposeById(id);
|
||||
serverId = compose.serverId;
|
||||
}
|
||||
const { PATCH_REPOS_PATH } = paths(!!serverId);
|
||||
|
||||
const application =
|
||||
type === "application"
|
||||
? await findApplicationById(id)
|
||||
: await findComposeById(id);
|
||||
|
||||
const repoPath = join(PATCH_REPOS_PATH, type, application.appName);
|
||||
const fullPath = join(repoPath, filePath);
|
||||
|
||||
// Read original file
|
||||
const command = `cat "${fullPath}" 2>/dev/null || echo "__FILE_NOT_FOUND__"`;
|
||||
const command = `cat "${fullPath}"`;
|
||||
|
||||
let content: string;
|
||||
try {
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, command);
|
||||
content = result.stdout;
|
||||
} else {
|
||||
const result = await execAsync(command);
|
||||
content = result.stdout;
|
||||
}
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: `File not found: ${filePath}`,
|
||||
});
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, command);
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
if (content.trim() === "__FILE_NOT_FOUND__") {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: `File not found: ${filePath}`,
|
||||
});
|
||||
}
|
||||
|
||||
// If no patch, return original content
|
||||
if (!patchContent) {
|
||||
return { content };
|
||||
}
|
||||
|
||||
// Try to apply patch
|
||||
const tempDir = `/tmp/patch_apply_${Date.now()}`;
|
||||
const encodedContent = Buffer.from(content).toString("base64");
|
||||
const encodedPatch = Buffer.from(patchContent).toString("base64");
|
||||
|
||||
// We need to recreate the file structure for git apply to work
|
||||
// git diff usually uses paths relative to repo root
|
||||
const applyCommand = `
|
||||
set -e;
|
||||
mkdir -p "${tempDir}";
|
||||
cd "${tempDir}";
|
||||
git init -q;
|
||||
# Create file with correct path
|
||||
mkdir -p "$(dirname "${filePath}")";
|
||||
echo "${encodedContent}" | base64 -d > "${filePath}";
|
||||
# Save patch
|
||||
echo "${encodedPatch}" | base64 -d > "patch.diff";
|
||||
# Apply patch
|
||||
git apply --ignore-space-change --ignore-whitespace patch.diff;
|
||||
# Read result
|
||||
cat "${filePath}";
|
||||
rm -rf "${tempDir}";
|
||||
`;
|
||||
|
||||
try {
|
||||
let patchedContent: string;
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, applyCommand);
|
||||
patchedContent = result.stdout;
|
||||
} else {
|
||||
const result = await execAsync(applyCommand);
|
||||
patchedContent = result.stdout;
|
||||
}
|
||||
return { content: patchedContent };
|
||||
} catch (error) {
|
||||
// Patch failed - return original content with error
|
||||
const cleanupCommand = `rm -rf "${tempDir}" 2>/dev/null || true`;
|
||||
try {
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, cleanupCommand);
|
||||
} else {
|
||||
await execAsync(cleanupCommand);
|
||||
}
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
|
||||
return {
|
||||
content,
|
||||
patchError: true,
|
||||
patchErrorMessage: `Failed to apply patch: ${error}`,
|
||||
};
|
||||
}
|
||||
const result = await execAsync(command);
|
||||
return result.stdout;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user