diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 68db88f80..02b2f4c55 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -44,6 +44,13 @@ export const registryRelations = relations(registry, ({ many }) => ({ }), })); +// Image references require a lowercase namespace (e.g. Docker Hub username). +const registryUsernameSchema = z + .string() + .trim() + .min(1) + .transform((s) => s.toLowerCase()); + // Registry URLs must be hostname[:port] only — no shell metacharacters // Empty string is allowed (means default/Docker Hub registry) const registryUrlSchema = z @@ -57,7 +64,7 @@ const registryUrlSchema = z const createSchema = createInsertSchema(registry, { registryName: z.string().min(1), - username: z.string().min(1), + username: registryUsernameSchema, password: z.string().min(1), registryUrl: registryUrlSchema, organizationId: z.string().min(1), @@ -70,7 +77,7 @@ export const apiCreateRegistry = createSchema .pick({}) .extend({ registryName: z.string().min(1), - username: z.string().min(1), + username: registryUsernameSchema, password: z.string().min(1), registryUrl: registryUrlSchema, registryType: z.enum(["cloud"]), @@ -83,7 +90,7 @@ export const apiCreateRegistry = createSchema export const apiTestRegistry = createSchema.pick({}).extend({ registryName: z.string().optional(), - username: z.string().min(1), + username: registryUsernameSchema, password: z.string().min(1), registryUrl: registryUrlSchema, registryType: z.enum(["cloud"]), diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts index aa014a05c..6bf02547c 100644 --- a/packages/server/src/utils/cluster/upload.ts +++ b/packages/server/src/utils/cluster/upload.ts @@ -101,8 +101,8 @@ export const getRegistryTag = (registry: Registry, imageName: string) => { // Extract the repository name (last part after '/') const repositoryName = extractRepositoryName(imageName); - // Build the final tag using registry's username/prefix - const targetPrefix = imagePrefix || username; + // Build the final tag using registry's username/prefix (must be lowercase for valid image refs) + const targetPrefix = (imagePrefix || username).toLowerCase(); const finalRegistry = registryUrl || ""; return finalRegistry