feat(multi server): add env and toggle dashboard remote

This commit is contained in:
Mauricio Siu 2024-09-21 01:44:31 -06:00
parent 497d45129c
commit 8bf6a22db8
7 changed files with 102 additions and 52 deletions

View File

@ -29,14 +29,13 @@ import { SetupServer } from "./setup-server";
import { UpdateServer } from "./update-server";
import { ShowTraefikFileSystemModal } from "./show-traefik-file-system-modal";
import { ShowModalLogs } from "../web-server/show-modal-logs";
import { ToggleTraefikDashboard } from "./toggle-traefik-dashboard";
import { EditTraefikEnv } from "../web-server/edit-traefik-env";
export const ShowServers = () => {
const { data, refetch } = api.server.all.useQuery();
const { mutateAsync } = api.server.remove.useMutation();
const { data: sshKeys } = api.sshKey.all.useQuery();
const { mutateAsync: toggleDashboard, isLoading: toggleDashboardIsLoading } =
api.settings.toggleDashboard.useMutation();
const {
mutateAsync: cleanDockerBuilder,
isLoading: cleanDockerBuilderIsLoading,
@ -63,9 +62,6 @@ export const ShowServers = () => {
const { mutateAsync: cleanAll, isLoading: cleanAllIsLoading } =
api.settings.cleanAll.useMutation();
const { data: haveTraefikDashboardPortEnabled, refetch: refetchDashboard } =
api.settings.haveTraefikDashboardPortEnabled.useQuery();
return (
<div className="p-6 space-y-6">
<div className="space-y-2 flex flex-row justify-between items-end">
@ -226,35 +222,18 @@ export const ShowServers = () => {
>
<span>Watch logs</span>
</ShowModalLogs>
<EditTraefikEnv serverId={server.serverId}>
<DropdownMenuItem
onSelect={(e) => e.preventDefault()}
className="w-full cursor-pointer space-x-3"
>
<span>Modify Env</span>
</DropdownMenuItem>
</EditTraefikEnv>
<ToggleTraefikDashboard
serverId={server.serverId}
/>
{/* <DropdownMenuItem
onClick={async () => {
await toggleDashboard({
enableDashboard:
!haveTraefikDashboardPortEnabled,
serverId: server.serverId,
})
.then(async () => {
toast.success(
`${haveTraefikDashboardPortEnabled ? "Disabled" : "Enabled"} Dashboard`,
);
refetchDashboard();
})
.catch(() => {
toast.error(
`${haveTraefikDashboardPortEnabled ? "Disabled" : "Enabled"} Dashboard`,
);
});
}}
className="w-full cursor-pointer space-x-3"
>
<span>
{haveTraefikDashboardPortEnabled
? "Disable"
: "Enable"}{" "}
Dashboard
</span>
</DropdownMenuItem> */}
<DropdownMenuSeparator />
<DropdownMenuLabel>Storage</DropdownMenuLabel>

View File

@ -0,0 +1,48 @@
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
import { api } from "@/utils/api";
import { toast } from "sonner";
interface Props {
serverId: string;
}
export const ToggleTraefikDashboard = ({ serverId }: Props) => {
const { mutateAsync: toggleDashboard, isLoading: toggleDashboardIsLoading } =
api.settings.toggleDashboard.useMutation();
const { data: haveTraefikDashboardPortEnabled, refetch: refetchDashboard } =
api.settings.haveTraefikDashboardPortEnabled.useQuery(
{
serverId,
},
{
enabled: !!serverId,
},
);
return (
<>
<DropdownMenuItem
onClick={async () => {
await toggleDashboard({
enableDashboard: !haveTraefikDashboardPortEnabled,
serverId: serverId,
})
.then(async () => {
toast.success(
`${haveTraefikDashboardPortEnabled ? "Disabled" : "Enabled"} Dashboard`,
);
refetchDashboard();
})
.catch(() => {
toast.error(
`${haveTraefikDashboardPortEnabled ? "Disabled" : "Enabled"} Dashboard`,
);
});
}}
className="w-full cursor-pointer space-x-3"
>
<span>
{haveTraefikDashboardPortEnabled ? "Disable" : "Enable"} Dashboard
</span>
</DropdownMenuItem>
</>
);
};

View File

@ -65,6 +65,7 @@ export const EditTraefikEnv = ({ children, serverId }: Props) => {
const onSubmit = async (data: Schema) => {
await mutateAsync({
env: data.env,
serverId,
})
.then(async () => {
toast.success("Traefik Env Updated");

View File

@ -88,6 +88,7 @@ export const settingsRouter = createTRPCRouter({
.mutation(async ({ input }) => {
await initializeTraefik({
enableDashboard: input.enableDashboard,
serverId: input.serverId,
});
return true;
}),
@ -357,6 +358,7 @@ export const settingsRouter = createTRPCRouter({
if (input?.serverId) {
const result = await execAsyncRemote(input.serverId, command);
console.log(result);
return result.stdout.trim();
}
const result = await execAsync(command);
@ -369,25 +371,38 @@ export const settingsRouter = createTRPCRouter({
const envs = prepareEnvironmentVariables(input.env);
await initializeTraefik({
env: envs,
serverId: input.serverId,
});
return true;
}),
haveTraefikDashboardPortEnabled: adminProcedure.query(async () => {
const { stdout } = await execAsync(
"docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik",
);
haveTraefikDashboardPortEnabled: adminProcedure
.input(apiStorage)
.query(async ({ input }) => {
const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`;
const parsed: any[] = JSON.parse(stdout.trim());
for (const port of parsed) {
if (port.PublishedPort === 8080) {
return true;
let stdout = "";
if (input?.serverId) {
const result = await execAsyncRemote(input.serverId, command);
stdout = result.stdout;
} else {
const result = await execAsync(
"docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik",
);
stdout = result.stdout;
}
}
return false;
}),
const parsed: any[] = JSON.parse(stdout.trim());
console.log(parsed);
for (const port of parsed) {
if (port.PublishedPort === 8080) {
return true;
}
}
return false;
}),
readStatsLogs: adminProcedure.input(apiReadStatsLogs).query(({ input }) => {
const rawConfig = readMonitoringConfig();

View File

@ -135,7 +135,6 @@ export const getContainersByAppLabel = async (
let stderr = "";
const command = `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`;
console.log(command);
if (serverId) {
const result = await execAsyncRemote(serverId, command);
stdout = result.stdout;

View File

@ -2,10 +2,11 @@ import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode";
import { dump } from "js-yaml";
import { docker, paths } from "../constants";
import { pullImage } from "../utils/docker/utils";
import { paths } from "../constants";
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
import type { FileConfig } from "../utils/traefik/file-types";
import type { MainTraefikConfig } from "../utils/traefik/types";
import { getRemoteDocker } from "../utils/servers/remote-docker";
const TRAEFIK_SSL_PORT =
Number.parseInt(process.env.TRAEFIK_SSL_PORT ?? "", 10) || 443;
@ -14,13 +15,15 @@ const TRAEFIK_PORT = Number.parseInt(process.env.TRAEFIK_PORT ?? "", 10) || 80;
interface TraefikOptions {
enableDashboard?: boolean;
env?: string[];
serverId?: string;
}
export const initializeTraefik = async ({
enableDashboard = false,
env,
serverId,
}: TraefikOptions = {}) => {
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths();
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
const imageName = "traefik:v3.1.2";
const containerName = "dokploy-traefik";
const settings: CreateServiceOptions = {
@ -84,8 +87,13 @@ export const initializeTraefik = async ({
],
},
};
const docker = await getRemoteDocker(serverId);
try {
await pullImage(imageName);
if (serverId) {
await pullRemoteImage(imageName, serverId);
} else {
await pullImage(imageName);
}
const service = docker.getService(containerName);
const inspect = await service.inspect();

View File

@ -3,7 +3,7 @@ import { docker } from "@/server/constants";
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;
const server = await findServerById(serverId);
if (!server.sshKeyId) return docker;