refactor(dokploy): add flag to prevent run commands when is cloud

This commit is contained in:
Mauricio Siu
2024-10-23 00:54:40 -06:00
parent 548df8c0f4
commit 017bdd2778
13 changed files with 278 additions and 456 deletions

View File

@@ -53,7 +53,10 @@ export const apiCreateDestination = createSchema
endpoint: true,
secretAccessKey: true,
})
.required();
.required()
.extend({
serverId: z.string().optional(),
});
export const apiFindOneDestination = createSchema
.pick({
@@ -77,4 +80,7 @@ export const apiUpdateDestination = createSchema
secretAccessKey: true,
destinationId: true,
})
.required();
.required()
.extend({
serverId: z.string().optional(),
});

View File

@@ -37,7 +37,6 @@ export * from "./services/application";
export * from "./setup/config-paths";
export * from "./setup/postgres-setup";
export * from "./setup/redis-setup";
export * from "./setup/registry-setup";
export * from "./setup/server-setup";
export * from "./setup/setup";
export * from "./setup/traefik-setup";
@@ -97,7 +96,6 @@ export * from "./utils/traefik/domain";
export * from "./utils/traefik/file-types";
export * from "./utils/traefik/middleware";
export * from "./utils/traefik/redirect";
export * from "./utils/traefik/registry";
export * from "./utils/traefik/security";
export * from "./utils/traefik/types";
export * from "./utils/traefik/web-server";

View File

