mirror of
https://github.com/LukeHagar/dokploy.git
synced 2025-12-06 04:19:37 +00:00
feat(database): enhance password validation for database schemas and update input components for password visibility
This commit is contained in:
@@ -83,7 +83,13 @@ const baseDatabaseSchema = z.object({
|
||||
message:
|
||||
"App name supports lowercase letters, numbers, '-' and can only start and end letters, and does not support continuous '-'",
|
||||
}),
|
||||
databasePassword: z.string(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.min(1, "Password is required")
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
dockerImage: z.string(),
|
||||
description: z.string().nullable(),
|
||||
serverId: z.string().nullable(),
|
||||
@@ -112,7 +118,12 @@ const mySchema = z.discriminatedUnion("type", [
|
||||
z
|
||||
.object({
|
||||
type: z.literal("mysql"),
|
||||
databaseRootPassword: z.string().default(""),
|
||||
databaseRootPassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseUser: z.string().default("mysql"),
|
||||
databaseName: z.string().default("mysql"),
|
||||
})
|
||||
@@ -121,7 +132,12 @@ const mySchema = z.discriminatedUnion("type", [
|
||||
.object({
|
||||
type: z.literal("mariadb"),
|
||||
dockerImage: z.string().default("mariadb:4"),
|
||||
databaseRootPassword: z.string().default(""),
|
||||
databaseRootPassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseUser: z.string().default("mariadb"),
|
||||
databaseName: z.string().default("mariadb"),
|
||||
})
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
import copy from "copy-to-clipboard";
|
||||
import { Clipboard, EyeIcon, EyeOffIcon } from "lucide-react";
|
||||
import { useRef, useState } from "react";
|
||||
import { Clipboard } from "lucide-react";
|
||||
import { useRef } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "../ui/button";
|
||||
import { Input, type InputProps } from "../ui/input";
|
||||
|
||||
export const ToggleVisibilityInput = ({ ...props }: InputProps) => {
|
||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const togglePasswordVisibility = () => {
|
||||
setIsPasswordVisible((prevVisibility) => !prevVisibility);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center space-x-2">
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type={isPasswordVisible ? "text" : "password"}
|
||||
{...props}
|
||||
/>
|
||||
<Input ref={inputRef} type={"password"} {...props} />
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
onClick={() => {
|
||||
@@ -29,13 +20,13 @@ export const ToggleVisibilityInput = ({ ...props }: InputProps) => {
|
||||
>
|
||||
<Clipboard className="size-4 text-muted-foreground" />
|
||||
</Button>
|
||||
<Button onClick={togglePasswordVisibility} variant={"secondary"}>
|
||||
{/* <Button onClick={togglePasswordVisibility} variant={"secondary"}>
|
||||
{isPasswordVisible ? (
|
||||
<EyeOffIcon className="size-4 text-muted-foreground" />
|
||||
) : (
|
||||
<EyeIcon className="size-4 text-muted-foreground" />
|
||||
)}
|
||||
</Button>
|
||||
</Button> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { EyeIcon, EyeOffIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -8,18 +9,39 @@ export interface InputProps
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, errorMessage, type, ...props }, ref) => {
|
||||
const [showPassword, setShowPassword] = React.useState(false);
|
||||
const isPassword = type === "password";
|
||||
const inputType = isPassword ? (showPassword ? "text" : "password") : type;
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
// bg-gray
|
||||
"flex h-10 w-full rounded-md bg-input px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
type={inputType}
|
||||
className={cn(
|
||||
// bg-gray
|
||||
"flex h-10 w-full rounded-md bg-input px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-50",
|
||||
isPassword && "pr-10", // Add padding for the eye icon
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
{isPassword && (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute inset-y-0 right-0 flex items-center pr-3 text-muted-foreground hover:text-foreground focus:outline-none"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOffIcon className="h-4 w-4" />
|
||||
) : (
|
||||
<EyeIcon className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
{errorMessage && (
|
||||
<span className="text-sm text-red-600 text-secondary-foreground">
|
||||
{errorMessage}
|
||||
|
||||
@@ -94,8 +94,20 @@ const createSchema = createInsertSchema(mariadb, {
|
||||
createdAt: z.string(),
|
||||
databaseName: z.string().min(1),
|
||||
databaseUser: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databaseRootPassword: z.string().optional(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.min(1, "Password is required")
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseRootPassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
})
|
||||
.optional(),
|
||||
dockerImage: z.string().default("mariadb:6"),
|
||||
command: z.string().optional(),
|
||||
env: z.string().optional(),
|
||||
|
||||
@@ -89,7 +89,13 @@ const createSchema = createInsertSchema(mongo, {
|
||||
createdAt: z.string(),
|
||||
mongoId: z.string(),
|
||||
name: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.min(1, "Password is required")
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseUser: z.string().min(1),
|
||||
dockerImage: z.string().default("mongo:15"),
|
||||
command: z.string().optional(),
|
||||
|
||||
@@ -92,8 +92,20 @@ const createSchema = createInsertSchema(mysql, {
|
||||
name: z.string().min(1),
|
||||
databaseName: z.string().min(1),
|
||||
databaseUser: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databaseRootPassword: z.string().optional(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.min(1, "Password is required")
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseRootPassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
})
|
||||
.optional(),
|
||||
dockerImage: z.string().default("mysql:8"),
|
||||
command: z.string().optional(),
|
||||
env: z.string().optional(),
|
||||
|
||||
@@ -88,7 +88,13 @@ export const postgresRelations = relations(postgres, ({ one, many }) => ({
|
||||
const createSchema = createInsertSchema(postgres, {
|
||||
postgresId: z.string(),
|
||||
name: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.min(1, "Password is required")
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseName: z.string().min(1),
|
||||
databaseUser: z.string().min(1),
|
||||
dockerImage: z.string().default("postgres:15"),
|
||||
|
||||
Reference in New Issue
Block a user