feat(cloud): add deploy on remote worker

This commit is contained in:
Mauricio Siu
2024-10-03 23:46:26 -06:00
parent 767d3e1944
commit 388ded9aa5
10 changed files with 229 additions and 479 deletions

View File

@@ -8,6 +8,8 @@
"start": "node dist/index.js"
},
"dependencies": {
"@hono/zod-validator": "0.3.0",
"zod": "^3.23.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"@dokploy/builders": "workspace:*",

View File

@@ -3,56 +3,42 @@ import { Hono } from "hono";
import "dotenv/config";
import { createClient } from "redis";
import { Queue } from "@nerimity/mimiqueue";
import { deployApplication } from "@dokploy/builders";
import { zValidator } from "@hono/zod-validator";
import { type DeployJob, deployJobSchema } from "./schema";
import { deploy } from "./utils";
const app = new Hono();
const redisClient = createClient({
// socket: {
// host: "localhost",
// port: 6379,
// },
url: process.env.REDIS_URL,
// password: "xlfvpQ0ma2BkkkPX",
});
app.post("/publish", async (c) => {
const { userId, applicationId } = await c.req.json();
queue
.add(
{
userId,
applicationId,
},
{ groupName: userId },
)
.then((res) => {
console.log(res);
});
return c.json({ message: `Despliegue encolado para el usuario ${userId}` });
app.post("/deploy", zValidator("json", deployJobSchema), (c) => {
const data = c.req.valid("json");
queue.add(data, { groupName: data.serverId }).then((res) => {
console.log(res);
});
return c.json(
{
message: "Deployment started",
},
200,
);
});
app.get("/health", async (c) => {
return c.json({ status: "ok" });
});
// await redisClient.connect();
// await redisClient.flushAll();
const queue = new Queue({
name: "deployments",
process: async (data) => {
// await setTimeout(8000);
await deployApplication({
applicationId: data.applicationId,
titleLog: "HHHHH",
descriptionLog: "",
});
return { done: "lol", data };
process: async (job: DeployJob) => {
console.log(job);
return await deploy(job);
},
redisClient,
});
const port = Number.parseInt(process.env.PORT || "3000");
(async () => {
await redisClient.connect();
await redisClient.flushAll();

24
apps/api/src/schema.ts Normal file
View File

@@ -0,0 +1,24 @@
import { z } from "zod";
export const deployJobSchema = z.discriminatedUnion("applicationType", [
z.object({
applicationId: z.string(),
titleLog: z.string(),
descriptionLog: z.string(),
server: z.boolean().optional(),
type: z.enum(["deploy", "redeploy"]),
applicationType: z.literal("application"),
serverId: z.string(),
}),
z.object({
composeId: z.string(),
titleLog: z.string(),
descriptionLog: z.string(),
server: z.boolean().optional(),
type: z.enum(["deploy", "redeploy"]),
applicationType: z.literal("compose"),
serverId: z.string(),
}),
]);
export type DeployJob = z.infer<typeof deployJobSchema>;

View File

@@ -1,30 +1,124 @@
// import { LEMON_SQUEEZY_API_KEY, LEMON_SQUEEZY_STORE_ID } from ".";
import {
deployApplication,
deployCompose,
deployRemoteApplication,
deployRemoteCompose,
rebuildApplication,
rebuildCompose,
rebuildRemoteApplication,
rebuildRemoteCompose,
updateApplicationStatus,
updateCompose,
} from "@dokploy/builders";
import type { LemonSqueezyLicenseResponse } from "./types";
import type { DeployJob } from "./schema";
const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY;
const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID;
export const validateLemonSqueezyLicense = async (
licenseKey: string,
): Promise<LemonSqueezyLicenseResponse> => {
// const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY;
// const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID;
// export const validateLemonSqueezyLicense = async (
// licenseKey: string,
// ): Promise<LemonSqueezyLicenseResponse> => {
// try {
// const response = await fetch(
// "https://api.lemonsqueezy.com/v1/licenses/validate",
// {
// method: "POST",
// headers: {
// "Content-Type": "application/json",
// "x-api-key": LEMON_SQUEEZY_API_KEY as string,
// },
// body: JSON.stringify({
// license_key: licenseKey,
// store_id: LEMON_SQUEEZY_STORE_ID as string,
// }),
// },
// );
// return response.json();
// } catch (error) {
// console.error("Error validating license:", error);
// return { valid: false, error: "Error validating license" };
// }
// };
export const deploy = async (job: DeployJob) => {
try {
const response = await fetch(
"https://api.lemonsqueezy.com/v1/licenses/validate",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": LEMON_SQUEEZY_API_KEY as string,
},
body: JSON.stringify({
license_key: licenseKey,
store_id: LEMON_SQUEEZY_STORE_ID as string,
}),
},
);
if (job.applicationType === "application") {
await updateApplicationStatus(job.applicationId, "running");
if (job.server) {
if (job.type === "redeploy") {
await rebuildRemoteApplication({
applicationId: job.applicationId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
} else if (job.type === "deploy") {
await deployRemoteApplication({
applicationId: job.applicationId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
}
} else {
if (job.type === "redeploy") {
await rebuildApplication({
applicationId: job.applicationId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
} else if (job.type === "deploy") {
await deployApplication({
applicationId: job.applicationId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
}
}
} else if (job.applicationType === "compose") {
await updateCompose(job.composeId, {
composeStatus: "running",
});
return response.json();
if (job.server) {
if (job.type === "redeploy") {
await rebuildRemoteCompose({
composeId: job.composeId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
} else if (job.type === "deploy") {
await deployRemoteCompose({
composeId: job.composeId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
}
} else {
if (job.type === "deploy") {
await deployCompose({
composeId: job.composeId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
} else if (job.type === "redeploy") {
await rebuildCompose({
composeId: job.composeId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
}
}
}
} catch (error) {
console.error("Error validating license:", error);
return { valid: false, error: "Error validating license" };
console.log(error);
if (job.applicationType === "application") {
await updateApplicationStatus(job.applicationId, "error");
} else if (job.applicationType === "compose") {
await updateCompose(job.composeId, {
composeStatus: "error",
});
}
}
return true;
};