Deploy projects by pushing files to Dokku server via git.
This commit is contained in:
parent
870783940d
commit
7ed19188d4
70
backend/server/package-lock.json
generated
70
backend/server/package-lock.json
generated
@ -15,6 +15,7 @@
|
|||||||
"e2b": "^0.16.1",
|
"e2b": "^0.16.1",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"rate-limiter-flexible": "^5.0.3",
|
"rate-limiter-flexible": "^5.0.3",
|
||||||
|
"simple-git": "^3.25.0",
|
||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"ssh2": "^1.15.0",
|
"ssh2": "^1.15.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
@ -77,6 +78,40 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kwsites/file-exists": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kwsites/file-exists/node_modules/debug": {
|
||||||
|
"version": "4.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
|
||||||
|
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kwsites/file-exists/node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"node_modules/@kwsites/promise-deferred": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="
|
||||||
|
},
|
||||||
"node_modules/@socket.io/component-emitter": {
|
"node_modules/@socket.io/component-emitter": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz",
|
||||||
@ -1695,6 +1730,41 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-git": {
|
||||||
|
"version": "3.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.25.0.tgz",
|
||||||
|
"integrity": "sha512-KIY5sBnzc4yEcJXW7Tdv4viEz8KyG+nU0hay+DWZasvdFOYKeUZ6Xc25LUHHjw0tinPT7O1eY6pzX7pRT1K8rw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@kwsites/file-exists": "^1.1.1",
|
||||||
|
"@kwsites/promise-deferred": "^1.1.1",
|
||||||
|
"debug": "^4.3.5"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/steveukx/git-js?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/simple-git/node_modules/debug": {
|
||||||
|
"version": "4.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
|
||||||
|
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/simple-git/node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
"node_modules/simple-update-notifier": {
|
"node_modules/simple-update-notifier": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"e2b": "^0.16.1",
|
"e2b": "^0.16.1",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"rate-limiter-flexible": "^5.0.3",
|
"rate-limiter-flexible": "^5.0.3",
|
||||||
|
"simple-git": "^3.25.0",
|
||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"ssh2": "^1.15.0",
|
"ssh2": "^1.15.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
|
80
backend/server/src/SecureGitClient.ts
Normal file
80
backend/server/src/SecureGitClient.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import simpleGit, { SimpleGit } from "simple-git";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
import os from "os";
|
||||||
|
|
||||||
|
export type FileData = {
|
||||||
|
id: string;
|
||||||
|
data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SecureGitClient {
|
||||||
|
private gitUrl: string;
|
||||||
|
private sshKeyPath: string;
|
||||||
|
|
||||||
|
constructor(gitUrl: string, sshKeyPath: string) {
|
||||||
|
this.gitUrl = gitUrl;
|
||||||
|
this.sshKeyPath = sshKeyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async pushFiles(fileData: FileData[], repository: string): Promise<void> {
|
||||||
|
let tempDir: string | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a temporary directory
|
||||||
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-push-'));
|
||||||
|
console.log(`Temporary directory created: ${tempDir}`);
|
||||||
|
|
||||||
|
// Write files to the temporary directory
|
||||||
|
for (const { id, data } of fileData) {
|
||||||
|
const filePath = path.join(tempDir, id);
|
||||||
|
const dirPath = path.dirname(filePath);
|
||||||
|
|
||||||
|
if (!fs.existsSync(dirPath)) {
|
||||||
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Writing ", filePath, data);
|
||||||
|
fs.writeFileSync(filePath, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the simple-git instance with the temporary directory and custom SSH command
|
||||||
|
const git: SimpleGit = simpleGit(tempDir, {
|
||||||
|
config: [
|
||||||
|
'core.sshCommand=ssh -i ' + this.sshKeyPath + ' -o IdentitiesOnly=yes'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize a new Git repository
|
||||||
|
await git.init();
|
||||||
|
|
||||||
|
// Add remote repository
|
||||||
|
await git.addRemote("origin", `${this.gitUrl}:${repository}`);
|
||||||
|
|
||||||
|
// Add files to the repository
|
||||||
|
for (const {id, data} of fileData) {
|
||||||
|
await git.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit the changes
|
||||||
|
await git.commit("Add files.");
|
||||||
|
|
||||||
|
// Push the changes to the remote repository
|
||||||
|
await git.push("origin", "master");
|
||||||
|
|
||||||
|
console.log("Files successfully pushed to the repository");
|
||||||
|
|
||||||
|
if (tempDir) {
|
||||||
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||||
|
console.log(`Temporary directory removed: ${tempDir}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (tempDir) {
|
||||||
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||||
|
console.log(`Temporary directory removed: ${tempDir}`);
|
||||||
|
}
|
||||||
|
console.error("Error pushing files to the repository:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import os from "os";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
import express, { Express } from "express";
|
import express, { Express } from "express";
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import { createServer } from "http";
|
import { createServer } from "http";
|
||||||
import { Server } from "socket.io";
|
import { Server } from "socket.io";
|
||||||
import { DokkuClient, SSHConfig } from "./DokkuClient";
|
import { DokkuClient } from "./DokkuClient";
|
||||||
|
import { SecureGitClient, FileData } from "./SecureGitClient";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -126,6 +126,11 @@ const client = new DokkuClient({
|
|||||||
|
|
||||||
client.connect();
|
client.connect();
|
||||||
|
|
||||||
|
const git = new SecureGitClient(
|
||||||
|
"dokku@gitwit.app",
|
||||||
|
process.env.DOKKU_KEY
|
||||||
|
)
|
||||||
|
|
||||||
io.on("connection", async (socket) => {
|
io.on("connection", async (socket) => {
|
||||||
try {
|
try {
|
||||||
if (inactivityTimeout) clearTimeout(inactivityTimeout);
|
if (inactivityTimeout) clearTimeout(inactivityTimeout);
|
||||||
@ -292,6 +297,33 @@ io.on("connection", async (socket) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
socket.on(
|
||||||
|
"deploy",
|
||||||
|
async (callback: (response: CallbackResponse) => void) => {
|
||||||
|
try {
|
||||||
|
// Push the project files to the Dokku server
|
||||||
|
console.log("Deploying project ${data.sandboxId}...");
|
||||||
|
// Remove the /project/[id]/ component of each file path:
|
||||||
|
const fixedFilePaths = sandboxFiles.fileData.map((file) => {
|
||||||
|
return {
|
||||||
|
...file,
|
||||||
|
id: file.id.split("/").slice(2).join("/"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// Push all files to Dokku.
|
||||||
|
await git.pushFiles(fixedFilePaths, data.sandboxId);
|
||||||
|
callback({
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
callback({
|
||||||
|
success: false,
|
||||||
|
message: "Failed to deploy project: " + error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
socket.on("createFile", async (name: string, callback) => {
|
socket.on("createFile", async (name: string, callback) => {
|
||||||
try {
|
try {
|
||||||
const size: number = await getProjectSize(data.sandboxId);
|
const size: number = await getProjectSize(data.sandboxId);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user