diff --git a/packages/server/src/db/schema/utils.ts b/packages/server/src/db/schema/utils.ts index 59ebf4b7..43332c8a 100644 --- a/packages/server/src/db/schema/utils.ts +++ b/packages/server/src/db/schema/utils.ts @@ -1,3 +1,4 @@ +import { generatePassword } from "@dokploy/server/templates/utils"; import { faker } from "@faker-js/faker"; import { customAlphabet } from "nanoid"; @@ -13,3 +14,17 @@ export const generateAppName = (type: string) => { const nanoidPart = customNanoid(); return `${type}-${randomFakerElement}-${nanoidPart}`; }; + +export const cleanAppName = (appName?: string) => { + if (!appName) { + return appName?.toLowerCase(); + } + return appName.trim().replace(/ /g, "-").toLowerCase(); +}; + +export const buildAppName = (type: string, baseAppName?: string) => { + if (baseAppName) { + return `${cleanAppName(baseAppName)}-${generatePassword(6)}`; + } + return generateAppName(type); +}; diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index fef1457c..b8ecb88b 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -3,10 +3,10 @@ import { db } from "@dokploy/server/db"; import { type apiCreateApplication, applications, + buildAppName, + cleanAppName, } from "@dokploy/server/db/schema"; -import { generateAppName } from "@dokploy/server/db/schema"; import { getAdvancedStats } from "@dokploy/server/monitoring/utilts"; -import { generatePassword } from "@dokploy/server/templates/utils"; import { buildApplication, getBuildCommand, @@ -46,34 +46,31 @@ import { createDeploymentPreview, updateDeploymentStatus, } from "./deployment"; -import { validUniqueServerAppName } from "./project"; -import { - findPreviewDeploymentById, - updatePreviewDeployment, -} from "./preview-deployment"; +import { type Domain, getDomainHost } from "./domain"; import { createPreviewDeploymentComment, getIssueComment, issueCommentExists, updateIssueComment, } from "./github"; -import { type Domain, getDomainHost } from "./domain"; +import { + findPreviewDeploymentById, + updatePreviewDeployment, +} from "./preview-deployment"; +import { validUniqueServerAppName } from "./project"; export type Application = typeof applications.$inferSelect; export const createApplication = async ( input: typeof apiCreateApplication._type, ) => { - input.appName = - `${input.appName}-${generatePassword(6)}` || generateAppName("app"); - if (input.appName) { - const valid = await validUniqueServerAppName(input.appName); + const appName = buildAppName("app", input.appName); - if (!valid) { - throw new TRPCError({ - code: "CONFLICT", - message: "Application with this 'AppName' already exists", - }); - } + const valid = await validUniqueServerAppName(appName); + if (!valid) { + throw new TRPCError({ + code: "CONFLICT", + message: "Application with this 'AppName' already exists", + }); } return await db.transaction(async (tx) => { @@ -81,6 +78,7 @@ export const createApplication = async ( .insert(applications) .values({ ...input, + appName, }) .returning() .then((value) => value[0]); @@ -140,10 +138,11 @@ export const updateApplication = async ( applicationId: string, applicationData: Partial, ) => { + const { appName, ...rest } = applicationData; const application = await db .update(applications) .set({ - ...applicationData, + ...rest, }) .where(eq(applications.applicationId, applicationId)) .returning(); diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 5ae0d774..5e5c6897 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -2,7 +2,7 @@ import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import { db } from "@dokploy/server/db"; import { type apiCreateCompose, compose } from "@dokploy/server/db/schema"; -import { generateAppName } from "@dokploy/server/db/schema"; +import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildCompose, @@ -52,17 +52,14 @@ import { validUniqueServerAppName } from "./project"; export type Compose = typeof compose.$inferSelect; export const createCompose = async (input: typeof apiCreateCompose._type) => { - input.appName = - `${input.appName}-${generatePassword(6)}` || generateAppName("compose"); - if (input.appName) { - const valid = await validUniqueServerAppName(input.appName); + const appName = buildAppName("compose", input.appName); - if (!valid) { - throw new TRPCError({ - code: "CONFLICT", - message: "Service with this 'AppName' already exists", - }); - } + const valid = await validUniqueServerAppName(appName); + if (!valid) { + throw new TRPCError({ + code: "CONFLICT", + message: "Service with this 'AppName' already exists", + }); } const newDestination = await db @@ -70,6 +67,7 @@ export const createCompose = async (input: typeof apiCreateCompose._type) => { .values({ ...input, composeFile: "", + appName, }) .returning() .then((value) => value[0]); @@ -87,8 +85,9 @@ 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); + const appName = cleanAppName(input.appName); + if (appName) { + const valid = await validUniqueServerAppName(appName); if (!valid) { throw new TRPCError({ @@ -101,6 +100,7 @@ export const createComposeByTemplate = async ( .insert(compose) .values({ ...input, + appName, }) .returning() .then((value) => value[0]); @@ -184,10 +184,11 @@ export const updateCompose = async ( composeId: string, composeData: Partial, ) => { + const { appName, ...rest } = composeData; const composeResult = await db .update(compose) .set({ - ...composeData, + ...rest, }) .where(eq(compose.composeId, composeId)) .returning(); diff --git a/packages/server/src/services/deployment.ts b/packages/server/src/services/deployment.ts index b18b132d..41adf238 100644 --- a/packages/server/src/services/deployment.ts +++ b/packages/server/src/services/deployment.ts @@ -23,8 +23,8 @@ import { type Server, findServerById } from "./server"; import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; import { - findPreviewDeploymentById, type PreviewDeployment, + findPreviewDeploymentById, updatePreviewDeployment, } from "./preview-deployment"; diff --git a/packages/server/src/services/mariadb.ts b/packages/server/src/services/mariadb.ts index 645b5c65..39ef5910 100644 --- a/packages/server/src/services/mariadb.ts +++ b/packages/server/src/services/mariadb.ts @@ -4,7 +4,7 @@ import { backups, mariadb, } from "@dokploy/server/db/schema"; -import { generateAppName } from "@dokploy/server/db/schema"; +import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildMariadb } from "@dokploy/server/utils/databases/mariadb"; import { pullImage } from "@dokploy/server/utils/docker/utils"; @@ -17,17 +17,14 @@ import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; export type Mariadb = typeof mariadb.$inferSelect; export const createMariadb = async (input: typeof apiCreateMariaDB._type) => { - input.appName = - `${input.appName}-${generatePassword(6)}` || generateAppName("mariadb"); - if (input.appName) { - const valid = await validUniqueServerAppName(input.appName); + const appName = buildAppName("mariadb", input.appName); - if (!valid) { - throw new TRPCError({ - code: "CONFLICT", - message: "Service with this 'AppName' already exists", - }); - } + const valid = await validUniqueServerAppName(input.appName); + if (!valid) { + throw new TRPCError({ + code: "CONFLICT", + message: "Service with this 'AppName' already exists", + }); } const newMariadb = await db @@ -40,6 +37,7 @@ export const createMariadb = async (input: typeof apiCreateMariaDB._type) => { databaseRootPassword: input.databaseRootPassword ? input.databaseRootPassword : generatePassword(), + appName, }) .returning() .then((value) => value[0]); @@ -82,10 +80,11 @@ export const updateMariadbById = async ( mariadbId: string, mariadbData: Partial, ) => { + const { appName, ...rest } = mariadbData; const result = await db .update(mariadb) .set({ - ...mariadbData, + ...rest, }) .where(eq(mariadb.mariadbId, mariadbId)) .returning(); diff --git a/packages/server/src/services/mongo.ts b/packages/server/src/services/mongo.ts index b87ec4da..f8d5e4d6 100644 --- a/packages/server/src/services/mongo.ts +++ b/packages/server/src/services/mongo.ts @@ -1,6 +1,6 @@ import { db } from "@dokploy/server/db"; import { type apiCreateMongo, backups, mongo } from "@dokploy/server/db/schema"; -import { generateAppName } from "@dokploy/server/db/schema"; +import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildMongo } from "@dokploy/server/utils/databases/mongo"; import { pullImage } from "@dokploy/server/utils/docker/utils"; @@ -13,17 +13,14 @@ import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; export type Mongo = typeof mongo.$inferSelect; export const createMongo = async (input: typeof apiCreateMongo._type) => { - input.appName = - `${input.appName}-${generatePassword(6)}` || generateAppName("mongo"); - if (input.appName) { - const valid = await validUniqueServerAppName(input.appName); + const appName = buildAppName("mongo", input.appName); - if (!valid) { - throw new TRPCError({ - code: "CONFLICT", - message: "Service with this 'AppName' already exists", - }); - } + const valid = await validUniqueServerAppName(appName); + if (!valid) { + throw new TRPCError({ + code: "CONFLICT", + message: "Service with this 'AppName' already exists", + }); } const newMongo = await db @@ -33,6 +30,7 @@ export const createMongo = async (input: typeof apiCreateMongo._type) => { databasePassword: input.databasePassword ? input.databasePassword : generatePassword(), + appName, }) .returning() .then((value) => value[0]); @@ -74,10 +72,11 @@ export const updateMongoById = async ( mongoId: string, mongoData: Partial, ) => { + const { appName, ...rest } = mongoData; const result = await db .update(mongo) .set({ - ...mongoData, + ...rest, }) .where(eq(mongo.mongoId, mongoId)) .returning(); diff --git a/packages/server/src/services/mysql.ts b/packages/server/src/services/mysql.ts index ee9df820..e2c8bb6f 100644 --- a/packages/server/src/services/mysql.ts +++ b/packages/server/src/services/mysql.ts @@ -1,6 +1,6 @@ import { db } from "@dokploy/server/db"; import { type apiCreateMySql, backups, mysql } from "@dokploy/server/db/schema"; -import { generateAppName } from "@dokploy/server/db/schema"; +import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildMysql } from "@dokploy/server/utils/databases/mysql"; import { pullImage } from "@dokploy/server/utils/docker/utils"; @@ -13,18 +13,14 @@ import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; export type MySql = typeof mysql.$inferSelect; export const createMysql = async (input: typeof apiCreateMySql._type) => { - input.appName = - `${input.appName}-${generatePassword(6)}` || generateAppName("mysql"); + const appName = buildAppName("mysql", input.appName); - if (input.appName) { - const valid = await validUniqueServerAppName(input.appName); - - if (!valid) { - throw new TRPCError({ - code: "CONFLICT", - message: "Service with this 'AppName' already exists", - }); - } + const valid = await validUniqueServerAppName(appName); + if (!valid) { + throw new TRPCError({ + code: "CONFLICT", + message: "Service with this 'AppName' already exists", + }); } const newMysql = await db @@ -37,6 +33,7 @@ export const createMysql = async (input: typeof apiCreateMySql._type) => { databaseRootPassword: input.databaseRootPassword ? input.databaseRootPassword : generatePassword(), + appName, }) .returning() .then((value) => value[0]); @@ -79,10 +76,11 @@ export const updateMySqlById = async ( mysqlId: string, mysqlData: Partial, ) => { + const { appName, ...rest } = mysqlData; const result = await db .update(mysql) .set({ - ...mysqlData, + ...rest, }) .where(eq(mysql.mysqlId, mysqlId)) .returning(); diff --git a/packages/server/src/services/postgres.ts b/packages/server/src/services/postgres.ts index c94ddbbe..87286e67 100644 --- a/packages/server/src/services/postgres.ts +++ b/packages/server/src/services/postgres.ts @@ -4,7 +4,7 @@ import { backups, postgres, } from "@dokploy/server/db/schema"; -import { generateAppName } from "@dokploy/server/db/schema"; +import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildPostgres } from "@dokploy/server/utils/databases/postgres"; import { pullImage } from "@dokploy/server/utils/docker/utils"; @@ -17,17 +17,14 @@ import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; export type Postgres = typeof postgres.$inferSelect; export const createPostgres = async (input: typeof apiCreatePostgres._type) => { - input.appName = - `${input.appName}-${generatePassword(6)}` || generateAppName("postgres"); - if (input.appName) { - const valid = await validUniqueServerAppName(input.appName); + const appName = buildAppName("postgres", input.appName); - if (!valid) { - throw new TRPCError({ - code: "CONFLICT", - message: "Service with this 'AppName' already exists", - }); - } + const valid = await validUniqueServerAppName(appName); + if (!valid) { + throw new TRPCError({ + code: "CONFLICT", + message: "Service with this 'AppName' already exists", + }); } const newPostgres = await db @@ -37,6 +34,7 @@ export const createPostgres = async (input: typeof apiCreatePostgres._type) => { databasePassword: input.databasePassword ? input.databasePassword : generatePassword(), + appName, }) .returning() .then((value) => value[0]); @@ -96,10 +94,11 @@ export const updatePostgresById = async ( postgresId: string, postgresData: Partial, ) => { + const { appName, ...rest } = postgresData; const result = await db .update(postgres) .set({ - ...postgresData, + ...rest, }) .where(eq(postgres.postgresId, postgresId)) .returning(); diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index e52f4553..06bf8fb5 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -7,20 +7,20 @@ import { import { TRPCError } from "@trpc/server"; import { and, desc, eq } from "drizzle-orm"; import { slugify } from "../setup/server-setup"; -import { findApplicationById } from "./application"; -import { createDomain } from "./domain"; import { generatePassword, generateRandomDomain } from "../templates/utils"; +import { removeService } from "../utils/docker/utils"; +import { removeDirectoryCode } from "../utils/filesystem/directory"; +import { authGithub } from "../utils/providers/github"; +import { removeTraefikConfig } from "../utils/traefik/application"; import { manageDomain } from "../utils/traefik/domain"; +import { findAdminById } from "./admin"; +import { findApplicationById } from "./application"; import { removeDeployments, removeDeploymentsByPreviewDeploymentId, } from "./deployment"; -import { removeDirectoryCode } from "../utils/filesystem/directory"; -import { removeTraefikConfig } from "../utils/traefik/application"; -import { removeService } from "../utils/docker/utils"; -import { authGithub } from "../utils/providers/github"; -import { getIssueComment, type Github } from "./github"; -import { findAdminById } from "./admin"; +import { createDomain } from "./domain"; +import { type Github, getIssueComment } from "./github"; export type PreviewDeployment = typeof previewDeployments.$inferSelect; diff --git a/packages/server/src/services/redis.ts b/packages/server/src/services/redis.ts index 7809de28..5b958081 100644 --- a/packages/server/src/services/redis.ts +++ b/packages/server/src/services/redis.ts @@ -1,6 +1,6 @@ import { db } from "@dokploy/server/db"; import { type apiCreateRedis, redis } from "@dokploy/server/db/schema"; -import { generateAppName } from "@dokploy/server/db/schema"; +import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildRedis } from "@dokploy/server/utils/databases/redis"; import { pullImage } from "@dokploy/server/utils/docker/utils"; @@ -14,17 +14,14 @@ 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) => { - input.appName = - `${input.appName}-${generatePassword(6)}` || generateAppName("redis"); - if (input.appName) { - const valid = await validUniqueServerAppName(input.appName); + const appName = buildAppName("redis", input.appName); - if (!valid) { - throw new TRPCError({ - code: "CONFLICT", - message: "Service with this 'AppName' already exists", - }); - } + const valid = await validUniqueServerAppName(appName); + if (!valid) { + throw new TRPCError({ + code: "CONFLICT", + message: "Service with this 'AppName' already exists", + }); } const newRedis = await db @@ -34,6 +31,7 @@ export const createRedis = async (input: typeof apiCreateRedis._type) => { databasePassword: input.databasePassword ? input.databasePassword : generatePassword(), + appName, }) .returning() .then((value) => value[0]); @@ -70,10 +68,11 @@ export const updateRedisById = async ( redisId: string, redisData: Partial, ) => { + const { appName, ...rest } = redisData; const result = await db .update(redis) .set({ - ...redisData, + ...rest, }) .where(eq(redis.redisId, redisId)) .returning();