mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor(server): add support for multi server
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
export const DockerLogs = dynamic(
|
export const DockerLogs = dynamic(
|
||||||
@@ -34,7 +35,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ShowDockerLogs = ({ appName, serverId }: Props) => {
|
export const ShowDockerLogs = ({ appName, serverId }: Props) => {
|
||||||
const { data } = api.docker.getContainersByAppNameMatch.useQuery(
|
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
|
||||||
{
|
{
|
||||||
appName,
|
appName,
|
||||||
serverId,
|
serverId,
|
||||||
@@ -64,7 +65,14 @@ export const ShowDockerLogs = ({ appName, serverId }: Props) => {
|
|||||||
<Label>Select a container to view logs</Label>
|
<Label>Select a container to view logs</Label>
|
||||||
<Select onValueChange={setContainerId} value={containerId}>
|
<Select onValueChange={setContainerId} value={containerId}>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select a container" />
|
{isLoading ? (
|
||||||
|
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
|
||||||
|
<span>Loading...</span>
|
||||||
|
<Loader2 className="animate-spin size-4" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<SelectValue placeholder="Select a container" />
|
||||||
|
)}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { Loader, Loader2 } from "lucide-react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
export const DockerLogs = dynamic(
|
export const DockerLogs = dynamic(
|
||||||
@@ -39,7 +40,7 @@ export const ShowDockerLogsCompose = ({
|
|||||||
appType,
|
appType,
|
||||||
serverId,
|
serverId,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { data } = api.docker.getContainersByAppNameMatch.useQuery(
|
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
|
||||||
{
|
{
|
||||||
appName,
|
appName,
|
||||||
appType,
|
appType,
|
||||||
@@ -70,7 +71,14 @@ export const ShowDockerLogsCompose = ({
|
|||||||
<Label>Select a container to view logs</Label>
|
<Label>Select a container to view logs</Label>
|
||||||
<Select onValueChange={setContainerId} value={containerId}>
|
<Select onValueChange={setContainerId} value={containerId}>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select a container" />
|
{isLoading ? (
|
||||||
|
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
|
||||||
|
<span>Loading...</span>
|
||||||
|
<Loader2 className="animate-spin size-4" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<SelectValue placeholder="Select a container" />
|
||||||
|
)}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { api } from "@/utils/api";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { DockerMonitoring } from "../../monitoring/docker/show";
|
import { DockerMonitoring } from "../../monitoring/docker/show";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
appName: string;
|
appName: string;
|
||||||
@@ -32,7 +33,7 @@ export const ShowMonitoringCompose = ({
|
|||||||
appType = "stack",
|
appType = "stack",
|
||||||
serverId,
|
serverId,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { data } = api.docker.getContainersByAppNameMatch.useQuery(
|
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
|
||||||
{
|
{
|
||||||
appName: appName,
|
appName: appName,
|
||||||
appType,
|
appType,
|
||||||
@@ -49,7 +50,7 @@ export const ShowMonitoringCompose = ({
|
|||||||
|
|
||||||
const [containerId, setContainerId] = useState<string | undefined>();
|
const [containerId, setContainerId] = useState<string | undefined>();
|
||||||
|
|
||||||
const { mutateAsync: restart, isLoading } =
|
const { mutateAsync: restart, isLoading: isRestarting } =
|
||||||
api.docker.restartContainer.useMutation();
|
api.docker.restartContainer.useMutation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -80,7 +81,14 @@ export const ShowMonitoringCompose = ({
|
|||||||
value={containerAppName}
|
value={containerAppName}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select a container" />
|
{isLoading ? (
|
||||||
|
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
|
||||||
|
<span>Loading...</span>
|
||||||
|
<Loader2 className="animate-spin size-4" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<SelectValue placeholder="Select a container" />
|
||||||
|
)}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
@@ -98,7 +106,7 @@ export const ShowMonitoringCompose = ({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<Button
|
<Button
|
||||||
isLoading={isLoading}
|
isLoading={isRestarting}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!containerId) return;
|
if (!containerId) return;
|
||||||
toast.success(`Restarting container ${containerAppName}`);
|
toast.success(`Restarting container ${containerAppName}`);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -38,7 +39,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DockerTerminalModal = ({ children, appName, serverId }: Props) => {
|
export const DockerTerminalModal = ({ children, appName, serverId }: Props) => {
|
||||||
const { data } = api.docker.getContainersByAppNameMatch.useQuery(
|
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
|
||||||
{
|
{
|
||||||
appName,
|
appName,
|
||||||
serverId,
|
serverId,
|
||||||
@@ -67,7 +68,14 @@ export const DockerTerminalModal = ({ children, appName, serverId }: Props) => {
|
|||||||
<Label>Select a container to view logs</Label>
|
<Label>Select a container to view logs</Label>
|
||||||
<Select onValueChange={setContainerId} value={containerId}>
|
<Select onValueChange={setContainerId} value={containerId}>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select a container" />
|
{isLoading ? (
|
||||||
|
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
|
||||||
|
<span>Loading...</span>
|
||||||
|
<Loader2 className="animate-spin size-4" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<SelectValue placeholder="Select a container" />
|
||||||
|
)}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -38,7 +39,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ShowModalLogs = ({ appName, children }: Props) => {
|
export const ShowModalLogs = ({ appName, children }: Props) => {
|
||||||
const { data } = api.docker.getContainersByAppLabel.useQuery(
|
const { data, isLoading } = api.docker.getContainersByAppLabel.useQuery(
|
||||||
{
|
{
|
||||||
appName,
|
appName,
|
||||||
},
|
},
|
||||||
@@ -72,7 +73,14 @@ export const ShowModalLogs = ({ appName, children }: Props) => {
|
|||||||
<Label>Select a container to view logs</Label>
|
<Label>Select a container to view logs</Label>
|
||||||
<Select onValueChange={setContainerId} value={containerId}>
|
<Select onValueChange={setContainerId} value={containerId}>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select a container" />
|
{isLoading ? (
|
||||||
|
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
|
||||||
|
<span>Loading...</span>
|
||||||
|
<Loader2 className="animate-spin size-4" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<SelectValue placeholder="Select a container" />
|
||||||
|
)}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
type DeploymentJob,
|
type DeploymentJob,
|
||||||
cleanQueuesByApplication,
|
cleanQueuesByApplication,
|
||||||
} from "@/server/queues/deployments-queue";
|
} from "@/server/queues/deployments-queue";
|
||||||
import { enqueueDeploymentJob, myQueue } from "@/server/queues/queueSetup";
|
import { myQueue } from "@/server/queues/queueSetup";
|
||||||
import {
|
import {
|
||||||
removeService,
|
removeService,
|
||||||
startService,
|
startService,
|
||||||
@@ -134,12 +134,19 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
const cleanupOperations = [
|
const cleanupOperations = [
|
||||||
async () => deleteAllMiddlewares(application),
|
async () => await deleteAllMiddlewares(application),
|
||||||
async () => await removeDeployments(application),
|
async () => await removeDeployments(application),
|
||||||
async () => await removeDirectoryCode(application?.appName),
|
async () =>
|
||||||
async () => await removeMonitoringDirectory(application?.appName),
|
await removeDirectoryCode(application.appName, application.serverId),
|
||||||
async () => await removeTraefikConfig(application?.appName),
|
async () =>
|
||||||
async () => await removeService(application?.appName),
|
await removeMonitoringDirectory(
|
||||||
|
application.appName,
|
||||||
|
application.serverId,
|
||||||
|
),
|
||||||
|
async () =>
|
||||||
|
await removeTraefikConfig(application.appName, application.serverId),
|
||||||
|
async () =>
|
||||||
|
await removeService(application?.appName, application.serverId),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const operation of cleanupOperations) {
|
for (const operation of cleanupOperations) {
|
||||||
@@ -327,7 +334,7 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
deploy: protectedProcedure
|
deploy: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const application = await findApplicationById(input.applicationId);
|
// const application = await findApplicationById(input.applicationId);
|
||||||
const jobData: DeploymentJob = {
|
const jobData: DeploymentJob = {
|
||||||
applicationId: input.applicationId,
|
applicationId: input.applicationId,
|
||||||
titleLog: "Manual deployment",
|
titleLog: "Manual deployment",
|
||||||
@@ -335,10 +342,18 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
type: "deploy",
|
type: "deploy",
|
||||||
applicationType: "application",
|
applicationType: "application",
|
||||||
};
|
};
|
||||||
if (!application.serverId) {
|
await myQueue.add(
|
||||||
} else {
|
"deployments",
|
||||||
await enqueueDeploymentJob(application.serverId, jobData);
|
{ ...jobData },
|
||||||
}
|
{
|
||||||
|
removeOnComplete: true,
|
||||||
|
removeOnFail: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// if (!application.serverId) {
|
||||||
|
// } else {
|
||||||
|
// await enqueueDeploymentJob(application.serverId, jobData);
|
||||||
|
// }
|
||||||
}),
|
}),
|
||||||
|
|
||||||
cleanQueues: protectedProcedure
|
cleanQueues: protectedProcedure
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
type DeploymentJob,
|
type DeploymentJob,
|
||||||
cleanQueuesByCompose,
|
cleanQueuesByCompose,
|
||||||
} from "@/server/queues/deployments-queue";
|
} from "@/server/queues/deployments-queue";
|
||||||
import { enqueueDeploymentJob, myQueue } from "@/server/queues/queueSetup";
|
import { myQueue } from "@/server/queues/queueSetup";
|
||||||
import { createCommand } from "@/server/utils/builders/compose";
|
import { createCommand } from "@/server/utils/builders/compose";
|
||||||
import {
|
import {
|
||||||
randomizeComposeFile,
|
randomizeComposeFile,
|
||||||
@@ -176,14 +176,7 @@ export const composeRouter = createTRPCRouter({
|
|||||||
// applicationType: "compose",
|
// applicationType: "compose",
|
||||||
// descriptionLog: "",
|
// descriptionLog: "",
|
||||||
// };
|
// };
|
||||||
// await myQueue.add(
|
|
||||||
// "deployments",
|
|
||||||
// { ...jobData },
|
|
||||||
// {
|
|
||||||
// removeOnComplete: true,
|
|
||||||
// removeOnFail: true,
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
const compose = await findComposeById(input.composeId);
|
const compose = await findComposeById(input.composeId);
|
||||||
const jobData: DeploymentJob = {
|
const jobData: DeploymentJob = {
|
||||||
composeId: input.composeId,
|
composeId: input.composeId,
|
||||||
@@ -192,10 +185,18 @@ export const composeRouter = createTRPCRouter({
|
|||||||
applicationType: "compose",
|
applicationType: "compose",
|
||||||
descriptionLog: "",
|
descriptionLog: "",
|
||||||
};
|
};
|
||||||
if (!compose.serverId) {
|
await myQueue.add(
|
||||||
} else {
|
"deployments",
|
||||||
await enqueueDeploymentJob(compose.serverId, jobData);
|
{ ...jobData },
|
||||||
}
|
{
|
||||||
|
removeOnComplete: true,
|
||||||
|
removeOnFail: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// if (!compose.serverId) {
|
||||||
|
// } else {
|
||||||
|
// await enqueueDeploymentJob(compose.serverId, jobData);
|
||||||
|
// }
|
||||||
}),
|
}),
|
||||||
redeploy: protectedProcedure
|
redeploy: protectedProcedure
|
||||||
.input(apiFindCompose)
|
.input(apiFindCompose)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
} from "@/server/utils/providers/gitlab";
|
} from "@/server/utils/providers/gitlab";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
import { executeCommand } from "@/server/utils/servers/command";
|
import { executeCommand } from "@/server/utils/servers/command";
|
||||||
|
import { execAsyncRemote } from "@/server/utils/process/execAsync";
|
||||||
export type Application = typeof applications.$inferSelect;
|
export type Application = typeof applications.$inferSelect;
|
||||||
|
|
||||||
export const createApplication = async (
|
export const createApplication = async (
|
||||||
@@ -164,7 +165,7 @@ export const deployApplication = async ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
let command = "set -e";
|
let command = "set -e;";
|
||||||
if (application.sourceType === "github") {
|
if (application.sourceType === "github") {
|
||||||
command += await getGithubCloneCommand(application, deployment.logPath);
|
command += await getGithubCloneCommand(application, deployment.logPath);
|
||||||
} else if (application.sourceType === "gitlab") {
|
} else if (application.sourceType === "gitlab") {
|
||||||
@@ -186,8 +187,7 @@ export const deployApplication = async ({
|
|||||||
if (application.sourceType !== "docker") {
|
if (application.sourceType !== "docker") {
|
||||||
command += getBuildCommand(application, deployment.logPath);
|
command += getBuildCommand(application, deployment.logPath);
|
||||||
}
|
}
|
||||||
|
await execAsyncRemote(application.serverId, command);
|
||||||
await executeCommand(application.serverId, command);
|
|
||||||
await mechanizeDockerContainer(application);
|
await mechanizeDockerContainer(application);
|
||||||
} else {
|
} else {
|
||||||
if (application.sourceType === "github") {
|
if (application.sourceType === "github") {
|
||||||
@@ -209,8 +209,6 @@ export const deployApplication = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Command", "Finish");
|
|
||||||
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updateApplicationStatus(applicationId, "done");
|
await updateApplicationStatus(applicationId, "done");
|
||||||
|
|
||||||
@@ -263,18 +261,31 @@ export const rebuildApplication = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (application.sourceType === "github") {
|
if (application.serverId) {
|
||||||
await buildApplication(application, deployment.logPath);
|
let command = "set -e;";
|
||||||
} else if (application.sourceType === "gitlab") {
|
if (application.sourceType === "docker") {
|
||||||
await buildApplication(application, deployment.logPath);
|
command += await buildRemoteDocker(application, deployment.logPath);
|
||||||
} else if (application.sourceType === "bitbucket") {
|
}
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "docker") {
|
if (application.sourceType !== "docker") {
|
||||||
await buildDocker(application, deployment.logPath);
|
command += getBuildCommand(application, deployment.logPath);
|
||||||
} else if (application.sourceType === "git") {
|
}
|
||||||
await buildApplication(application, deployment.logPath);
|
await execAsyncRemote(application.serverId, command);
|
||||||
} else if (application.sourceType === "drop") {
|
await mechanizeDockerContainer(application);
|
||||||
await buildApplication(application, deployment.logPath);
|
} else {
|
||||||
|
if (application.sourceType === "github") {
|
||||||
|
await buildApplication(application, deployment.logPath);
|
||||||
|
} else if (application.sourceType === "gitlab") {
|
||||||
|
await buildApplication(application, deployment.logPath);
|
||||||
|
} else if (application.sourceType === "bitbucket") {
|
||||||
|
await buildApplication(application, deployment.logPath);
|
||||||
|
} else if (application.sourceType === "docker") {
|
||||||
|
await buildDocker(application, deployment.logPath);
|
||||||
|
} else if (application.sourceType === "git") {
|
||||||
|
await buildApplication(application, deployment.logPath);
|
||||||
|
} else if (application.sourceType === "drop") {
|
||||||
|
await buildApplication(application, deployment.logPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updateApplicationStatus(applicationId, "done");
|
await updateApplicationStatus(applicationId, "done");
|
||||||
|
|||||||
@@ -241,20 +241,6 @@ export const deployCompose = async ({
|
|||||||
} else if (compose.sourceType === "raw") {
|
} else if (compose.sourceType === "raw") {
|
||||||
command += getCreateComposeFileCommand(compose);
|
command += getCreateComposeFileCommand(compose);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Promise.resolve()
|
|
||||||
// .then(() => {
|
|
||||||
// return execAsyncRemote(compose.serverId, command);
|
|
||||||
// })
|
|
||||||
// .then(() => {
|
|
||||||
// return getBuildComposeCommand(compose, deployment.logPath);
|
|
||||||
// })
|
|
||||||
// .catch((err) => {
|
|
||||||
// throw err;
|
|
||||||
// })
|
|
||||||
// .then(() => {
|
|
||||||
// console.log(" ---- done ----");
|
|
||||||
// });
|
|
||||||
async function* sequentialSteps() {
|
async function* sequentialSteps() {
|
||||||
yield execAsyncRemote(compose.serverId, command);
|
yield execAsyncRemote(compose.serverId, command);
|
||||||
yield getBuildComposeCommand(compose, deployment.logPath);
|
yield getBuildComposeCommand(compose, deployment.logPath);
|
||||||
@@ -262,9 +248,6 @@ export const deployCompose = async ({
|
|||||||
|
|
||||||
const steps = sequentialSteps();
|
const steps = sequentialSteps();
|
||||||
for await (const step of steps) {
|
for await (const step of steps) {
|
||||||
if (step.stderr) {
|
|
||||||
console.log(step.stderr);
|
|
||||||
}
|
|
||||||
step;
|
step;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +313,12 @@ export const rebuildCompose = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await buildCompose(compose, deployment.logPath);
|
if (compose.serverId) {
|
||||||
|
await getBuildComposeCommand(compose, deployment.logPath);
|
||||||
|
} else {
|
||||||
|
await buildCompose(compose, deployment.logPath);
|
||||||
|
}
|
||||||
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updateCompose(composeId, {
|
await updateCompose(composeId, {
|
||||||
composeStatus: "done",
|
composeStatus: "done",
|
||||||
@@ -351,13 +339,24 @@ export const removeCompose = async (compose: Compose) => {
|
|||||||
const projectPath = join(COMPOSE_PATH, compose.appName);
|
const projectPath = join(COMPOSE_PATH, compose.appName);
|
||||||
|
|
||||||
if (compose.composeType === "stack") {
|
if (compose.composeType === "stack") {
|
||||||
await execAsync(`docker stack rm ${compose.appName}`, {
|
const command = `cd ${projectPath} && docker stack rm ${compose.appName} && rm -rf ${projectPath}`;
|
||||||
|
if (compose.serverId) {
|
||||||
|
await execAsyncRemote(compose.serverId, command);
|
||||||
|
} else {
|
||||||
|
await execAsync(command);
|
||||||
|
}
|
||||||
|
await execAsync(command, {
|
||||||
cwd: projectPath,
|
cwd: projectPath,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await execAsync(`docker compose -p ${compose.appName} down`, {
|
const command = `cd ${projectPath} && docker compose -p ${compose.appName} down && rm -rf ${projectPath}`;
|
||||||
cwd: projectPath,
|
if (compose.serverId) {
|
||||||
});
|
await execAsyncRemote(compose.serverId, command);
|
||||||
|
} else {
|
||||||
|
await execAsync(command, {
|
||||||
|
cwd: projectPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -370,9 +369,16 @@ export const stopCompose = async (composeId: string) => {
|
|||||||
const compose = await findComposeById(composeId);
|
const compose = await findComposeById(composeId);
|
||||||
try {
|
try {
|
||||||
if (compose.composeType === "docker-compose") {
|
if (compose.composeType === "docker-compose") {
|
||||||
await execAsync(`docker compose -p ${compose.appName} stop`, {
|
if (compose.serverId) {
|
||||||
cwd: join(COMPOSE_PATH, compose.appName),
|
await execAsyncRemote(
|
||||||
});
|
compose.serverId,
|
||||||
|
`cd ${join(COMPOSE_PATH, compose.appName)} && docker compose -p ${compose.appName} stop`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await execAsync(`docker compose -p ${compose.appName} stop`, {
|
||||||
|
cwd: join(COMPOSE_PATH, compose.appName),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateCompose(composeId, {
|
await updateCompose(composeId, {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { type Application, findApplicationById } from "./application";
|
|||||||
import { type Compose, findComposeById } from "./compose";
|
import { type Compose, findComposeById } from "./compose";
|
||||||
import { findServerById, type Server } from "./server";
|
import { findServerById, type Server } from "./server";
|
||||||
import { executeCommand } from "@/server/utils/servers/command";
|
import { executeCommand } from "@/server/utils/servers/command";
|
||||||
|
import { execAsyncRemote } from "@/server/utils/process/execAsync";
|
||||||
|
|
||||||
export type Deployment = typeof deployments.$inferSelect;
|
export type Deployment = typeof deployments.$inferSelect;
|
||||||
|
|
||||||
@@ -208,14 +209,23 @@ const removeLastTenComposeDeployments = async (composeId: string) => {
|
|||||||
export const removeDeployments = async (application: Application) => {
|
export const removeDeployments = async (application: Application) => {
|
||||||
const { appName, applicationId } = application;
|
const { appName, applicationId } = application;
|
||||||
const logsPath = path.join(LOGS_PATH, appName);
|
const logsPath = path.join(LOGS_PATH, appName);
|
||||||
await removeDirectoryIfExistsContent(logsPath);
|
if (application.serverId) {
|
||||||
|
await execAsyncRemote(application.serverId, `rm -rf ${logsPath}`);
|
||||||
|
} else {
|
||||||
|
await removeDirectoryIfExistsContent(logsPath);
|
||||||
|
}
|
||||||
await removeDeploymentsByApplicationId(applicationId);
|
await removeDeploymentsByApplicationId(applicationId);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeDeploymentsByComposeId = async (compose: Compose) => {
|
export const removeDeploymentsByComposeId = async (compose: Compose) => {
|
||||||
const { appName } = compose;
|
const { appName } = compose;
|
||||||
const logsPath = path.join(LOGS_PATH, appName);
|
const logsPath = path.join(LOGS_PATH, appName);
|
||||||
await removeDirectoryIfExistsContent(logsPath);
|
if (compose.serverId) {
|
||||||
|
await execAsyncRemote(compose.serverId, `rm -rf ${logsPath}`);
|
||||||
|
} else {
|
||||||
|
await removeDirectoryIfExistsContent(logsPath);
|
||||||
|
}
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.delete(deployments)
|
.delete(deployments)
|
||||||
.where(eq(deployments.composeId, compose.composeId))
|
.where(eq(deployments.composeId, compose.composeId))
|
||||||
|
|||||||
@@ -29,52 +29,52 @@ type DeployJob =
|
|||||||
|
|
||||||
export type DeploymentJob = DeployJob;
|
export type DeploymentJob = DeployJob;
|
||||||
|
|
||||||
// export const deploymentWorker = new Worker(
|
export const deploymentWorker = new Worker(
|
||||||
// "deployments",
|
"deployments",
|
||||||
// async (job: Job<DeploymentJob>) => {
|
async (job: Job<DeploymentJob>) => {
|
||||||
// try {
|
try {
|
||||||
// if (job.data.applicationType === "application") {
|
if (job.data.applicationType === "application") {
|
||||||
// await updateApplicationStatus(job.data.applicationId, "running");
|
await updateApplicationStatus(job.data.applicationId, "running");
|
||||||
// if (job.data.type === "redeploy") {
|
if (job.data.type === "redeploy") {
|
||||||
// await rebuildApplication({
|
await rebuildApplication({
|
||||||
// applicationId: job.data.applicationId,
|
applicationId: job.data.applicationId,
|
||||||
// titleLog: job.data.titleLog,
|
titleLog: job.data.titleLog,
|
||||||
// descriptionLog: job.data.descriptionLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
// });
|
});
|
||||||
// } else if (job.data.type === "deploy") {
|
} else if (job.data.type === "deploy") {
|
||||||
// await deployApplication({
|
await deployApplication({
|
||||||
// applicationId: job.data.applicationId,
|
applicationId: job.data.applicationId,
|
||||||
// titleLog: job.data.titleLog,
|
titleLog: job.data.titleLog,
|
||||||
// descriptionLog: job.data.descriptionLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
// } else if (job.data.applicationType === "compose") {
|
} else if (job.data.applicationType === "compose") {
|
||||||
// await updateCompose(job.data.composeId, {
|
await updateCompose(job.data.composeId, {
|
||||||
// composeStatus: "running",
|
composeStatus: "running",
|
||||||
// });
|
});
|
||||||
// if (job.data.type === "deploy") {
|
if (job.data.type === "deploy") {
|
||||||
// await deployCompose({
|
await deployCompose({
|
||||||
// composeId: job.data.composeId,
|
composeId: job.data.composeId,
|
||||||
// titleLog: job.data.titleLog,
|
titleLog: job.data.titleLog,
|
||||||
// descriptionLog: job.data.descriptionLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
// });
|
});
|
||||||
// } else if (job.data.type === "redeploy") {
|
} else if (job.data.type === "redeploy") {
|
||||||
// await rebuildCompose({
|
await rebuildCompose({
|
||||||
// composeId: job.data.composeId,
|
composeId: job.data.composeId,
|
||||||
// titleLog: job.data.titleLog,
|
titleLog: job.data.titleLog,
|
||||||
// descriptionLog: job.data.descriptionLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// } catch (error) {
|
} catch (error) {
|
||||||
// console.log("Error", error);
|
console.log("Error", error);
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// autorun: false,
|
autorun: false,
|
||||||
// connection: redisConfig,
|
connection: redisConfig,
|
||||||
// },
|
},
|
||||||
// );
|
);
|
||||||
|
|
||||||
export const cleanQueuesByApplication = async (applicationId: string) => {
|
export const cleanQueuesByApplication = async (applicationId: string) => {
|
||||||
const jobs = await myQueue.getJobs(["waiting", "delayed"]);
|
const jobs = await myQueue.getJobs(["waiting", "delayed"]);
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import {
|
|||||||
} from "../api/services/compose";
|
} from "../api/services/compose";
|
||||||
|
|
||||||
export const redisConfig: ConnectionOptions = {
|
export const redisConfig: ConnectionOptions = {
|
||||||
host: "31.220.108.27",
|
// host: "31.220.108.27",
|
||||||
password: "xYBugfHkULig1iLN",
|
// password: "xYBugfHkULig1iLN",
|
||||||
// host: process.env.NODE_ENV === "production" ? "dokploy-redis" : "127.0.0.1",
|
host: process.env.NODE_ENV === "production" ? "dokploy-redis" : "127.0.0.1",
|
||||||
port: 1233,
|
// port: 1233,
|
||||||
};
|
};
|
||||||
// TODO: maybe add a options to clean the queue to the times
|
// TODO: maybe add a options to clean the queue to the times
|
||||||
const myQueue = new Queue("deployments", {
|
const myQueue = new Queue("deployments", {
|
||||||
@@ -48,83 +48,3 @@ function createRedisConnection(server: Server) {
|
|||||||
port: "6379",
|
port: "6379",
|
||||||
} as ConnectionOptions;
|
} as ConnectionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupServerQueueAndWorker(server: Server) {
|
|
||||||
const connection = createRedisConnection(server);
|
|
||||||
|
|
||||||
if (!workersMap.has(server.serverId)) {
|
|
||||||
const queue = new Queue(`deployments-${server.serverId}`, {
|
|
||||||
connection,
|
|
||||||
});
|
|
||||||
const worker = new Worker(
|
|
||||||
`deployments-${server.serverId}`,
|
|
||||||
async (job: Job<DeploymentJob>) => {
|
|
||||||
// Ejecuta el trabajo de despliegue
|
|
||||||
try {
|
|
||||||
if (job.data.applicationType === "application") {
|
|
||||||
await updateApplicationStatus(job.data.applicationId, "running");
|
|
||||||
if (job.data.type === "redeploy") {
|
|
||||||
await rebuildApplication({
|
|
||||||
applicationId: job.data.applicationId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
} else if (job.data.type === "deploy") {
|
|
||||||
await deployApplication({
|
|
||||||
applicationId: job.data.applicationId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (job.data.applicationType === "compose") {
|
|
||||||
await updateCompose(job.data.composeId, {
|
|
||||||
composeStatus: "running",
|
|
||||||
});
|
|
||||||
if (job.data.type === "deploy") {
|
|
||||||
await deployCompose({
|
|
||||||
composeId: job.data.composeId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
} else if (job.data.type === "redeploy") {
|
|
||||||
await rebuildCompose({
|
|
||||||
composeId: job.data.composeId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error", error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
limiter: {
|
|
||||||
max: 1,
|
|
||||||
duration: 1000,
|
|
||||||
},
|
|
||||||
connection,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
// Almacena worker y queue para reutilizar
|
|
||||||
workersMap.set(server.serverId, worker);
|
|
||||||
queuesMap.set(server.serverId, queue);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
queue: queuesMap.get(server.serverId),
|
|
||||||
worker: workersMap.get(server.serverId),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function enqueueDeploymentJob(
|
|
||||||
serverId: string,
|
|
||||||
jobData: DeploymentJob,
|
|
||||||
) {
|
|
||||||
const server = await findServerById(serverId);
|
|
||||||
const { queue } = await setupServerQueueAndWorker(server);
|
|
||||||
|
|
||||||
await queue?.add(`deployments-${serverId}`, jobData, {
|
|
||||||
removeOnComplete: true,
|
|
||||||
removeOnFail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
setupTerminalWebSocketServer,
|
setupTerminalWebSocketServer,
|
||||||
} from "./wss/terminal";
|
} from "./wss/terminal";
|
||||||
import { IS_CLOUD } from "./constants";
|
import { IS_CLOUD } from "./constants";
|
||||||
|
import { deploymentWorker } from "./queues/deployments-queue";
|
||||||
|
|
||||||
config({ path: ".env" });
|
config({ path: ".env" });
|
||||||
const PORT = Number.parseInt(process.env.PORT || "3000", 10);
|
const PORT = Number.parseInt(process.env.PORT || "3000", 10);
|
||||||
@@ -68,7 +69,7 @@ void app.prepare().then(async () => {
|
|||||||
|
|
||||||
server.listen(PORT);
|
server.listen(PORT);
|
||||||
console.log("Server Started:", PORT);
|
console.log("Server Started:", PORT);
|
||||||
// deploymentWorker.run();
|
deploymentWorker.run();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Main Server Error", e);
|
console.error("Main Server Error", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,14 +98,24 @@ Compose Type: ${composeType} ✅`;
|
|||||||
});
|
});
|
||||||
|
|
||||||
const bashCommand = `
|
const bashCommand = `
|
||||||
set -e;
|
set -e
|
||||||
echo "${logBox}" >> ${logPath};
|
{
|
||||||
${newCompose}
|
echo "${logBox}" >> "${logPath}"
|
||||||
${envCommand}
|
|
||||||
cd ${projectPath} || exit 1;
|
${newCompose}
|
||||||
docker ${command.split(" ").join(" ")} >> ${logPath} 2>&1;
|
|
||||||
echo "Docker Compose Deployed: ✅" >> ${logPath};
|
${envCommand}
|
||||||
`;
|
|
||||||
|
cd "${projectPath}";
|
||||||
|
|
||||||
|
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
||||||
|
|
||||||
|
echo "Docker Compose Deployed: ✅" >> "${logPath}"
|
||||||
|
} || {
|
||||||
|
echo "Error: ❌ Script execution failed" >> "${logPath}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
return await execAsyncRemote(compose.serverId, bashCommand);
|
return await execAsyncRemote(compose.serverId, bashCommand);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ export const buildHeroku = async (
|
|||||||
if (writeStream.writable) {
|
if (writeStream.writable) {
|
||||||
writeStream.write(data);
|
writeStream.write(data);
|
||||||
}
|
}
|
||||||
// Stream the data
|
|
||||||
console.log(data);
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export const getNixpacksCommand = (
|
|||||||
/* No need for any start command, since we'll use nginx later on */
|
/* No need for any start command, since we'll use nginx later on */
|
||||||
args.push("--no-error-without-start");
|
args.push("--no-error-without-start");
|
||||||
}
|
}
|
||||||
|
console.log("args", args);
|
||||||
const command = `nixpacks ${args.join(" ")}`;
|
const command = `nixpacks ${args.join(" ")}`;
|
||||||
const bashCommand = `
|
const bashCommand = `
|
||||||
echo "Starting nixpacks build..." >> ${logPath};
|
echo "Starting nixpacks build..." >> ${logPath};
|
||||||
@@ -108,6 +108,9 @@ echo "Nixpacks build completed." >> ${logPath};
|
|||||||
Then, remove the container and create a static build.
|
Then, remove the container and create a static build.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (publishDirectory) {
|
||||||
|
}
|
||||||
|
|
||||||
// if (publishDirectory) {
|
// if (publishDirectory) {
|
||||||
// bashCommand += `
|
// bashCommand += `
|
||||||
// docker create --name ${buildContainerId} ${appName}
|
// docker create --name ${buildContainerId} ${appName}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export const writeDomainsToComposeRemote = async (
|
|||||||
domains: Domain[],
|
domains: Domain[],
|
||||||
) => {
|
) => {
|
||||||
if (!domains.length) {
|
if (!domains.length) {
|
||||||
return;
|
return "";
|
||||||
}
|
}
|
||||||
const composeConverted = await addDomainToCompose(compose, domains);
|
const composeConverted = await addDomainToCompose(compose, domains);
|
||||||
const path = getComposePath(compose);
|
const path = getComposePath(compose);
|
||||||
|
|||||||
@@ -214,9 +214,17 @@ export const startServiceRemote = async (serverId: string, appName: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeService = async (appName: string) => {
|
export const removeService = async (
|
||||||
|
appName: string,
|
||||||
|
serverId?: string | null,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
await execAsync(`docker service rm ${appName}`);
|
const command = `docker service rm ${appName}`;
|
||||||
|
if (serverId) {
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
|
} else {
|
||||||
|
await execAsync(command);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
COMPOSE_PATH,
|
COMPOSE_PATH,
|
||||||
MONITORING_PATH,
|
MONITORING_PATH,
|
||||||
} from "@/server/constants";
|
} from "@/server/constants";
|
||||||
import { execAsync } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
|
|
||||||
export const recreateDirectory = async (pathFolder: string): Promise<void> => {
|
export const recreateDirectory = async (pathFolder: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
@@ -34,31 +34,54 @@ export const removeFileOrDirectory = async (path: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeDirectoryCode = async (appName: string) => {
|
export const removeDirectoryCode = async (
|
||||||
|
appName: string,
|
||||||
|
serverId?: string | null,
|
||||||
|
) => {
|
||||||
const directoryPath = path.join(APPLICATIONS_PATH, appName);
|
const directoryPath = path.join(APPLICATIONS_PATH, appName);
|
||||||
|
const command = `rm -rf ${directoryPath}`;
|
||||||
try {
|
try {
|
||||||
await execAsync(`rm -rf ${directoryPath}`);
|
if (serverId) {
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
|
} else {
|
||||||
|
await execAsync(command);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error to remove ${directoryPath}: ${error}`);
|
console.error(`Error to remove ${directoryPath}: ${error}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeComposeDirectory = async (appName: string) => {
|
export const removeComposeDirectory = async (
|
||||||
|
appName: string,
|
||||||
|
serverId?: string | null,
|
||||||
|
) => {
|
||||||
const directoryPath = path.join(COMPOSE_PATH, appName);
|
const directoryPath = path.join(COMPOSE_PATH, appName);
|
||||||
|
const command = `rm -rf ${directoryPath}`;
|
||||||
try {
|
try {
|
||||||
await execAsync(`rm -rf ${directoryPath}`);
|
if (serverId) {
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
|
} else {
|
||||||
|
await execAsync(command);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error to remove ${directoryPath}: ${error}`);
|
console.error(`Error to remove ${directoryPath}: ${error}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeMonitoringDirectory = async (appName: string) => {
|
export const removeMonitoringDirectory = async (
|
||||||
|
appName: string,
|
||||||
|
serverId?: string | null,
|
||||||
|
) => {
|
||||||
const directoryPath = path.join(MONITORING_PATH, appName);
|
const directoryPath = path.join(MONITORING_PATH, appName);
|
||||||
|
const command = `rm -rf ${directoryPath}`;
|
||||||
try {
|
try {
|
||||||
await execAsync(`rm -rf ${directoryPath}`);
|
if (serverId) {
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
|
} else {
|
||||||
|
await execAsync(command);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error to remove ${directoryPath}: ${error}`);
|
console.error(`Error to remove ${directoryPath}: ${error}`);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ export const execAsyncRemote = async (
|
|||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
resolve({ stdout, stderr });
|
resolve({ stdout, stderr });
|
||||||
} else {
|
} else {
|
||||||
reject(new Error(`Command exited with code ${code}`));
|
reject(
|
||||||
|
new Error(
|
||||||
|
`Command exited with code ${code}. Stderr: ${stderr}, command: ${command}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("data", (data: string) => {
|
.on("data", (data: string) => {
|
||||||
|
|||||||
@@ -181,6 +181,11 @@ export const getBitbucketCloneCommand = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!bitbucketId) {
|
if (!bitbucketId) {
|
||||||
|
const command = `
|
||||||
|
echo "Error: ❌ Bitbucket Provider not found" >> ${logPath};
|
||||||
|
exit 1;
|
||||||
|
`;
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
message: "Bitbucket Provider not found",
|
message: "Bitbucket Provider not found",
|
||||||
@@ -198,7 +203,7 @@ export const getBitbucketCloneCommand = async (
|
|||||||
rm -rf ${outputPath};
|
rm -rf ${outputPath};
|
||||||
mkdir -p ${outputPath};
|
mkdir -p ${outputPath};
|
||||||
if ! git clone --branch ${bitbucketBranch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
if ! git clone --branch ${bitbucketBranch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
||||||
echo "[ERROR] Fail to clone the repository ${repoclone}" >> ${logPath};
|
echo "❌ [ERROR] Fail to clone the repository ${repoclone}" >> ${logPath};
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { TRPCError } from "@trpc/server";
|
|||||||
import { recreateDirectory } from "../filesystem/directory";
|
import { recreateDirectory } from "../filesystem/directory";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
import { Compose } from "@/server/api/services/compose";
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
|
|
||||||
export const cloneGitRepository = async (
|
export const cloneGitRepository = async (
|
||||||
entity: {
|
entity: {
|
||||||
@@ -108,6 +108,12 @@ export const getCustomGitCloneCommand = async (
|
|||||||
} = entity;
|
} = entity;
|
||||||
|
|
||||||
if (!customGitUrl || !customGitBranch) {
|
if (!customGitUrl || !customGitBranch) {
|
||||||
|
const command = `
|
||||||
|
echo "Error: ❌ Repository not found" >> ${logPath};
|
||||||
|
exit 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "BAD_REQUEST",
|
code: "BAD_REQUEST",
|
||||||
message: "Error: Repository not found",
|
message: "Error: Repository not found",
|
||||||
@@ -143,7 +149,7 @@ export const getCustomGitCloneCommand = async (
|
|||||||
|
|
||||||
command.push(
|
command.push(
|
||||||
`if ! git clone --branch ${customGitBranch} --depth 1 --progress ${customGitUrl} ${outputPath} >> ${logPath} 2>&1; then
|
`if ! git clone --branch ${customGitBranch} --depth 1 --progress ${customGitUrl} ${outputPath} >> ${logPath} 2>&1; then
|
||||||
echo "[ERROR] Fail to clone the repository ${customGitUrl}" >> ${logPath};
|
echo "❌ [ERROR] Fail to clone the repository ${customGitUrl}" >> ${logPath};
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -151,7 +151,20 @@ export const getGithubCloneCommand = async (
|
|||||||
) => {
|
) => {
|
||||||
const { appName, repository, owner, branch, githubId, serverId } = entity;
|
const { appName, repository, owner, branch, githubId, serverId } = entity;
|
||||||
|
|
||||||
if (!githubId || !serverId) {
|
if (!serverId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Server not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!githubId) {
|
||||||
|
const command = `
|
||||||
|
echo "Error: ❌ Github Provider not found" >> ${logPath};
|
||||||
|
exit 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
await executeCommand(serverId, command);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
message: "GitHub Provider not found",
|
message: "GitHub Provider not found",
|
||||||
@@ -192,7 +205,7 @@ export const getGithubCloneCommand = async (
|
|||||||
rm -rf ${outputPath};
|
rm -rf ${outputPath};
|
||||||
mkdir -p ${outputPath};
|
mkdir -p ${outputPath};
|
||||||
if ! git clone --branch ${branch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
if ! git clone --branch ${branch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
||||||
echo "[ERROR] Fallo al clonar el repositorio ${repoclone}" >> ${logPath};
|
echo "❌ [ERROR] Fallo al clonar el repositorio ${repoclone}" >> ${logPath};
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
||||||
|
|||||||
@@ -179,6 +179,12 @@ export const getGitlabCloneCommand = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!gitlabId) {
|
if (!gitlabId) {
|
||||||
|
const command = `
|
||||||
|
echo "Error: ❌ Gitlab Provider not found" >> ${logPath};
|
||||||
|
exit 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
message: "Gitlab Provider not found",
|
message: "Gitlab Provider not found",
|
||||||
@@ -203,7 +209,7 @@ export const getGitlabCloneCommand = async (
|
|||||||
exit 1; # Exit with error code
|
exit 1; # Exit with error code
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await executeCommand(serverId, bashCommand);
|
await execAsyncRemote(serverId, bashCommand);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +224,7 @@ export const getGitlabCloneCommand = async (
|
|||||||
rm -rf ${outputPath};
|
rm -rf ${outputPath};
|
||||||
mkdir -p ${outputPath};
|
mkdir -p ${outputPath};
|
||||||
if ! git clone --branch ${gitlabBranch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
if ! git clone --branch ${gitlabBranch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
||||||
echo "[ERROR] Fail to clone the repository ${repoclone}" >> ${logPath};
|
echo "❌ [ERROR] Fail to clone the repository ${repoclone}" >> ${logPath};
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
||||||
|
|||||||
@@ -47,9 +47,20 @@ export const createTraefikConfig = (appName: string) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeTraefikConfig = async (appName: string) => {
|
export const removeTraefikConfig = async (
|
||||||
|
appName: string,
|
||||||
|
serverId?: string | null,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
|
|
||||||
|
if (serverId) {
|
||||||
|
await execAsyncRemote(serverId, `rm ${configPath}`);
|
||||||
|
} else {
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
await fs.promises.unlink(configPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
await fs.promises.unlink(configPath);
|
await fs.promises.unlink(configPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { dump, load } from "js-yaml";
|
|||||||
import type { ApplicationNested } from "../builders";
|
import type { ApplicationNested } from "../builders";
|
||||||
import type { FileConfig } from "./file-types";
|
import type { FileConfig } from "./file-types";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
|
import { writeTraefikConfigRemote } from "./application";
|
||||||
|
|
||||||
export const addMiddleware = (config: FileConfig, middlewareName: string) => {
|
export const addMiddleware = (config: FileConfig, middlewareName: string) => {
|
||||||
if (config.http?.routers) {
|
if (config.http?.routers) {
|
||||||
@@ -43,7 +44,7 @@ export const deleteMiddleware = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteAllMiddlewares = (application: ApplicationNested) => {
|
export const deleteAllMiddlewares = async (application: ApplicationNested) => {
|
||||||
const config = loadMiddlewares<FileConfig>();
|
const config = loadMiddlewares<FileConfig>();
|
||||||
const { security, appName, redirects } = application;
|
const { security, appName, redirects } = application;
|
||||||
|
|
||||||
@@ -60,7 +61,11 @@ export const deleteAllMiddlewares = (application: ApplicationNested) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeMiddleware(config);
|
if (application.serverId) {
|
||||||
|
await writeTraefikConfigRemote(config, "middlewares", application.serverId);
|
||||||
|
} else {
|
||||||
|
writeMiddleware(config);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadMiddlewares = <T>() => {
|
export const loadMiddlewares = <T>() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user