feat(multi server): add support for drag n drop

This commit is contained in:
Mauricio Siu
2024-09-15 17:05:17 -06:00
parent 0d3c978aad
commit 19295ba746
7 changed files with 103 additions and 17 deletions

View File

@@ -9,7 +9,6 @@ import { COMPOSE_PATH } from "@/server/constants";
import type { InferResultType } from "@/server/types/with";
import boxen from "boxen";
import {
getComposePath,
writeDomainsToCompose,
writeDomainsToComposeRemote,
} from "../docker/domain";
@@ -114,11 +113,6 @@ Compose Type: ${composeType} ✅`;
cd "${projectPath}";
if [ ! -f "${composePath}" ]; then
echo "❌ Error: Compose file not found" >> "${logPath}";
exit 1;
fi
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
echo "Docker Compose Deployed: ✅" >> "${logPath}"

View File

@@ -2,12 +2,27 @@ import fs from "node:fs/promises";
import path, { join } from "node:path";
import { APPLICATIONS_PATH } from "@/server/constants";
import AdmZip from "adm-zip";
import { recreateDirectory } from "../filesystem/directory";
import {
recreateDirectory,
recreateDirectoryRemote,
} from "../filesystem/directory";
import type { Application } from "@/server/api/services/application";
import { execAsyncRemote } from "../process/execAsync";
import { Client, type SFTPWrapper } from "ssh2";
import { findServerById } from "@/server/api/services/server";
import { readSSHKey } from "../filesystem/ssh";
export const unzipDrop = async (zipFile: File, application: Application) => {
let sftp: SFTPWrapper | null = null;
export const unzipDrop = async (zipFile: File, appName: string) => {
try {
const { appName } = application;
const outputPath = join(APPLICATIONS_PATH, appName, "code");
await recreateDirectory(outputPath);
if (application.serverId) {
await recreateDirectoryRemote(outputPath, application.serverId);
} else {
await recreateDirectory(outputPath);
}
const arrayBuffer = await zipFile.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
@@ -28,6 +43,9 @@ export const unzipDrop = async (zipFile: File, appName: string) => {
? rootEntries[0]?.entryName.split("/")[0]
: "";
if (application.serverId) {
sftp = await getSFTPConnection(application.serverId);
}
for (const entry of zipEntries) {
let filePath = entry.entryName;
@@ -42,15 +60,64 @@ export const unzipDrop = async (zipFile: File, appName: string) => {
if (!filePath) continue;
const fullPath = path.join(outputPath, filePath);
if (entry.isDirectory) {
await fs.mkdir(fullPath, { recursive: true });
if (application.serverId) {
if (entry.isDirectory) {
await execAsyncRemote(application.serverId, `mkdir -p ${fullPath}`);
} else {
if (sftp === null) throw new Error("No SFTP connection available");
await uploadFileToServer(sftp, entry.getData(), fullPath);
}
} else {
await fs.mkdir(path.dirname(fullPath), { recursive: true });
await fs.writeFile(fullPath, entry.getData());
if (entry.isDirectory) {
await fs.mkdir(fullPath, { recursive: true });
} else {
await fs.mkdir(path.dirname(fullPath), { recursive: true });
await fs.writeFile(fullPath, entry.getData());
}
}
}
} catch (error) {
console.error("Error processing ZIP file:", error);
throw error;
} finally {
sftp?.end();
}
};
const getSFTPConnection = async (serverId: string): Promise<SFTPWrapper> => {
const server = await findServerById(serverId);
if (!server.sshKeyId) throw new Error("No SSH key available for this server");
const keys = await readSSHKey(server.sshKeyId);
return new Promise((resolve, reject) => {
const conn = new Client();
conn
.on("ready", () => {
conn.sftp((err, sftp) => {
if (err) return reject(err);
resolve(sftp);
});
})
.connect({
host: server.ipAddress,
port: server.port,
username: server.username,
privateKey: keys.privateKey,
timeout: 99999,
});
});
};
const uploadFileToServer = (
sftp: SFTPWrapper,
data: Buffer,
remotePath: string,
): Promise<void> => {
return new Promise((resolve, reject) => {
sftp.writeFile(remotePath, data, (err) => {
if (err) return reject(err);
resolve();
});
});
};

View File

@@ -151,14 +151,21 @@ export const writeDomainsToComposeRemote = async (
try {
const composeConverted = await addDomainToCompose(compose, domains);
const path = getComposePath(compose);
if (!composeConverted) {
return `
echo "❌ Error: Compose file not found" >> ${logPath};
exit 1;
`;
}
if (compose.serverId) {
const composeString = dump(composeConverted, { lineWidth: 1000 });
const encodedContent = encodeBase64(composeString);
return `echo "${encodedContent}" | base64 -d > "${path}";`;
}
} catch (error) {
return `
echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath};
// @ts-ignore
return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath};
exit 1;
`;
}

View File

@@ -17,6 +17,20 @@ export const recreateDirectory = async (pathFolder: string): Promise<void> => {
}
};
export const recreateDirectoryRemote = async (
pathFolder: string,
serverId: string | null,
): Promise<void> => {
try {
await execAsyncRemote(
serverId,
`rm -rf ${pathFolder}; mkdir -p ${pathFolder}`,
);
} catch (error) {
console.error(`Error recreating directory '${pathFolder}':`, error);
}
};
export const removeDirectoryIfExistsContent = async (
path: string,
): Promise<void> => {