@@ -1,14 +1,9 @@
import { db } from "@/server/db";
import { type apiCreateRegistry, registry } from "@/server/db/schema";
import { initializeRegistry } from "@/server/setup/registry-setup";
import { removeService } from "@/server/utils/docker/utils";
import { execAsync, execAsyncRemote } from "@/server/utils/process/execAsync";
import {
manageRegistry,
removeSelfHostedRegistry,
} from "@/server/utils/traefik/registry";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { IS_CLOUD } from "../constants";
export type Registry = typeof registry.$inferSelect;
@@ -32,6 +27,13 @@ export const createRegistry = async (
message: "Error input: Inserting registry",
});
}
if (IS_CLOUD && !input.serverId && input.serverId !== "none") {
throw new TRPCError({
code: "NOT_FOUND",
message: "Select a server to add the registry",
});
}
const loginCommand = `echo ${input.password} | docker login ${input.registryUrl} --username ${input.username} --password-stdin`;
if (input.serverId && input.serverId !== "none") {
await execAsyncRemote(input.serverId, loginCommand);
@@ -58,13 +60,10 @@ export const removeRegistry = async (registryId: string) => {
});
}
if (response.registryType === "selfHosted") {
await removeSelfHostedRegistry();
await removeService("dokploy-registry");
if (!IS_CLOUD) {
await execAsync(`docker logout ${response.registryUrl}`);
}
await execAsync(`docker logout ${response.registryUrl}`);
return response;
} catch (error) {
throw new TRPCError({
@@ -89,12 +88,19 @@ export const updateRegistry = async (
.returning()
.then((res) => res[0]);
if (response?.registryType === "selfHosted") {
await manageRegistry(response);
await initializeRegistry(response.username, response.password);
}
const loginCommand = `echo ${response?.password} | docker login ${response?.registryUrl} --username ${response?.username} --password-stdin`;
if (
IS_CLOUD &&
!registryData?.serverId &&
registryData?.serverId !== "none"
) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Select a server to add the registry",
});
}
if (registryData?.serverId && registryData?.serverId !== "none") {
await execAsyncRemote(registryData.serverId, loginCommand);
} else if (response?.registryType === "cloud") {

View File

@@ -1,91 +0,0 @@
import type { CreateServiceOptions } from "dockerode";
import { docker, paths } from "../constants";
import { generatePassword } from "../templates/utils";
import { pullImage } from "../utils/docker/utils";
import { execAsync } from "../utils/process/execAsync";
export const initializeRegistry = async (
username: string,
password: string,
) => {
const { REGISTRY_PATH } = paths();
const imageName = "registry:2.8.3";
const containerName = "dokploy-registry";
await generateRegistryPassword(username, password);
const randomPass = generatePassword();
const settings: CreateServiceOptions = {
Name: containerName,
TaskTemplate: {
ContainerSpec: {
Image: imageName,
Env: [
"REGISTRY_STORAGE_DELETE_ENABLED=true",
"REGISTRY_AUTH=htpasswd",
"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm",
"REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd",
`REGISTRY_HTTP_SECRET=${randomPass}`,
],
Mounts: [
{
Type: "bind",
Source: `${REGISTRY_PATH}/htpasswd`,
Target: "/auth/htpasswd",
ReadOnly: true,
},
{
Type: "volume",
Source: "registry-data",
Target: "/var/lib/registry",
ReadOnly: false,
},
],
},
Networks: [{ Target: "dokploy-network" }],
Placement: {
Constraints: ["node.role==manager"],
},
},
Mode: {
Replicated: {
Replicas: 1,
},
},
EndpointSpec: {
Ports: [
{
TargetPort: 5000,
PublishedPort: 5000,
Protocol: "tcp",
PublishMode: "host",
},
],
},
};
try {
await pullImage(imageName);
const service = docker.getService(containerName);
const inspect = await service.inspect();
await service.update({
version: Number.parseInt(inspect.Version.Index),
...settings,
});
console.log("Registry Started ✅");
} catch (error) {
await docker.createService(settings);
console.log("Registry Not Found: Starting ✅");
}
};
const generateRegistryPassword = async (username: string, password: string) => {
try {
const { REGISTRY_PATH } = paths();
const command = `htpasswd -nbB ${username} "${password}" > ${REGISTRY_PATH}/htpasswd`;
const result = await execAsync(command);
console.log("Password generated ✅");
return result.stdout.trim();
} catch (error) {
console.error("Error generating password:", error);
return null;
}
};

View File

@@ -55,8 +55,6 @@ export const cloneGitRepository = async (
await addHostToKnownHosts(customGitUrl);
}
await recreateDirectory(outputPath);
// const command = `GIT_SSH_COMMAND="ssh -i ${keyPath} -o UserKnownHostsFile=${knownHostsPath}" git clone --branch ${customGitBranch} --depth 1 ${customGitUrl} ${gitCopyPath} --progress`;
// const { stdout, stderr } = await execAsync(command);
writeStream.write(
`\nCloning Repo Custom ${customGitUrl} to ${outputPath}: ✅\n`,
);

View File

@@ -1,75 +0,0 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { paths } from "@/server/constants";
import type { Registry } from "@/server/services/registry";
import { dump, load } from "js-yaml";
import { removeDirectoryIfExistsContent } from "../filesystem/directory";
import type { FileConfig, HttpRouter } from "./file-types";
export const manageRegistry = async (registry: Registry) => {
const { REGISTRY_PATH } = paths();
if (!existsSync(REGISTRY_PATH)) {
mkdirSync(REGISTRY_PATH, { recursive: true });
}
const appName = "dokploy-registry";
const config: FileConfig = loadOrCreateConfig();
const serviceName = `${appName}-service`;
const routerName = `${appName}-router`;
config.http = config.http || { routers: {}, services: {} };
config.http.routers = config.http.routers || {};
config.http.services = config.http.services || {};
config.http.routers[routerName] = await createRegistryRouterConfig(registry);
config.http.services[serviceName] = {
loadBalancer: {
servers: [{ url: `http://${appName}:5000` }],
passHostHeader: true,
},
};
const yamlConfig = dump(config);
const configFile = join(REGISTRY_PATH, "registry.yml");
writeFileSync(configFile, yamlConfig);
};
export const removeSelfHostedRegistry = async () => {
const { REGISTRY_PATH } = paths();
await removeDirectoryIfExistsContent(REGISTRY_PATH);
};
const createRegistryRouterConfig = async (registry: Registry) => {
const { registryUrl } = registry;
const routerConfig: HttpRouter = {
rule: `Host(\`${registryUrl}\`)`,
service: "dokploy-registry-service",
middlewares: ["redirect-to-https"],
entryPoints: [
"web",
...(process.env.NODE_ENV === "production" ? ["websecure"] : []),
],
...(process.env.NODE_ENV === "production"
? {
tls: { certResolver: "letsencrypt" },
}
: {}),
};
return routerConfig;
};
const loadOrCreateConfig = (): FileConfig => {
const { REGISTRY_PATH } = paths();
const configPath = join(REGISTRY_PATH, "registry.yml");
if (existsSync(configPath)) {
const yamlStr = readFileSync(configPath, "utf8");
const parsedConfig = (load(yamlStr) as FileConfig) || {
http: { routers: {}, services: {} },
};
return parsedConfig;
}
return { http: { routers: {}, services: {} } };
};