From 8e5b0988cfb08b535e8463cc482e37a65559bba6 Mon Sep 17 00:00:00 2001 From: Aaron Gonzales Date: Mon, 16 Dec 2024 18:13:07 +0800 Subject: [PATCH 1/2] fix: fixed/improved handling of app names in api --- packages/server/src/db/schema/utils.ts | 15 ++++++++ packages/server/src/services/application.ts | 35 +++++++++---------- packages/server/src/services/compose.ts | 27 +++++++------- packages/server/src/services/deployment.ts | 2 +- packages/server/src/services/mariadb.ts | 21 ++++++----- packages/server/src/services/mongo.ts | 21 ++++++----- packages/server/src/services/mysql.ts | 22 ++++++------ packages/server/src/services/postgres.ts | 21 ++++++----- .../server/src/services/preview-deployment.ts | 16 ++++----- packages/server/src/services/redis.ts | 21 ++++++----- 10 files changed, 105 insertions(+), 96 deletions(-) diff --git a/packages/server/src/db/schema/utils.ts b/packages/server/src/db/schema/utils.ts index 59ebf4b7..e8651769 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; + } + return appName.trim().replace(/ /g, "-"); +}; + +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..d2ce3432 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]); @@ -144,6 +142,7 @@ export const updateApplication = async ( .update(applications) .set({ ...applicationData, + appName: cleanAppName(applicationData.appName), }) .where(eq(applications.applicationId, applicationId)) .returning(); diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 5ae0d774..27e7bfa4 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]); @@ -188,6 +188,7 @@ export const updateCompose = async ( .update(compose) .set({ ...composeData, + appName: cleanAppName(composeData.appName), }) .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..7906cc5b 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]); @@ -86,6 +84,7 @@ export const updateMariadbById = async ( .update(mariadb) .set({ ...mariadbData, + appName: cleanAppName(mariadbData.appName), }) .where(eq(mariadb.mariadbId, mariadbId)) .returning(); diff --git a/packages/server/src/services/mongo.ts b/packages/server/src/services/mongo.ts index b87ec4da..82a8a84f 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]); @@ -78,6 +76,7 @@ export const updateMongoById = async ( .update(mongo) .set({ ...mongoData, + appName: cleanAppName(mongoData.appName), }) .where(eq(mongo.mongoId, mongoId)) .returning(); diff --git a/packages/server/src/services/mysql.ts b/packages/server/src/services/mysql.ts index ee9df820..1bb2c478 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]); @@ -83,6 +80,7 @@ export const updateMySqlById = async ( .update(mysql) .set({ ...mysqlData, + appName: cleanAppName(mysqlData.appName), }) .where(eq(mysql.mysqlId, mysqlId)) .returning(); diff --git a/packages/server/src/services/postgres.ts b/packages/server/src/services/postgres.ts index c94ddbbe..d2cd4874 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]); @@ -100,6 +98,7 @@ export const updatePostgresById = async ( .update(postgres) .set({ ...postgresData, + appName: cleanAppName(postgresData.appName), }) .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..96b3066c 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]); @@ -74,6 +72,7 @@ export const updateRedisById = async ( .update(redis) .set({ ...redisData, + appName: cleanAppName(redisData.appName), }) .where(eq(redis.redisId, redisId)) .returning(); From 0abf62dd5212b2bc996c9d8ddfd4d3a7d8ccced7 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 21 Dec 2024 13:10:34 -0600 Subject: [PATCH 2/2] refactor: prevent update appName in services --- packages/server/src/db/schema/utils.ts | 4 ++-- packages/server/src/services/application.ts | 4 ++-- packages/server/src/services/compose.ts | 4 ++-- packages/server/src/services/mariadb.ts | 4 ++-- packages/server/src/services/mongo.ts | 4 ++-- packages/server/src/services/mysql.ts | 4 ++-- packages/server/src/services/postgres.ts | 4 ++-- packages/server/src/services/redis.ts | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/server/src/db/schema/utils.ts b/packages/server/src/db/schema/utils.ts index e8651769..43332c8a 100644 --- a/packages/server/src/db/schema/utils.ts +++ b/packages/server/src/db/schema/utils.ts @@ -17,9 +17,9 @@ export const generateAppName = (type: string) => { export const cleanAppName = (appName?: string) => { if (!appName) { - return appName; + return appName?.toLowerCase(); } - return appName.trim().replace(/ /g, "-"); + return appName.trim().replace(/ /g, "-").toLowerCase(); }; export const buildAppName = (type: string, baseAppName?: string) => { diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index d2ce3432..b8ecb88b 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -138,11 +138,11 @@ export const updateApplication = async ( applicationId: string, applicationData: Partial, ) => { + const { appName, ...rest } = applicationData; const application = await db .update(applications) .set({ - ...applicationData, - appName: cleanAppName(applicationData.appName), + ...rest, }) .where(eq(applications.applicationId, applicationId)) .returning(); diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 27e7bfa4..5e5c6897 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -184,11 +184,11 @@ export const updateCompose = async ( composeId: string, composeData: Partial, ) => { + const { appName, ...rest } = composeData; const composeResult = await db .update(compose) .set({ - ...composeData, - appName: cleanAppName(composeData.appName), + ...rest, }) .where(eq(compose.composeId, composeId)) .returning(); diff --git a/packages/server/src/services/mariadb.ts b/packages/server/src/services/mariadb.ts index 7906cc5b..39ef5910 100644 --- a/packages/server/src/services/mariadb.ts +++ b/packages/server/src/services/mariadb.ts @@ -80,11 +80,11 @@ export const updateMariadbById = async ( mariadbId: string, mariadbData: Partial, ) => { + const { appName, ...rest } = mariadbData; const result = await db .update(mariadb) .set({ - ...mariadbData, - appName: cleanAppName(mariadbData.appName), + ...rest, }) .where(eq(mariadb.mariadbId, mariadbId)) .returning(); diff --git a/packages/server/src/services/mongo.ts b/packages/server/src/services/mongo.ts index 82a8a84f..f8d5e4d6 100644 --- a/packages/server/src/services/mongo.ts +++ b/packages/server/src/services/mongo.ts @@ -72,11 +72,11 @@ export const updateMongoById = async ( mongoId: string, mongoData: Partial, ) => { + const { appName, ...rest } = mongoData; const result = await db .update(mongo) .set({ - ...mongoData, - appName: cleanAppName(mongoData.appName), + ...rest, }) .where(eq(mongo.mongoId, mongoId)) .returning(); diff --git a/packages/server/src/services/mysql.ts b/packages/server/src/services/mysql.ts index 1bb2c478..e2c8bb6f 100644 --- a/packages/server/src/services/mysql.ts +++ b/packages/server/src/services/mysql.ts @@ -76,11 +76,11 @@ export const updateMySqlById = async ( mysqlId: string, mysqlData: Partial, ) => { + const { appName, ...rest } = mysqlData; const result = await db .update(mysql) .set({ - ...mysqlData, - appName: cleanAppName(mysqlData.appName), + ...rest, }) .where(eq(mysql.mysqlId, mysqlId)) .returning(); diff --git a/packages/server/src/services/postgres.ts b/packages/server/src/services/postgres.ts index d2cd4874..87286e67 100644 --- a/packages/server/src/services/postgres.ts +++ b/packages/server/src/services/postgres.ts @@ -94,11 +94,11 @@ export const updatePostgresById = async ( postgresId: string, postgresData: Partial, ) => { + const { appName, ...rest } = postgresData; const result = await db .update(postgres) .set({ - ...postgresData, - appName: cleanAppName(postgresData.appName), + ...rest, }) .where(eq(postgres.postgresId, postgresId)) .returning(); diff --git a/packages/server/src/services/redis.ts b/packages/server/src/services/redis.ts index 96b3066c..5b958081 100644 --- a/packages/server/src/services/redis.ts +++ b/packages/server/src/services/redis.ts @@ -68,11 +68,11 @@ export const updateRedisById = async ( redisId: string, redisData: Partial, ) => { + const { appName, ...rest } = redisData; const result = await db .update(redis) .set({ - ...redisData, - appName: cleanAppName(redisData.appName), + ...rest, }) .where(eq(redis.redisId, redisId)) .returning();