mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: ssh keys filesystel
This commit is contained in:
@@ -37,7 +37,7 @@ const GitProviderSchema = z.object({
|
||||
}),
|
||||
branch: z.string().min(1, "Branch required"),
|
||||
buildPath: z.string().min(1, "Build Path required"),
|
||||
sshKey: z.string(),
|
||||
sshKey: z.string().optional(),
|
||||
});
|
||||
|
||||
type GitProvider = z.infer<typeof GitProviderSchema>;
|
||||
@@ -63,7 +63,7 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
|
||||
branch: "",
|
||||
buildPath: "/",
|
||||
repositoryURL: "",
|
||||
sshKey: "",
|
||||
sshKey: undefined,
|
||||
},
|
||||
resolver: zodResolver(GitProviderSchema),
|
||||
});
|
||||
@@ -71,7 +71,7 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
form.reset({
|
||||
sshKey: data.customGitSSHKeyId || "",
|
||||
sshKey: data.customGitSSHKeyId || undefined,
|
||||
branch: data.customGitBranch || "",
|
||||
buildPath: data.customGitBuildPath || "/",
|
||||
repositoryURL: data.customGitUrl || "",
|
||||
@@ -84,7 +84,7 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
|
||||
customGitBranch: values.branch,
|
||||
customGitBuildPath: values.buildPath,
|
||||
customGitUrl: values.repositoryURL,
|
||||
customGitSSHKeyId: values.sshKey,
|
||||
customGitSSHKeyId: values.sshKey === "none" ? null : values.sshKey,
|
||||
applicationId,
|
||||
})
|
||||
.then(async () => {
|
||||
@@ -149,6 +149,7 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
|
||||
{sshKey.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
<SelectLabel>Keys ({sshKeys?.length})</SelectLabel>
|
||||
</SelectGroup>
|
||||
<SelectSeparator />
|
||||
|
||||
@@ -33,8 +33,8 @@ import {
|
||||
} from "@/server/utils/filesystem/directory";
|
||||
import {
|
||||
generateSSHKey,
|
||||
readRSAFile,
|
||||
removeRSAFiles,
|
||||
readSSHPublicKey,
|
||||
removeSSHKey,
|
||||
} from "@/server/utils/filesystem/ssh";
|
||||
import {
|
||||
readConfig,
|
||||
@@ -130,7 +130,7 @@ export const applicationRouter = createTRPCRouter({
|
||||
async () => await removeMonitoringDirectory(application?.appName),
|
||||
async () => await removeTraefikConfig(application?.appName),
|
||||
async () => await removeService(application?.appName),
|
||||
async () => await removeRSAFiles(application?.appName),
|
||||
async () => await removeSSHKey(application?.appName),
|
||||
];
|
||||
|
||||
for (const operation of cleanupOperations) {
|
||||
@@ -248,7 +248,7 @@ export const applicationRouter = createTRPCRouter({
|
||||
const application = await findApplicationById(input.applicationId);
|
||||
try {
|
||||
await generateSSHKey(application.appName);
|
||||
const file = await readRSAFile(application.appName);
|
||||
const file = await readSSHPublicKey(application.appName);
|
||||
|
||||
// await updateApplication(input.applicationId, {
|
||||
// customGitSSHKey: file,
|
||||
@@ -261,7 +261,7 @@ export const applicationRouter = createTRPCRouter({
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input }) => {
|
||||
const application = await findApplicationById(input.applicationId);
|
||||
await removeRSAFiles(application.appName);
|
||||
await removeSSHKey(application.appName);
|
||||
// await updateApplication(input.applicationId, {
|
||||
// customGitSSHKey: null,
|
||||
// });
|
||||
|
||||
@@ -18,8 +18,8 @@ import { randomizeComposeFile } from "@/server/utils/docker/compose";
|
||||
import { removeComposeDirectory } from "@/server/utils/filesystem/directory";
|
||||
import {
|
||||
generateSSHKey,
|
||||
readRSAFile,
|
||||
removeRSAFiles,
|
||||
readSSHPublicKey,
|
||||
removeSSHKey,
|
||||
} from "@/server/utils/filesystem/ssh";
|
||||
import { templates } from "@/templates/templates";
|
||||
import type { TemplatesKeys } from "@/templates/types/templates-data.type";
|
||||
@@ -102,7 +102,7 @@ export const composeRouter = createTRPCRouter({
|
||||
async () => await removeCompose(composeResult),
|
||||
async () => await removeDeploymentsByComposeId(composeResult),
|
||||
async () => await removeComposeDirectory(composeResult.appName),
|
||||
async () => await removeRSAFiles(composeResult.appName),
|
||||
async () => await removeSSHKey(composeResult.appName),
|
||||
];
|
||||
|
||||
for (const operation of cleanupOperations) {
|
||||
@@ -187,7 +187,7 @@ export const composeRouter = createTRPCRouter({
|
||||
const compose = await findComposeById(input.composeId);
|
||||
try {
|
||||
await generateSSHKey(compose.appName);
|
||||
const file = await readRSAFile(compose.appName);
|
||||
const file = await readSSHPublicKey(compose.appName);
|
||||
|
||||
await updateCompose(input.composeId, {
|
||||
customGitSSHKey: file,
|
||||
@@ -208,7 +208,7 @@ export const composeRouter = createTRPCRouter({
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input }) => {
|
||||
const compose = await findComposeById(input.composeId);
|
||||
await removeRSAFiles(compose.appName);
|
||||
await removeSSHKey(compose.appName);
|
||||
await updateCompose(input.composeId, {
|
||||
customGitSSHKey: null,
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
type apiUpdateSshKey,
|
||||
sshKeys,
|
||||
} from "@/server/db/schema";
|
||||
import { removeSSHKey, saveSSHKey } from "@/server/utils/filesystem/ssh";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
@@ -20,6 +21,11 @@ export const createSshKey = async ({
|
||||
.returning()
|
||||
.then((response) => response[0])
|
||||
.catch((e) => console.error(e));
|
||||
|
||||
if (sshKey) {
|
||||
saveSSHKey(sshKey.sshKeyId, sshKey.publicKey, privateKey);
|
||||
}
|
||||
|
||||
if (!sshKey) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
@@ -38,6 +44,8 @@ export const removeSSHKeyById = async (
|
||||
.where(eq(sshKeys.sshKeyId, sshKeyId))
|
||||
.returning();
|
||||
|
||||
removeSSHKey(sshKeyId);
|
||||
|
||||
return result[0];
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,35 @@ import * as path from "node:path";
|
||||
import { SSH_PATH } from "@/server/constants";
|
||||
import { spawnAsync } from "../process/spawnAsync";
|
||||
|
||||
export const generateSSHKey = async (appName: string) => {
|
||||
export const saveSSHKey = async (
|
||||
id: string,
|
||||
publicKey: string,
|
||||
privateKey: string,
|
||||
) => {
|
||||
const applicationDirectory = SSH_PATH;
|
||||
|
||||
const privateKeyPath = path.join(applicationDirectory, `${id}_rsa`);
|
||||
const publicKeyPath = path.join(applicationDirectory, `${id}_rsa.pub`);
|
||||
|
||||
const privateKeyStream = fs.createWriteStream(privateKeyPath, {
|
||||
mode: 0o400,
|
||||
});
|
||||
privateKeyStream.write(privateKey);
|
||||
privateKeyStream.end();
|
||||
|
||||
const publicKeyStream = fs.createWriteStream(publicKeyPath, { mode: 0o400 });
|
||||
publicKeyStream.write(publicKey);
|
||||
publicKeyStream.end();
|
||||
};
|
||||
|
||||
export const generateSSHKey = async (id: string) => {
|
||||
const applicationDirectory = SSH_PATH;
|
||||
|
||||
if (!fs.existsSync(applicationDirectory)) {
|
||||
fs.mkdirSync(applicationDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
const keyPath = path.join(applicationDirectory, `${appName}_rsa`);
|
||||
const keyPath = path.join(applicationDirectory, `${id}_rsa`);
|
||||
|
||||
if (fs.existsSync(`${keyPath}`)) {
|
||||
fs.unlinkSync(`${keyPath}`);
|
||||
@@ -37,12 +58,12 @@ export const generateSSHKey = async (appName: string) => {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
export const readRSAFile = async (appName: string) => {
|
||||
export const readSSHPublicKey = async (id: string) => {
|
||||
try {
|
||||
if (!fs.existsSync(SSH_PATH)) {
|
||||
fs.mkdirSync(SSH_PATH, { recursive: true });
|
||||
}
|
||||
const keyPath = path.join(SSH_PATH, `${appName}_rsa.pub`);
|
||||
const keyPath = path.join(SSH_PATH, `${id}_rsa.pub`);
|
||||
const data = fs.readFileSync(keyPath, { encoding: "utf-8" });
|
||||
return data;
|
||||
} catch (error) {
|
||||
@@ -50,10 +71,10 @@ export const readRSAFile = async (appName: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const removeRSAFiles = async (appName: string) => {
|
||||
export const removeSSHKey = async (id: string) => {
|
||||
try {
|
||||
const publicKeyPath = path.join(SSH_PATH, `${appName}_rsa.pub`);
|
||||
const privateKeyPath = path.join(SSH_PATH, `${appName}_rsa`);
|
||||
const publicKeyPath = path.join(SSH_PATH, `${id}_rsa.pub`);
|
||||
const privateKeyPath = path.join(SSH_PATH, `${id}_rsa`);
|
||||
await fs.promises.unlink(publicKeyPath);
|
||||
await fs.promises.unlink(privateKeyPath);
|
||||
} catch (error) {
|
||||
|
||||
@@ -11,12 +11,12 @@ export const cloneGitRepository = async (
|
||||
appName: string;
|
||||
customGitUrl?: string | null;
|
||||
customGitBranch?: string | null;
|
||||
customGitSSHKey?: string | null;
|
||||
customGitSSHKeyId?: string | null;
|
||||
},
|
||||
logPath: string,
|
||||
isCompose = false,
|
||||
) => {
|
||||
const { appName, customGitUrl, customGitBranch, customGitSSHKey } = entity;
|
||||
const { appName, customGitUrl, customGitBranch, customGitSSHKeyId } = entity;
|
||||
|
||||
if (!customGitUrl || !customGitBranch) {
|
||||
throw new TRPCError({
|
||||
@@ -26,7 +26,7 @@ export const cloneGitRepository = async (
|
||||
}
|
||||
|
||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||
const keyPath = path.join(SSH_PATH, `${appName}_rsa`);
|
||||
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
|
||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||
const outputPath = join(basePath, appName, "code");
|
||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
||||
@@ -39,7 +39,7 @@ export const cloneGitRepository = async (
|
||||
writeStream.write(
|
||||
`\nCloning Repo Custom ${customGitUrl} to ${outputPath}: ✅\n`,
|
||||
);
|
||||
|
||||
console.log(customGitSSHKeyId);
|
||||
await spawnAsync(
|
||||
"git",
|
||||
[
|
||||
@@ -60,7 +60,7 @@ export const cloneGitRepository = async (
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
...(customGitSSHKey && {
|
||||
...(customGitSSHKeyId && {
|
||||
GIT_SSH_COMMAND: `ssh -i ${keyPath} -o UserKnownHostsFile=${knownHostsPath}`,
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user