feat: generate ssh key

This commit is contained in:
Lorenzo Migliorero
2024-07-25 22:52:39 +02:00
parent 71555a15f8
commit a2eff67d44
3 changed files with 63 additions and 16 deletions

View File

@@ -42,6 +42,8 @@ export const AddSSHKey = ({ children }: Props) => {
const { mutateAsync, isError, error, isLoading } = const { mutateAsync, isError, error, isLoading } =
api.sshKey.create.useMutation(); api.sshKey.create.useMutation();
const generateMutation = api.sshKey.generate.useMutation();
const form = useForm<SSHKey>({ const form = useForm<SSHKey>({
resolver: zodResolver(sshKeyCreate), resolver: zodResolver(sshKeyCreate),
}); });
@@ -71,8 +73,31 @@ export const AddSSHKey = ({ children }: Props) => {
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle>SSH Key</DialogTitle> <DialogTitle>SSH Key</DialogTitle>
<DialogDescription> <DialogDescription className="space-y-4">
In this section you can add an SSH key <div>
In this section you can add one of your keys or generate a new
one.
</div>
<Button
variant={"secondary"}
isLoading={generateMutation.isLoading}
className="max-sm:w-full"
onClick={async () => {
await generateMutation
.mutateAsync()
.then(async (data) => {
toast.success("SSH Key Generated");
form.setValue("privateKey", data.privateKey);
form.setValue("publicKey", data.publicKey);
})
.catch(() => {
toast.error("Error to generate the SSH Key");
});
}}
type="button"
>
Generate SSH Key
</Button>
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>} {isError && <AlertBlock type="error">{error?.message}</AlertBlock>}

View File

@@ -10,6 +10,7 @@ import {
apiRemoveSshKey, apiRemoveSshKey,
apiUpdateSshKey, apiUpdateSshKey,
} from "@/server/db/schema"; } from "@/server/db/schema";
import { generateSSHKey } from "@/server/utils/filesystem/ssh";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { import {
createSshKey, createSshKey,
@@ -49,6 +50,9 @@ export const sshRouter = createTRPCRouter({
all: adminProcedure.query(async () => { all: adminProcedure.query(async () => {
return await db.query.sshKeys.findMany({}); return await db.query.sshKeys.findMany({});
}), }),
generate: protectedProcedure.mutation(async () => {
return await generateSSHKey();
}),
update: adminProcedure.input(apiUpdateSshKey).mutation(async ({ input }) => { update: adminProcedure.input(apiUpdateSshKey).mutation(async ({ input }) => {
try { try {
return await updateSSHKeyById(input); return await updateSSHKeyById(input);

View File

@@ -3,6 +3,29 @@ import * as path from "node:path";
import { SSH_PATH } from "@/server/constants"; import { SSH_PATH } from "@/server/constants";
import { spawnAsync } from "../process/spawnAsync"; import { spawnAsync } from "../process/spawnAsync";
const readSSHKey = async (id: string) => {
try {
if (!fs.existsSync(SSH_PATH)) {
fs.mkdirSync(SSH_PATH, { recursive: true });
}
return {
privateKey: fs
.readFileSync(path.join(SSH_PATH, `${id}_rsa`), {
encoding: "utf-8",
})
.trim(),
publicKey: fs
.readFileSync(path.join(SSH_PATH, `${id}_rsa.pub`), {
encoding: "utf-8",
})
.trim(),
};
} catch (error) {
throw error;
}
};
export const saveSSHKey = async ( export const saveSSHKey = async (
id: string, id: string,
publicKey: string, publicKey: string,
@@ -24,21 +47,23 @@ export const saveSSHKey = async (
publicKeyStream.end(); publicKeyStream.end();
}; };
export const generateSSHKey = async (id: string) => { export const generateSSHKey = async () => {
const applicationDirectory = SSH_PATH; const applicationDirectory = SSH_PATH;
if (!fs.existsSync(applicationDirectory)) { if (!fs.existsSync(applicationDirectory)) {
fs.mkdirSync(applicationDirectory, { recursive: true }); fs.mkdirSync(applicationDirectory, { recursive: true });
} }
const keyPath = path.join(applicationDirectory, `${id}_rsa`); const keyPath = path.join(applicationDirectory, "temp_rsa");
if (fs.existsSync(`${keyPath}`)) { if (fs.existsSync(`${keyPath}`)) {
fs.unlinkSync(`${keyPath}`); fs.unlinkSync(`${keyPath}`);
} }
if (fs.existsSync(`${keyPath}.pub`)) { if (fs.existsSync(`${keyPath}.pub`)) {
fs.unlinkSync(`${keyPath}.pub`); fs.unlinkSync(`${keyPath}.pub`);
} }
const args = [ const args = [
"-t", "-t",
"rsa", "rsa",
@@ -46,25 +71,18 @@ export const generateSSHKey = async (id: string) => {
"4096", "4096",
"-C", "-C",
"dokploy", "dokploy",
"-m",
"PEM",
"-f", "-f",
keyPath, keyPath,
"-N", "-N",
"", "",
]; ];
try { try {
await spawnAsync("ssh-keygen", args); await spawnAsync("ssh-keygen", args);
return keyPath; const data = await readSSHKey("temp");
} catch (error) { await removeSSHKey("temp");
throw error;
}
};
export const readSSHPublicKey = async (id: string) => {
try {
if (!fs.existsSync(SSH_PATH)) {
fs.mkdirSync(SSH_PATH, { recursive: true });
}
const keyPath = path.join(SSH_PATH, `${id}_rsa.pub`);
const data = fs.readFileSync(keyPath, { encoding: "utf-8" });
return data; return data;
} catch (error) { } catch (error) {
throw error; throw error;