mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge branch 'canary' into 57-dokploy-api-or-cli
This commit is contained in:
@@ -66,9 +66,10 @@ export const applicationRouter = createTRPCRouter({
|
||||
if (ctx.user.rol === "user") {
|
||||
await addNewService(ctx.user.authId, newApplication.applicationId);
|
||||
}
|
||||
|
||||
return newApplication;
|
||||
} catch (error) {
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof TRPCError) {
|
||||
throw error;
|
||||
}
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to create the application",
|
||||
|
||||
@@ -34,13 +34,18 @@ import { nanoid } from "nanoid";
|
||||
import { removeDeploymentsByComposeId } from "../services/deployment";
|
||||
import { removeComposeDirectory } from "@/server/utils/filesystem/directory";
|
||||
import { createCommand } from "@/server/utils/builders/compose";
|
||||
import { loadTemplateModule, readComposeFile } from "@/templates/utils";
|
||||
import {
|
||||
generatePassword,
|
||||
loadTemplateModule,
|
||||
readComposeFile,
|
||||
} from "@/templates/utils";
|
||||
import { findAdmin } from "../services/admin";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { findProjectById, slugifyProjectName } from "../services/project";
|
||||
import { findProjectById } from "../services/project";
|
||||
import { createMount } from "../services/mount";
|
||||
import type { TemplatesKeys } from "@/templates/types/templates-data.type";
|
||||
import { templates } from "@/templates/templates";
|
||||
import { slugify } from "@/lib/slug";
|
||||
|
||||
export const composeRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -229,7 +234,7 @@ export const composeRouter = createTRPCRouter({
|
||||
|
||||
const project = await findProjectById(input.projectId);
|
||||
|
||||
const projectName = slugifyProjectName(`${project.name}-${input.id}`);
|
||||
const projectName = slugify(`${project.name} ${input.id}`);
|
||||
const { envs, mounts } = generate({
|
||||
serverIp: admin.serverIp,
|
||||
projectName: projectName,
|
||||
@@ -241,6 +246,7 @@ export const composeRouter = createTRPCRouter({
|
||||
env: envs.join("\n"),
|
||||
name: input.id,
|
||||
sourceType: "raw",
|
||||
appName: `${projectName}-${generatePassword(6)}`,
|
||||
});
|
||||
|
||||
if (ctx.user.rol === "user") {
|
||||
|
||||
@@ -49,6 +49,9 @@ export const mariadbRouter = createTRPCRouter({
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof TRPCError) {
|
||||
throw error;
|
||||
}
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting mariadb database",
|
||||
|
||||
@@ -49,6 +49,9 @@ export const mongoRouter = createTRPCRouter({
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof TRPCError) {
|
||||
throw error;
|
||||
}
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting mongo database",
|
||||
|
||||
@@ -50,6 +50,9 @@ export const mysqlRouter = createTRPCRouter({
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof TRPCError) {
|
||||
throw error;
|
||||
}
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting mysql database",
|
||||
|
||||
@@ -49,6 +49,9 @@ export const postgresRouter = createTRPCRouter({
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof TRPCError) {
|
||||
throw error;
|
||||
}
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting postgresql database",
|
||||
|
||||
@@ -17,7 +17,7 @@ import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { manageRegistry } from "@/server/utils/traefik/registry";
|
||||
import { initializeRegistry } from "@/server/setup/registry-setup";
|
||||
import { docker } from "@/server/constants";
|
||||
import { execAsync } from "@/server/utils/process/execAsync";
|
||||
|
||||
export const registryRouter = createTRPCRouter({
|
||||
create: adminProcedure
|
||||
@@ -57,15 +57,11 @@ export const registryRouter = createTRPCRouter({
|
||||
.input(apiTestRegistry)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
const result = await docker.checkAuth({
|
||||
username: input.username,
|
||||
password: input.password,
|
||||
serveraddress: input.registryUrl,
|
||||
});
|
||||
|
||||
const loginCommand = `echo ${input.password} | docker login ${input.registryUrl} --username ${input.username} --password-stdin`;
|
||||
await execAsync(loginCommand);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log("Error Registry:", error);
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -15,11 +15,23 @@ import { findAdmin } from "./admin";
|
||||
import { createTraefikConfig } from "@/server/utils/traefik/application";
|
||||
import { docker } from "@/server/constants";
|
||||
import { getAdvancedStats } from "@/server/monitoring/utilts";
|
||||
import { validUniqueServerAppName } from "./project";
|
||||
export type Application = typeof applications.$inferSelect;
|
||||
|
||||
export const createApplication = async (
|
||||
input: typeof apiCreateApplication._type,
|
||||
) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Application with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return await db.transaction(async (tx) => {
|
||||
const newApplication = await tx
|
||||
.insert(applications)
|
||||
|
||||
@@ -13,10 +13,21 @@ import { join } from "node:path";
|
||||
import { COMPOSE_PATH } from "@/server/constants";
|
||||
import { cloneGithubRepository } from "@/server/utils/providers/github";
|
||||
import { cloneGitRepository } from "@/server/utils/providers/git";
|
||||
import { validUniqueServerAppName } from "./project";
|
||||
|
||||
export type Compose = typeof compose.$inferSelect;
|
||||
|
||||
export const createCompose = async (input: typeof apiCreateCompose._type) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Service with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
const newDestination = await db
|
||||
.insert(compose)
|
||||
.values({
|
||||
@@ -39,6 +50,16 @@ export const createCompose = async (input: typeof apiCreateCompose._type) => {
|
||||
export const createComposeByTemplate = async (
|
||||
input: typeof compose.$inferInsert,
|
||||
) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Service with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
const newDestination = await db
|
||||
.insert(compose)
|
||||
.values({
|
||||
|
||||
@@ -46,9 +46,7 @@ export const getContainers = async () => {
|
||||
.filter((container) => !container.name.includes("dokploy"));
|
||||
|
||||
return containers;
|
||||
} catch (error) {
|
||||
console.error(`Execution error: ${error}`);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const getConfig = async (containerId: string) => {
|
||||
@@ -65,9 +63,7 @@ export const getConfig = async (containerId: string) => {
|
||||
const config = JSON.parse(stdout);
|
||||
|
||||
return config;
|
||||
} catch (error) {
|
||||
console.error(`Execution error: ${error}`);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const getContainersByAppNameMatch = async (appName: string) => {
|
||||
@@ -103,9 +99,7 @@ export const getContainersByAppNameMatch = async (appName: string) => {
|
||||
});
|
||||
|
||||
return containers || [];
|
||||
} catch (error) {
|
||||
console.error(`Execution error: ${error}`);
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -144,9 +138,7 @@ export const getContainersByAppLabel = async (appName: string) => {
|
||||
});
|
||||
|
||||
return containers || [];
|
||||
} catch (error) {
|
||||
console.error(`Execution error: ${error}`);
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
@@ -5,10 +5,22 @@ import { buildMariadb } from "@/server/utils/databases/mariadb";
|
||||
import { pullImage } from "@/server/utils/docker/utils";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq, getTableColumns } from "drizzle-orm";
|
||||
import { validUniqueServerAppName } from "./project";
|
||||
|
||||
export type Mariadb = typeof mariadb.$inferSelect;
|
||||
|
||||
export const createMariadb = async (input: typeof apiCreateMariaDB._type) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Service with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const newMariadb = await db
|
||||
.insert(mariadb)
|
||||
.values({
|
||||
|
||||
@@ -5,10 +5,22 @@ import { buildMongo } from "@/server/utils/databases/mongo";
|
||||
import { pullImage } from "@/server/utils/docker/utils";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq, getTableColumns } from "drizzle-orm";
|
||||
import { validUniqueServerAppName } from "./project";
|
||||
|
||||
export type Mongo = typeof mongo.$inferSelect;
|
||||
|
||||
export const createMongo = async (input: typeof apiCreateMongo._type) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Service with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const newMongo = await db
|
||||
.insert(mongo)
|
||||
.values({
|
||||
|
||||
@@ -5,11 +5,22 @@ import { buildMysql } from "@/server/utils/databases/mysql";
|
||||
import { pullImage } from "@/server/utils/docker/utils";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq, getTableColumns } from "drizzle-orm";
|
||||
import { nanoid } from "nanoid";
|
||||
import { validUniqueServerAppName } from "./project";
|
||||
|
||||
export type MySql = typeof mysql.$inferSelect;
|
||||
|
||||
export const createMysql = async (input: typeof apiCreateMySql._type) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Service with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const newMysql = await db
|
||||
.insert(mysql)
|
||||
.values({
|
||||
|
||||
@@ -5,10 +5,22 @@ import { buildPostgres } from "@/server/utils/databases/postgres";
|
||||
import { pullImage } from "@/server/utils/docker/utils";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq, getTableColumns } from "drizzle-orm";
|
||||
import { validUniqueServerAppName } from "./project";
|
||||
|
||||
export type Postgres = typeof postgres.$inferSelect;
|
||||
|
||||
export const createPostgres = async (input: typeof apiCreatePostgres._type) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Service with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const newPostgres = await db
|
||||
.insert(postgres)
|
||||
.values({
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { db } from "@/server/db";
|
||||
import { type apiCreateProject, projects } from "@/server/db/schema";
|
||||
import {
|
||||
type apiCreateProject,
|
||||
applications,
|
||||
mariadb,
|
||||
mongo,
|
||||
mysql,
|
||||
postgres,
|
||||
projects,
|
||||
redis,
|
||||
} from "@/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { findAdmin } from "./admin";
|
||||
@@ -75,12 +84,40 @@ export const updateProjectById = async (
|
||||
return result;
|
||||
};
|
||||
|
||||
export const slugifyProjectName = (projectName: string): string => {
|
||||
return projectName
|
||||
.toLowerCase()
|
||||
.replace(/[0-9]/g, "")
|
||||
.replace(/[^a-z\s-]/g, "")
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^-+|-+$/g, "");
|
||||
export const validUniqueServerAppName = async (appName: string) => {
|
||||
const query = await db.query.projects.findMany({
|
||||
with: {
|
||||
applications: {
|
||||
where: eq(applications.appName, appName),
|
||||
},
|
||||
mariadb: {
|
||||
where: eq(mariadb.appName, appName),
|
||||
},
|
||||
mongo: {
|
||||
where: eq(mongo.appName, appName),
|
||||
},
|
||||
mysql: {
|
||||
where: eq(mysql.appName, appName),
|
||||
},
|
||||
postgres: {
|
||||
where: eq(postgres.appName, appName),
|
||||
},
|
||||
redis: {
|
||||
where: eq(redis.appName, appName),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Filter out items with non-empty fields
|
||||
const nonEmptyProjects = query.filter(
|
||||
(project) =>
|
||||
project.applications.length > 0 ||
|
||||
project.mariadb.length > 0 ||
|
||||
project.mongo.length > 0 ||
|
||||
project.mysql.length > 0 ||
|
||||
project.postgres.length > 0 ||
|
||||
project.redis.length > 0,
|
||||
);
|
||||
|
||||
return nonEmptyProjects.length === 0;
|
||||
};
|
||||
|
||||
@@ -5,11 +5,23 @@ import { buildRedis } from "@/server/utils/databases/redis";
|
||||
import { pullImage } from "@/server/utils/docker/utils";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { validUniqueServerAppName } from "./project";
|
||||
|
||||
export type Redis = typeof redis.$inferSelect;
|
||||
|
||||
// https://github.com/drizzle-team/drizzle-orm/discussions/1483#discussioncomment-7523881
|
||||
export const createRedis = async (input: typeof apiCreateRedis._type) => {
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
if (!valid) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Service with this 'AppName' already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const newRedis = await db
|
||||
.insert(redis)
|
||||
.values({
|
||||
|
||||
@@ -9,28 +9,35 @@ import {
|
||||
} from "@/server/utils/traefik/registry";
|
||||
import { removeService } from "@/server/utils/docker/utils";
|
||||
import { initializeRegistry } from "@/server/setup/registry-setup";
|
||||
import { execAsync } from "@/server/utils/process/execAsync";
|
||||
|
||||
export type Registry = typeof registry.$inferSelect;
|
||||
|
||||
export const createRegistry = async (input: typeof apiCreateRegistry._type) => {
|
||||
const admin = await findAdmin();
|
||||
|
||||
const newRegistry = await db
|
||||
.insert(registry)
|
||||
.values({
|
||||
...input,
|
||||
adminId: admin.adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
return await db.transaction(async (tx) => {
|
||||
const newRegistry = await tx
|
||||
.insert(registry)
|
||||
.values({
|
||||
...input,
|
||||
adminId: admin.adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newRegistry) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting registry",
|
||||
});
|
||||
}
|
||||
return newRegistry;
|
||||
if (!newRegistry) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting registry",
|
||||
});
|
||||
}
|
||||
|
||||
const loginCommand = `echo ${input.password} | docker login ${input.registryUrl} --username ${input.username} --password-stdin`;
|
||||
await execAsync(loginCommand);
|
||||
|
||||
return newRegistry;
|
||||
});
|
||||
};
|
||||
|
||||
export const removeRegistry = async (registryId: string) => {
|
||||
@@ -53,6 +60,8 @@ export const removeRegistry = async (registryId: string) => {
|
||||
await removeService("dokploy-registry");
|
||||
}
|
||||
|
||||
await execAsync(`docker logout ${response.registryUrl}`);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
|
||||
Reference in New Issue
Block a user