diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 489c7cb0e..c06ee191f 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -47,7 +47,7 @@ import { UpdateConfigSwarmSchema, } from "./shared"; import { sshKeys } from "./ssh-key"; -import { generateAppName } from "./utils"; +import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils"; export const sourceType = pgEnum("sourceType", [ "docker", "git", @@ -287,7 +287,12 @@ export const applicationsRelations = relations( ); const createSchema = createInsertSchema(applications, { - appName: z.string(), + appName: z + .string() + .min(1) + .max(63) + .regex(APP_NAME_REGEX, APP_NAME_MESSAGE) + .optional(), createdAt: z.string(), applicationId: z.string(), autoDeploy: z.boolean(), diff --git a/packages/server/src/db/schema/compose.ts b/packages/server/src/db/schema/compose.ts index 95264f52f..02bd60f0b 100644 --- a/packages/server/src/db/schema/compose.ts +++ b/packages/server/src/db/schema/compose.ts @@ -16,7 +16,7 @@ import { schedules } from "./schedule"; import { server } from "./server"; import { applicationStatus, triggerType } from "./shared"; import { sshKeys } from "./ssh-key"; -import { generateAppName } from "./utils"; +import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils"; export const sourceTypeCompose = pgEnum("sourceTypeCompose", [ "git", "github", @@ -147,6 +147,12 @@ export const composeRelations = relations(compose, ({ one, many }) => ({ const createSchema = createInsertSchema(compose, { name: z.string().min(1), + appName: z + .string() + .min(1) + .max(63) + .regex(APP_NAME_REGEX, APP_NAME_MESSAGE) + .optional(), description: z.string(), env: z.string().optional(), composeFile: z.string().optional(), diff --git a/packages/server/src/db/schema/mariadb.ts b/packages/server/src/db/schema/mariadb.ts index c143a5547..2ec9894fa 100644 --- a/packages/server/src/db/schema/mariadb.ts +++ b/packages/server/src/db/schema/mariadb.ts @@ -26,7 +26,7 @@ import { type UpdateConfigSwarm, UpdateConfigSwarmSchema, } from "./shared"; -import { generateAppName } from "./utils"; +import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils"; export const mariadb = pgTable("mariadb", { mariadbId: text("mariadbId") @@ -96,7 +96,12 @@ export const mariadbRelations = relations(mariadb, ({ one, many }) => ({ const createSchema = createInsertSchema(mariadb, { mariadbId: z.string(), name: z.string().min(1), - appName: z.string().min(1), + appName: z + .string() + .min(1) + .max(63) + .regex(APP_NAME_REGEX, APP_NAME_MESSAGE) + .optional(), createdAt: z.string(), databaseName: z.string().min(1), databaseUser: z.string().min(1), @@ -138,20 +143,18 @@ const createSchema = createInsertSchema(mariadb, { endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); -export const apiCreateMariaDB = createSchema - .pick({ - name: true, - appName: true, - dockerImage: true, - databaseRootPassword: true, - environmentId: true, - description: true, - databaseName: true, - databaseUser: true, - databasePassword: true, - serverId: true, - }) - .required(); +export const apiCreateMariaDB = createSchema.pick({ + name: true, + appName: true, + dockerImage: true, + databaseRootPassword: true, + environmentId: true, + description: true, + databaseName: true, + databaseUser: true, + databasePassword: true, + serverId: true, +}); export const apiFindOneMariaDB = createSchema .pick({ diff --git a/packages/server/src/db/schema/mongo.ts b/packages/server/src/db/schema/mongo.ts index e760a880a..30a9e7403 100644 --- a/packages/server/src/db/schema/mongo.ts +++ b/packages/server/src/db/schema/mongo.ts @@ -33,7 +33,7 @@ import { type UpdateConfigSwarm, UpdateConfigSwarmSchema, } from "./shared"; -import { generateAppName } from "./utils"; +import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils"; export const mongo = pgTable("mongo", { mongoId: text("mongoId") @@ -98,7 +98,12 @@ export const mongoRelations = relations(mongo, ({ one, many }) => ({ })); const createSchema = createInsertSchema(mongo, { - appName: z.string().min(1), + appName: z + .string() + .min(1) + .max(63) + .regex(APP_NAME_REGEX, APP_NAME_MESSAGE) + .optional(), createdAt: z.string(), mongoId: z.string(), name: z.string().min(1), @@ -135,19 +140,17 @@ const createSchema = createInsertSchema(mongo, { endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); -export const apiCreateMongo = createSchema - .pick({ - name: true, - appName: true, - dockerImage: true, - environmentId: true, - description: true, - databaseUser: true, - databasePassword: true, - serverId: true, - replicaSets: true, - }) - .required(); +export const apiCreateMongo = createSchema.pick({ + name: true, + appName: true, + dockerImage: true, + environmentId: true, + description: true, + databaseUser: true, + databasePassword: true, + serverId: true, + replicaSets: true, +}); export const apiFindOneMongo = createSchema .pick({ diff --git a/packages/server/src/db/schema/mysql.ts b/packages/server/src/db/schema/mysql.ts index 3e5c14b5e..86c8c7f0f 100644 --- a/packages/server/src/db/schema/mysql.ts +++ b/packages/server/src/db/schema/mysql.ts @@ -26,7 +26,7 @@ import { type UpdateConfigSwarm, UpdateConfigSwarmSchema, } from "./shared"; -import { generateAppName } from "./utils"; +import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils"; export const mysql = pgTable("mysql", { mysqlId: text("mysqlId") @@ -93,7 +93,12 @@ export const mysqlRelations = relations(mysql, ({ one, many }) => ({ const createSchema = createInsertSchema(mysql, { mysqlId: z.string(), - appName: z.string().min(1), + appName: z + .string() + .min(1) + .max(63) + .regex(APP_NAME_REGEX, APP_NAME_MESSAGE) + .optional(), createdAt: z.string(), name: z.string().min(1), databaseName: z.string().min(1), @@ -135,20 +140,18 @@ const createSchema = createInsertSchema(mysql, { endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); -export const apiCreateMySql = createSchema - .pick({ - name: true, - appName: true, - dockerImage: true, - environmentId: true, - description: true, - databaseName: true, - databaseUser: true, - databasePassword: true, - databaseRootPassword: true, - serverId: true, - }) - .required(); +export const apiCreateMySql = createSchema.pick({ + name: true, + appName: true, + dockerImage: true, + environmentId: true, + description: true, + databaseName: true, + databaseUser: true, + databasePassword: true, + databaseRootPassword: true, + serverId: true, +}); export const apiFindOneMySql = createSchema .pick({ diff --git a/packages/server/src/db/schema/postgres.ts b/packages/server/src/db/schema/postgres.ts index 9d4224006..9cd5fee29 100644 --- a/packages/server/src/db/schema/postgres.ts +++ b/packages/server/src/db/schema/postgres.ts @@ -26,7 +26,7 @@ import { type UpdateConfigSwarm, UpdateConfigSwarmSchema, } from "./shared"; -import { generateAppName } from "./utils"; +import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils"; export const postgres = pgTable("postgres", { postgresId: text("postgresId") @@ -94,6 +94,12 @@ export const postgresRelations = relations(postgres, ({ one, many }) => ({ const createSchema = createInsertSchema(postgres, { postgresId: z.string(), name: z.string().min(1), + appName: z + .string() + .min(1) + .max(63) + .regex(APP_NAME_REGEX, APP_NAME_MESSAGE) + .optional(), databasePassword: z .string() .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { @@ -128,19 +134,17 @@ const createSchema = createInsertSchema(postgres, { endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); -export const apiCreatePostgres = createSchema - .pick({ - name: true, - appName: true, - databaseName: true, - databaseUser: true, - databasePassword: true, - dockerImage: true, - environmentId: true, - description: true, - serverId: true, - }) - .required(); +export const apiCreatePostgres = createSchema.pick({ + name: true, + appName: true, + databaseName: true, + databaseUser: true, + databasePassword: true, + dockerImage: true, + environmentId: true, + description: true, + serverId: true, +}); export const apiFindOnePostgres = createSchema .pick({ diff --git a/packages/server/src/db/schema/redis.ts b/packages/server/src/db/schema/redis.ts index a7528745d..dba8d9826 100644 --- a/packages/server/src/db/schema/redis.ts +++ b/packages/server/src/db/schema/redis.ts @@ -25,7 +25,7 @@ import { type UpdateConfigSwarm, UpdateConfigSwarmSchema, } from "./shared"; -import { generateAppName } from "./utils"; +import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils"; export const redis = pgTable("redis", { redisId: text("redisId") @@ -88,7 +88,12 @@ export const redisRelations = relations(redis, ({ one, many }) => ({ const createSchema = createInsertSchema(redis, { redisId: z.string(), - appName: z.string().min(1), + appName: z + .string() + .min(1) + .max(63) + .regex(APP_NAME_REGEX, APP_NAME_MESSAGE) + .optional(), createdAt: z.string(), name: z.string().min(1), databasePassword: z.string(), @@ -117,17 +122,15 @@ const createSchema = createInsertSchema(redis, { endpointSpecSwarm: EndpointSpecSwarmSchema.nullable(), }); -export const apiCreateRedis = createSchema - .pick({ - name: true, - appName: true, - databasePassword: true, - dockerImage: true, - environmentId: true, - description: true, - serverId: true, - }) - .required(); +export const apiCreateRedis = createSchema.pick({ + name: true, + appName: true, + databasePassword: true, + dockerImage: true, + environmentId: true, + description: true, + serverId: true, +}); export const apiFindOneRedis = createSchema .pick({ diff --git a/packages/server/src/db/schema/utils.ts b/packages/server/src/db/schema/utils.ts index 2d272b8e9..811d3f767 100644 --- a/packages/server/src/db/schema/utils.ts +++ b/packages/server/src/db/schema/utils.ts @@ -6,6 +6,12 @@ const alphabet = "abcdefghijklmnopqrstuvwxyz123456789"; const customNanoid = customAlphabet(alphabet, 6); +/** App name: letters, numbers, dots, underscores, hyphens only (no spaces). Safe for shell/Docker. */ +export const APP_NAME_REGEX = /^[a-zA-Z0-9._-]+$/; + +export const APP_NAME_MESSAGE = + "App name can only contain letters, numbers, dots, underscores and hyphens"; + export const generateAppName = (type: string) => { const verb = faker.hacker.verb().replace(/ /g, "-"); const adjective = faker.hacker.adjective().replace(/ /g, "-");