mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: add ssh key
This commit is contained in:
1
apps/dokploy/drizzle/0038_mushy_blindfold.sql
Normal file
1
apps/dokploy/drizzle/0038_mushy_blindfold.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "ssh-key" ADD COLUMN "privateKey" text DEFAULT '' NOT NULL;
|
||||||
3830
apps/dokploy/drizzle/meta/0038_snapshot.json
Normal file
3830
apps/dokploy/drizzle/meta/0038_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -267,6 +267,13 @@
|
|||||||
"when": 1726988289562,
|
"when": 1726988289562,
|
||||||
"tag": "0037_legal_namor",
|
"tag": "0037_legal_namor",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 38,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1727903587684,
|
||||||
|
"tag": "0038_mushy_blindfold",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ void app.prepare().then(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
await migration();
|
// await migration(); //TODO: Fix migration
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen(PORT);
|
server.listen(PORT);
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import type http from "node:http";
|
|||||||
import { spawn } from "node-pty";
|
import { spawn } from "node-pty";
|
||||||
import { Client } from "ssh2";
|
import { Client } from "ssh2";
|
||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import {
|
import { findServerById, validateWebSocketRequest } from "@dokploy/builders";
|
||||||
findServerById,
|
|
||||||
readSSHKey,
|
|
||||||
validateWebSocketRequest,
|
|
||||||
} from "@dokploy/builders";
|
|
||||||
import { getShell } from "./utils";
|
import { getShell } from "./utils";
|
||||||
|
|
||||||
export const setupDockerContainerLogsWebSocketServer = (
|
export const setupDockerContainerLogsWebSocketServer = (
|
||||||
@@ -52,7 +48,6 @@ export const setupDockerContainerLogsWebSocketServer = (
|
|||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
|
|
||||||
if (!server.sshKeyId) return;
|
if (!server.sshKeyId) return;
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
new Promise<void>((resolve, reject) => {
|
new Promise<void>((resolve, reject) => {
|
||||||
client
|
client
|
||||||
@@ -84,7 +79,7 @@ export const setupDockerContainerLogsWebSocketServer = (
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import type http from "node:http";
|
|||||||
import { spawn } from "node-pty";
|
import { spawn } from "node-pty";
|
||||||
import { Client } from "ssh2";
|
import { Client } from "ssh2";
|
||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import {
|
import { findServerById, validateWebSocketRequest } from "@dokploy/builders";
|
||||||
findServerById,
|
|
||||||
readSSHKey,
|
|
||||||
validateWebSocketRequest,
|
|
||||||
} from "@dokploy/builders";
|
|
||||||
import { getShell } from "./utils";
|
import { getShell } from "./utils";
|
||||||
|
|
||||||
export const setupDockerContainerTerminalWebSocketServer = (
|
export const setupDockerContainerTerminalWebSocketServer = (
|
||||||
@@ -53,7 +49,6 @@ export const setupDockerContainerTerminalWebSocketServer = (
|
|||||||
if (!server.sshKeyId)
|
if (!server.sshKeyId)
|
||||||
throw new Error("No SSH key available for this server");
|
throw new Error("No SSH key available for this server");
|
||||||
|
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
const conn = new Client();
|
const conn = new Client();
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
@@ -109,7 +104,7 @@ export const setupDockerContainerTerminalWebSocketServer = (
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import { spawn } from "node:child_process";
|
|||||||
import type http from "node:http";
|
import type http from "node:http";
|
||||||
import { Client } from "ssh2";
|
import { Client } from "ssh2";
|
||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import {
|
import { findServerById, validateWebSocketRequest } from "@dokploy/builders";
|
||||||
findServerById,
|
|
||||||
readSSHKey,
|
|
||||||
validateWebSocketRequest,
|
|
||||||
} from "@dokploy/builders";
|
|
||||||
|
|
||||||
export const setupDeploymentLogsWebSocketServer = (
|
export const setupDeploymentLogsWebSocketServer = (
|
||||||
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
|
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
|
||||||
@@ -51,7 +47,6 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
|
|
||||||
if (!server.sshKeyId) return;
|
if (!server.sshKeyId) return;
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
new Promise<void>((resolve, reject) => {
|
new Promise<void>((resolve, reject) => {
|
||||||
client
|
client
|
||||||
@@ -83,7 +78,7 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"outDir": "dist/",
|
"outDir": "dist/",
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"isolatedModules": false,
|
"isolatedModules": false,
|
||||||
"noEmit": false,
|
"noEmit": false,
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "./server/**/*"]
|
"include": ["next-env.d.ts", "./server/**/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,12 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "dokploy",
|
"name": "dokploy",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"workspaces": ["apps/*", "packages/*"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dokploy:setup": "pnpm --filter=dokploy run setup",
|
"dokploy:setup": "pnpm --filter=dokploy run setup",
|
||||||
"dokploy:dev": "pnpm --filter=dokploy run dev",
|
"dokploy:dev": "pnpm --filter=dokploy run dev",
|
||||||
"dokploy:build": "pnpm --filter=dokploy run build",
|
"dokploy:build": "pnpm --filter=dokploy run build",
|
||||||
"dokploy:start": "pnpm --filter=dokploy run start",
|
"dokploy:start": "pnpm --filter=dokploy run start",
|
||||||
"test": "pnpm --filter=dokploy run test",
|
"test": "pnpm --filter=dokploy run test",
|
||||||
|
"builder:dev": "pnpm --filter=builders run dev",
|
||||||
"docker:build:canary": "./apps/dokploy/docker/build.sh canary",
|
"docker:build:canary": "./apps/dokploy/docker/build.sh canary",
|
||||||
"docs:dev": "pnpm --filter=docs run dev",
|
"docs:dev": "pnpm --filter=docs run dev",
|
||||||
"docs:build": "pnpm --filter=docs run build",
|
"docs:build": "pnpm --filter=docs run build",
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import path from "node:path";
|
|||||||
build({
|
build({
|
||||||
entryPoints: ["./src/**/*.ts", "./src/**/*.tsx"], // Punto de entrada principal de tu aplicación
|
entryPoints: ["./src/**/*.ts", "./src/**/*.tsx"], // Punto de entrada principal de tu aplicación
|
||||||
outdir: "dist",
|
outdir: "dist",
|
||||||
bundle: false, // Cambia a true si deseas bundlear tu código
|
|
||||||
platform: "node",
|
platform: "node",
|
||||||
format: "esm",
|
format: "esm",
|
||||||
target: ["esnext"],
|
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
tsconfig: "./tsconfig.server.json",
|
tsconfig: "./tsconfig.server.json",
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsup --config ./tsup.ts --watch",
|
"dev": "tsc --project tsconfig.server.json --watch && tsc-alias -p tsconfig.server.json",
|
||||||
"build": "tsc --project tsconfig.server.json && tsc-alias -p tsconfig.server.json",
|
"build": "rm -rf ./dist && tsc --project tsconfig.server.json && tsc-alias -p tsconfig.server.json",
|
||||||
"esbuild": "tsx ./esbuild.config.ts",
|
"esbuild": "tsx --watch ./esbuild.config.ts & tsc-alias -p tsconfig.server.json --watch",
|
||||||
"build:types": "tsc --emitDeclarationOnly --experimenta-dts"
|
"build:types": "tsc --emitDeclarationOnly --experimenta-dts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const sshKeys = pgTable("ssh-key", {
|
|||||||
.notNull()
|
.notNull()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => nanoid()),
|
.$defaultFn(() => nanoid()),
|
||||||
|
privateKey: text("privateKey").notNull().default(""),
|
||||||
publicKey: text("publicKey").notNull(),
|
publicKey: text("publicKey").notNull(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
description: text("description"),
|
description: text("description"),
|
||||||
@@ -37,6 +38,7 @@ export const apiCreateSshKey = createSchema
|
|||||||
.pick({
|
.pick({
|
||||||
name: true,
|
name: true,
|
||||||
description: true,
|
description: true,
|
||||||
|
privateKey: true,
|
||||||
publicKey: true,
|
publicKey: true,
|
||||||
})
|
})
|
||||||
.merge(sshKeyCreate.pick({ privateKey: true }));
|
.merge(sshKeyCreate.pick({ privateKey: true }));
|
||||||
|
|||||||
@@ -6,14 +6,10 @@ import {
|
|||||||
type apiUpdateSshKey,
|
type apiUpdateSshKey,
|
||||||
sshKeys,
|
sshKeys,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { removeSSHKey, saveSSHKey } from "@/server/utils/filesystem/ssh";
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
export const createSshKey = async ({
|
export const createSshKey = async (input: typeof apiCreateSshKey._type) => {
|
||||||
privateKey,
|
|
||||||
...input
|
|
||||||
}: typeof apiCreateSshKey._type) => {
|
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const sshKey = await tx
|
const sshKey = await tx
|
||||||
.insert(sshKeys)
|
.insert(sshKeys)
|
||||||
@@ -22,10 +18,6 @@ export const createSshKey = async ({
|
|||||||
.then((response) => response[0])
|
.then((response) => response[0])
|
||||||
.catch((e) => console.error(e));
|
.catch((e) => console.error(e));
|
||||||
|
|
||||||
if (sshKey) {
|
|
||||||
saveSSHKey(sshKey.sshKeyId, sshKey.publicKey, privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sshKey) {
|
if (!sshKey) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "BAD_REQUEST",
|
code: "BAD_REQUEST",
|
||||||
@@ -44,8 +36,6 @@ export const removeSSHKeyById = async (
|
|||||||
.where(eq(sshKeys.sshKeyId, sshKeyId))
|
.where(eq(sshKeys.sshKeyId, sshKeyId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
removeSSHKey(sshKeyId);
|
|
||||||
|
|
||||||
return result[0];
|
return result[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
} from "@/server/setup/traefik-setup";
|
} from "@/server/setup/traefik-setup";
|
||||||
import { Client } from "ssh2";
|
import { Client } from "ssh2";
|
||||||
import { recreateDirectory } from "../utils/filesystem/directory";
|
import { recreateDirectory } from "../utils/filesystem/directory";
|
||||||
import { readSSHKey } from "../utils/filesystem/ssh";
|
|
||||||
|
|
||||||
import slug from "slugify";
|
import slug from "slugify";
|
||||||
|
|
||||||
@@ -70,13 +69,7 @@ const installRequirements = async (serverId: string, logPath: string) => {
|
|||||||
writeStream.close();
|
writeStream.close();
|
||||||
throw new Error("No SSH Key found");
|
throw new Error("No SSH Key found");
|
||||||
}
|
}
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
|
|
||||||
if (!keys.privateKey) {
|
|
||||||
writeStream.write("❌ No SSH Key found");
|
|
||||||
writeStream.close();
|
|
||||||
throw new Error("No SSH Key found");
|
|
||||||
}
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
client
|
client
|
||||||
.once("ready", () => {
|
.once("ready", () => {
|
||||||
@@ -142,7 +135,7 @@ const installRequirements = async (serverId: string, logPath: string) => {
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
recreateDirectory,
|
recreateDirectory,
|
||||||
recreateDirectoryRemote,
|
recreateDirectoryRemote,
|
||||||
} from "../filesystem/directory";
|
} from "../filesystem/directory";
|
||||||
import { readSSHKey } from "../filesystem/ssh";
|
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
|
|
||||||
export const unzipDrop = async (zipFile: File, application: Application) => {
|
export const unzipDrop = async (zipFile: File, application: Application) => {
|
||||||
@@ -90,7 +89,6 @@ const getSFTPConnection = async (serverId: string): Promise<SFTPWrapper> => {
|
|||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
if (!server.sshKeyId) throw new Error("No SSH key available for this server");
|
if (!server.sshKeyId) throw new Error("No SSH key available for this server");
|
||||||
|
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const conn = new Client();
|
const conn = new Client();
|
||||||
conn
|
conn
|
||||||
@@ -104,7 +102,7 @@ const getSFTPConnection = async (serverId: string): Promise<SFTPWrapper> => {
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,99 +1,26 @@
|
|||||||
import * as fs from "node:fs";
|
import * as ssh2 from "ssh2";
|
||||||
import * as path from "node:path";
|
|
||||||
import { paths } from "@/server/constants";
|
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
|
||||||
|
|
||||||
export const readSSHKey = async (id: string) => {
|
export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => {
|
||||||
const { SSH_PATH } = paths();
|
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(SSH_PATH)) {
|
if (type === "rsa") {
|
||||||
fs.mkdirSync(SSH_PATH, { recursive: true });
|
const keys = ssh2.utils.generateKeyPairSync("rsa", {
|
||||||
|
bits: 4096,
|
||||||
|
comment: "dokploy",
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
privateKey: keys.private,
|
||||||
|
publicKey: keys.public,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
const keys = ssh2.utils.generateKeyPairSync("ed25519", {
|
||||||
|
comment: "dokploy",
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
privateKey: fs.readFileSync(path.join(SSH_PATH, `${id}_rsa`), {
|
privateKey: keys.private,
|
||||||
encoding: "utf-8",
|
publicKey: keys.public,
|
||||||
}),
|
|
||||||
publicKey: fs.readFileSync(path.join(SSH_PATH, `${id}_rsa.pub`), {
|
|
||||||
encoding: "utf-8",
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveSSHKey = async (
|
|
||||||
id: string,
|
|
||||||
publicKey: string,
|
|
||||||
privateKey: string,
|
|
||||||
) => {
|
|
||||||
const { SSH_PATH } = paths();
|
|
||||||
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: 0o600,
|
|
||||||
});
|
|
||||||
privateKeyStream.write(privateKey);
|
|
||||||
privateKeyStream.end();
|
|
||||||
|
|
||||||
fs.writeFileSync(publicKeyPath, publicKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => {
|
|
||||||
const { SSH_PATH } = paths();
|
|
||||||
const applicationDirectory = SSH_PATH;
|
|
||||||
|
|
||||||
if (!fs.existsSync(applicationDirectory)) {
|
|
||||||
fs.mkdirSync(applicationDirectory, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyPath = path.join(applicationDirectory, "temp_rsa");
|
|
||||||
|
|
||||||
if (fs.existsSync(`${keyPath}`)) {
|
|
||||||
fs.unlinkSync(`${keyPath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs.existsSync(`${keyPath}.pub`)) {
|
|
||||||
fs.unlinkSync(`${keyPath}.pub`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = [
|
|
||||||
"-t",
|
|
||||||
type,
|
|
||||||
"-b",
|
|
||||||
"4096",
|
|
||||||
"-C",
|
|
||||||
"dokploy",
|
|
||||||
"-m",
|
|
||||||
"PEM",
|
|
||||||
"-f",
|
|
||||||
keyPath,
|
|
||||||
"-N",
|
|
||||||
"",
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
await spawnAsync("ssh-keygen", args);
|
|
||||||
const data = await readSSHKey("temp");
|
|
||||||
await removeSSHKey("temp");
|
|
||||||
return data;
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeSSHKey = async (id: string) => {
|
|
||||||
try {
|
|
||||||
const { SSH_PATH } = paths();
|
|
||||||
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) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { exec } from "node:child_process";
|
|||||||
import util from "node:util";
|
import util from "node:util";
|
||||||
import { findServerById } from "@/server/services/server";
|
import { findServerById } from "@/server/services/server";
|
||||||
import { Client } from "ssh2";
|
import { Client } from "ssh2";
|
||||||
import { readSSHKey } from "../filesystem/ssh";
|
|
||||||
export const execAsync = util.promisify(exec);
|
export const execAsync = util.promisify(exec);
|
||||||
|
|
||||||
export const execAsyncRemote = async (
|
export const execAsyncRemote = async (
|
||||||
@@ -13,8 +12,6 @@ export const execAsyncRemote = async (
|
|||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
if (!server.sshKeyId) throw new Error("No SSH key available for this server");
|
if (!server.sshKeyId) throw new Error("No SSH key available for this server");
|
||||||
|
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
|
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -62,7 +59,7 @@ export const execAsyncRemote = async (
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { findServerById } from "@/server/services/server";
|
import { findServerById } from "@/server/services/server";
|
||||||
import { docker } from "@/server/constants";
|
import { docker } from "@/server/constants";
|
||||||
import Dockerode from "dockerode";
|
import Dockerode from "dockerode";
|
||||||
import { readSSHKey } from "../filesystem/ssh";
|
|
||||||
|
|
||||||
export const getRemoteDocker = async (serverId?: string | null) => {
|
export const getRemoteDocker = async (serverId?: string | null) => {
|
||||||
if (!serverId) return docker;
|
if (!serverId) return docker;
|
||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
if (!server.sshKeyId) return docker;
|
if (!server.sshKeyId) return docker;
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
const dockerode = new Dockerode({
|
const dockerode = new Dockerode({
|
||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
@@ -15,7 +13,7 @@ export const getRemoteDocker = async (serverId?: string | null) => {
|
|||||||
protocol: "ssh",
|
protocol: "ssh",
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
sshOptions: {
|
sshOptions: {
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Client } from "ssh2";
|
|||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import { findServerById } from "@/server/services/server";
|
import { findServerById } from "@/server/services/server";
|
||||||
import { validateWebSocketRequest } from "../auth/auth";
|
import { validateWebSocketRequest } from "../auth/auth";
|
||||||
import { readSSHKey } from "../utils/filesystem/ssh";
|
|
||||||
import { getShell } from "./utils";
|
import { getShell } from "./utils";
|
||||||
|
|
||||||
export const setupDockerContainerLogsWebSocketServer = (
|
export const setupDockerContainerLogsWebSocketServer = (
|
||||||
@@ -50,7 +49,6 @@ export const setupDockerContainerLogsWebSocketServer = (
|
|||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
|
|
||||||
if (!server.sshKeyId) return;
|
if (!server.sshKeyId) return;
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
new Promise<void>((resolve, reject) => {
|
new Promise<void>((resolve, reject) => {
|
||||||
client
|
client
|
||||||
@@ -82,7 +80,7 @@ export const setupDockerContainerLogsWebSocketServer = (
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Client } from "ssh2";
|
|||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import { findServerById } from "@/server/services/server";
|
import { findServerById } from "@/server/services/server";
|
||||||
import { validateWebSocketRequest } from "../auth/auth";
|
import { validateWebSocketRequest } from "../auth/auth";
|
||||||
import { readSSHKey } from "../utils/filesystem/ssh";
|
|
||||||
import { getShell } from "./utils";
|
import { getShell } from "./utils";
|
||||||
|
|
||||||
export const setupDockerContainerTerminalWebSocketServer = (
|
export const setupDockerContainerTerminalWebSocketServer = (
|
||||||
@@ -51,7 +50,6 @@ export const setupDockerContainerTerminalWebSocketServer = (
|
|||||||
if (!server.sshKeyId)
|
if (!server.sshKeyId)
|
||||||
throw new Error("No SSH key available for this server");
|
throw new Error("No SSH key available for this server");
|
||||||
|
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
const conn = new Client();
|
const conn = new Client();
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
@@ -107,7 +105,7 @@ export const setupDockerContainerTerminalWebSocketServer = (
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Client } from "ssh2";
|
|||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import { findServerById } from "@/server/services/server";
|
import { findServerById } from "@/server/services/server";
|
||||||
import { validateWebSocketRequest } from "../auth/auth";
|
import { validateWebSocketRequest } from "../auth/auth";
|
||||||
import { readSSHKey } from "../utils/filesystem/ssh";
|
|
||||||
|
|
||||||
export const setupDeploymentLogsWebSocketServer = (
|
export const setupDeploymentLogsWebSocketServer = (
|
||||||
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
|
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
|
||||||
@@ -49,7 +48,6 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
|
|
||||||
if (!server.sshKeyId) return;
|
if (!server.sshKeyId) return;
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
new Promise<void>((resolve, reject) => {
|
new Promise<void>((resolve, reject) => {
|
||||||
client
|
client
|
||||||
@@ -81,7 +79,7 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
username: server.username,
|
username: server.username,
|
||||||
privateKey: keys.privateKey,
|
privateKey: server.sshKey?.privateKey,
|
||||||
timeout: 99999,
|
timeout: 99999,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"noEmit": false,
|
"noEmit": false,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
|
"rootDir": "./src",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"incremental": false,
|
"incremental": false,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
|||||||
40
pnpm-lock.yaml
generated
40
pnpm-lock.yaml
generated
@@ -591,7 +591,7 @@ importers:
|
|||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
otpauth:
|
otpauth:
|
||||||
specifier: ^9.2.3
|
specifier: ^9.2.3
|
||||||
version: 9.3.1
|
version: 9.3.4
|
||||||
postgres:
|
postgres:
|
||||||
specifier: 3.4.4
|
specifier: 3.4.4
|
||||||
version: 3.4.4
|
version: 3.4.4
|
||||||
@@ -600,7 +600,7 @@ importers:
|
|||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
qrcode:
|
qrcode:
|
||||||
specifier: ^1.5.3
|
specifier: ^1.5.3
|
||||||
version: 1.5.3
|
version: 1.5.4
|
||||||
react:
|
react:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0
|
version: 18.2.0
|
||||||
@@ -649,7 +649,7 @@ importers:
|
|||||||
version: 2.1.6
|
version: 2.1.6
|
||||||
'@types/nodemailer':
|
'@types/nodemailer':
|
||||||
specifier: ^6.4.15
|
specifier: ^6.4.15
|
||||||
version: 6.4.15
|
version: 6.4.16
|
||||||
'@types/qrcode':
|
'@types/qrcode':
|
||||||
specifier: ^1.5.5
|
specifier: ^1.5.5
|
||||||
version: 1.5.5
|
version: 1.5.5
|
||||||
@@ -1840,9 +1840,9 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@noble/hashes@1.4.0':
|
'@noble/hashes@1.5.0':
|
||||||
resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
|
resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: ^14.21.3 || >=16}
|
||||||
|
|
||||||
'@node-rs/argon2-android-arm-eabi@1.7.0':
|
'@node-rs/argon2-android-arm-eabi@1.7.0':
|
||||||
resolution: {integrity: sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==}
|
resolution: {integrity: sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==}
|
||||||
@@ -3519,8 +3519,8 @@ packages:
|
|||||||
'@types/node@20.4.6':
|
'@types/node@20.4.6':
|
||||||
resolution: {integrity: sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==}
|
resolution: {integrity: sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==}
|
||||||
|
|
||||||
'@types/nodemailer@6.4.15':
|
'@types/nodemailer@6.4.16':
|
||||||
resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==}
|
resolution: {integrity: sha512-uz6hN6Pp0upXMcilM61CoKyjT7sskBoOWpptkjjJp8jIMlTdc3xG01U7proKkXzruMS4hS0zqtHNkNPFB20rKQ==}
|
||||||
|
|
||||||
'@types/prop-types@15.7.12':
|
'@types/prop-types@15.7.12':
|
||||||
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
||||||
@@ -4736,9 +4736,6 @@ packages:
|
|||||||
emoji-regex@9.2.2:
|
emoji-regex@9.2.2:
|
||||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||||
|
|
||||||
encode-utf8@1.0.3:
|
|
||||||
resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
|
|
||||||
|
|
||||||
end-of-stream@1.4.4:
|
end-of-stream@1.4.4:
|
||||||
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||||
|
|
||||||
@@ -6729,8 +6726,8 @@ packages:
|
|||||||
oslo@1.2.0:
|
oslo@1.2.0:
|
||||||
resolution: {integrity: sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==}
|
resolution: {integrity: sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==}
|
||||||
|
|
||||||
otpauth@9.3.1:
|
otpauth@9.3.4:
|
||||||
resolution: {integrity: sha512-E6d2tMxPofHNk4sRFp+kqW7vQ+WJGO9VLI2N/W00DnI+ThskU12Qa10kyNSGklrzhN5c+wRUsN4GijVgCU2N9w==}
|
resolution: {integrity: sha512-qXv+lpsCUO9ewitLYfeDKbLYt7UUCivnU/fwGK2OqhgrCBsRkTUNKWsgKAhkXG3aistOY+jEeuL90JEBu6W3mQ==}
|
||||||
|
|
||||||
p-cancelable@3.0.0:
|
p-cancelable@3.0.0:
|
||||||
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
|
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
|
||||||
@@ -7047,8 +7044,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
qrcode@1.5.3:
|
qrcode@1.5.4:
|
||||||
resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
|
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -9316,7 +9313,7 @@ snapshots:
|
|||||||
'@next/swc-win32-x64-msvc@14.2.5':
|
'@next/swc-win32-x64-msvc@14.2.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@noble/hashes@1.4.0': {}
|
'@noble/hashes@1.5.0': {}
|
||||||
|
|
||||||
'@node-rs/argon2-android-arm-eabi@1.7.0':
|
'@node-rs/argon2-android-arm-eabi@1.7.0':
|
||||||
optional: true
|
optional: true
|
||||||
@@ -11594,7 +11591,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/node@20.4.6': {}
|
'@types/node@20.4.6': {}
|
||||||
|
|
||||||
'@types/nodemailer@6.4.15':
|
'@types/nodemailer@6.4.16':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.14.10
|
'@types/node': 20.14.10
|
||||||
|
|
||||||
@@ -12838,8 +12835,6 @@ snapshots:
|
|||||||
|
|
||||||
emoji-regex@9.2.2: {}
|
emoji-regex@9.2.2: {}
|
||||||
|
|
||||||
encode-utf8@1.0.3: {}
|
|
||||||
|
|
||||||
end-of-stream@1.4.4:
|
end-of-stream@1.4.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
once: 1.4.0
|
once: 1.4.0
|
||||||
@@ -15452,9 +15447,9 @@ snapshots:
|
|||||||
'@node-rs/argon2': 1.7.0
|
'@node-rs/argon2': 1.7.0
|
||||||
'@node-rs/bcrypt': 1.9.0
|
'@node-rs/bcrypt': 1.9.0
|
||||||
|
|
||||||
otpauth@9.3.1:
|
otpauth@9.3.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.4.0
|
'@noble/hashes': 1.5.0
|
||||||
|
|
||||||
p-cancelable@3.0.0: {}
|
p-cancelable@3.0.0: {}
|
||||||
|
|
||||||
@@ -15708,10 +15703,9 @@ snapshots:
|
|||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
qrcode@1.5.3:
|
qrcode@1.5.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
dijkstrajs: 1.0.3
|
dijkstrajs: 1.0.3
|
||||||
encode-utf8: 1.0.3
|
|
||||||
pngjs: 5.0.0
|
pngjs: 5.0.0
|
||||||
yargs: 15.4.1
|
yargs: 15.4.1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user