mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Compare commits
37 Commits
v0.21.5
...
feat/caddy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a8508e623 | ||
|
|
e14f2780af | ||
|
|
33ab87f3db | ||
|
|
571d73a5b6 | ||
|
|
a630909612 | ||
|
|
8eaa006f0f | ||
|
|
8e8bc3e71e | ||
|
|
f4cd617107 | ||
|
|
48cfe66a6b | ||
|
|
bdc10cacef | ||
|
|
8fbad8a26e | ||
|
|
0f36bcb04e | ||
|
|
f4054453b4 | ||
|
|
dbd36fc024 | ||
|
|
850d06a32c | ||
|
|
dfd3dc180d | ||
|
|
3d42bfc81b | ||
|
|
d2eaa4b40b | ||
|
|
7d7f2b4b1f | ||
|
|
8e97c63faa | ||
|
|
74ec8f4594 | ||
|
|
76c0bff13a | ||
|
|
9b5cd0f5fe | ||
|
|
efee798880 | ||
|
|
1c470b8ba7 | ||
|
|
692864ced1 | ||
|
|
9ca61476d2 | ||
|
|
773a610be1 | ||
|
|
37f9e073f0 | ||
|
|
d335a9515d | ||
|
|
7a5a3de43d | ||
|
|
1279fac137 | ||
|
|
0e1f0b42ee | ||
|
|
05f43ad06b | ||
|
|
350bed217c | ||
|
|
7ac7481343 | ||
|
|
d9c34c4524 |
@@ -52,7 +52,7 @@ feat: add new feature
|
|||||||
|
|
||||||
Before you start, please make the clone based on the `canary` branch, since the `main` branch is the source of truth and should always reflect the latest stable release, also the PRs will be merged to the `canary` branch.
|
Before you start, please make the clone based on the `canary` branch, since the `main` branch is the source of truth and should always reflect the latest stable release, also the PRs will be merged to the `canary` branch.
|
||||||
|
|
||||||
We use Node v20.9.0
|
We use Node v20.9.0 and recommend this specific version. If you have nvm installed, you can run `nvm install 20.9.0 && nvm use` in the root directory.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/dokploy/dokploy.git
|
git clone https://github.com/dokploy/dokploy.git
|
||||||
@@ -87,6 +87,8 @@ pnpm run dokploy:dev
|
|||||||
|
|
||||||
Go to http://localhost:3000 to see the development server
|
Go to http://localhost:3000 to see the development server
|
||||||
|
|
||||||
|
Note: this project uses Biome. If your editor is configured to use another formatter such as Prettier, it's recommended to either change it to use Biome or turn it off.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -298,7 +298,11 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
refetch();
|
refetch();
|
||||||
toast.success("Preview deployments enabled");
|
toast.success(
|
||||||
|
checked
|
||||||
|
? "Preview deployments enabled"
|
||||||
|
: "Preview deployments disabled",
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export const RestoreBackup = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||||
|
|
||||||
const { data: destinations = [] } = api.destination.all.useQuery();
|
const { data: destinations = [] } = api.destination.all.useQuery();
|
||||||
|
|
||||||
@@ -99,13 +100,18 @@ export const RestoreBackup = ({
|
|||||||
const destionationId = form.watch("destinationId");
|
const destionationId = form.watch("destinationId");
|
||||||
|
|
||||||
const debouncedSetSearch = debounce((value: string) => {
|
const debouncedSetSearch = debounce((value: string) => {
|
||||||
|
setDebouncedSearchTerm(value);
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
const handleSearchChange = (value: string) => {
|
||||||
setSearch(value);
|
setSearch(value);
|
||||||
}, 300);
|
debouncedSetSearch(value);
|
||||||
|
};
|
||||||
|
|
||||||
const { data: files = [], isLoading } = api.backup.listBackupFiles.useQuery(
|
const { data: files = [], isLoading } = api.backup.listBackupFiles.useQuery(
|
||||||
{
|
{
|
||||||
destinationId: destionationId,
|
destinationId: destionationId,
|
||||||
search,
|
search: debouncedSearchTerm,
|
||||||
serverId: serverId ?? "",
|
serverId: serverId ?? "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -284,7 +290,8 @@ export const RestoreBackup = ({
|
|||||||
<Command>
|
<Command>
|
||||||
<CommandInput
|
<CommandInput
|
||||||
placeholder="Search backup files..."
|
placeholder="Search backup files..."
|
||||||
onValueChange={debouncedSetSearch}
|
value={search}
|
||||||
|
onValueChange={handleSearchChange}
|
||||||
className="h-9"
|
className="h-9"
|
||||||
/>
|
/>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@@ -308,6 +315,8 @@ export const RestoreBackup = ({
|
|||||||
key={file}
|
key={file}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
form.setValue("backupFile", file);
|
form.setValue("backupFile", file);
|
||||||
|
setSearch(file);
|
||||||
|
setDebouncedSearchTerm(file);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex w-full justify-between">
|
<div className="flex w-full justify-between">
|
||||||
|
|||||||
@@ -248,7 +248,9 @@ export const AddGitlabProvider = () => {
|
|||||||
name="groupName"
|
name="groupName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Group Name (Optional)</FormLabel>
|
<FormLabel>
|
||||||
|
Group Name (Optional, Comma-Separated List)
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="For organization/group access use the slugish name of the group eg: my-org"
|
placeholder="For organization/group access use the slugish name of the group eg: my-org"
|
||||||
|
|||||||
@@ -156,7 +156,9 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
|
|||||||
name="groupName"
|
name="groupName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Group Name (Optional)</FormLabel>
|
<FormLabel>
|
||||||
|
Group Name (Optional, Comma-Separated List)
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="For organization/group access use the slugish name of the group eg: my-org"
|
placeholder="For organization/group access use the slugish name of the group eg: my-org"
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const PasswordSchema = z.object({
|
|||||||
password: z.string().min(8, {
|
password: z.string().min(8, {
|
||||||
message: "Password is required",
|
message: "Password is required",
|
||||||
}),
|
}),
|
||||||
|
issuer: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const PinSchema = z.object({
|
const PinSchema = z.object({
|
||||||
@@ -60,12 +61,86 @@ export const Enable2FA = () => {
|
|||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
const [step, setStep] = useState<"password" | "verify">("password");
|
const [step, setStep] = useState<"password" | "verify">("password");
|
||||||
const [isPasswordLoading, setIsPasswordLoading] = useState(false);
|
const [isPasswordLoading, setIsPasswordLoading] = useState(false);
|
||||||
|
const [otpValue, setOtpValue] = useState("");
|
||||||
|
|
||||||
|
const handleVerifySubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
const result = await authClient.twoFactor.verifyTotp({
|
||||||
|
code: otpValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
if (result.error.code === "INVALID_TWO_FACTOR_AUTHENTICATION") {
|
||||||
|
toast.error("Invalid verification code");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.data) {
|
||||||
|
throw new Error("No response received from server");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success("2FA configured successfully");
|
||||||
|
utils.user.get.invalidate();
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
const errorMessage =
|
||||||
|
error.message === "Failed to fetch"
|
||||||
|
? "Connection error. Please check your internet connection."
|
||||||
|
: error.message;
|
||||||
|
|
||||||
|
toast.error(errorMessage);
|
||||||
|
} else {
|
||||||
|
toast.error("Error verifying 2FA code", {
|
||||||
|
description: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const passwordForm = useForm<PasswordForm>({
|
||||||
|
resolver: zodResolver(PasswordSchema),
|
||||||
|
defaultValues: {
|
||||||
|
password: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pinForm = useForm<PinForm>({
|
||||||
|
resolver: zodResolver(PinSchema),
|
||||||
|
defaultValues: {
|
||||||
|
pin: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isDialogOpen) {
|
||||||
|
setStep("password");
|
||||||
|
setData(null);
|
||||||
|
setBackupCodes([]);
|
||||||
|
setOtpValue("");
|
||||||
|
passwordForm.reset({
|
||||||
|
password: "",
|
||||||
|
issuer: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [isDialogOpen, passwordForm]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (step === "verify") {
|
||||||
|
setOtpValue("");
|
||||||
|
}
|
||||||
|
}, [step]);
|
||||||
|
|
||||||
const handlePasswordSubmit = async (formData: PasswordForm) => {
|
const handlePasswordSubmit = async (formData: PasswordForm) => {
|
||||||
setIsPasswordLoading(true);
|
setIsPasswordLoading(true);
|
||||||
try {
|
try {
|
||||||
const { data: enableData, error } = await authClient.twoFactor.enable({
|
const { data: enableData, error } = await authClient.twoFactor.enable({
|
||||||
password: formData.password,
|
password: formData.password,
|
||||||
|
issuer: formData.issuer,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!enableData) {
|
if (!enableData) {
|
||||||
@@ -103,75 +178,6 @@ export const Enable2FA = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleVerifySubmit = async (formData: PinForm) => {
|
|
||||||
try {
|
|
||||||
const result = await authClient.twoFactor.verifyTotp({
|
|
||||||
code: formData.pin,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
if (result.error.code === "INVALID_TWO_FACTOR_AUTHENTICATION") {
|
|
||||||
pinForm.setError("pin", {
|
|
||||||
message: "Invalid code. Please try again.",
|
|
||||||
});
|
|
||||||
toast.error("Invalid verification code");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw result.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.data) {
|
|
||||||
throw new Error("No response received from server");
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success("2FA configured successfully");
|
|
||||||
utils.user.get.invalidate();
|
|
||||||
setIsDialogOpen(false);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
const errorMessage =
|
|
||||||
error.message === "Failed to fetch"
|
|
||||||
? "Connection error. Please check your internet connection."
|
|
||||||
: error.message;
|
|
||||||
|
|
||||||
pinForm.setError("pin", {
|
|
||||||
message: errorMessage,
|
|
||||||
});
|
|
||||||
toast.error(errorMessage);
|
|
||||||
} else {
|
|
||||||
pinForm.setError("pin", {
|
|
||||||
message: "Error verifying code",
|
|
||||||
});
|
|
||||||
toast.error("Error verifying 2FA code");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const passwordForm = useForm<PasswordForm>({
|
|
||||||
resolver: zodResolver(PasswordSchema),
|
|
||||||
defaultValues: {
|
|
||||||
password: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pinForm = useForm<PinForm>({
|
|
||||||
resolver: zodResolver(PinSchema),
|
|
||||||
defaultValues: {
|
|
||||||
pin: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isDialogOpen) {
|
|
||||||
setStep("password");
|
|
||||||
setData(null);
|
|
||||||
setBackupCodes([]);
|
|
||||||
passwordForm.reset();
|
|
||||||
pinForm.reset();
|
|
||||||
}
|
|
||||||
}, [isDialogOpen, passwordForm, pinForm]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
@@ -217,6 +223,27 @@ export const Enable2FA = () => {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<FormField
|
||||||
|
control={passwordForm.control}
|
||||||
|
name="issuer"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Issuer</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter your issuer"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
Use a custom issuer to identify the service you're
|
||||||
|
authenticating with.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@@ -228,11 +255,7 @@ export const Enable2FA = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
) : (
|
) : (
|
||||||
<Form {...pinForm}>
|
<Form {...pinForm}>
|
||||||
<form
|
<form onSubmit={handleVerifySubmit} className="space-y-6">
|
||||||
id="pin-form"
|
|
||||||
onSubmit={pinForm.handleSubmit(handleVerifySubmit)}
|
|
||||||
className="space-y-6"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col gap-6 justify-center items-center">
|
<div className="flex flex-col gap-6 justify-center items-center">
|
||||||
{data?.qrCodeUrl ? (
|
{data?.qrCodeUrl ? (
|
||||||
<>
|
<>
|
||||||
@@ -284,36 +307,33 @@ export const Enable2FA = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormField
|
<div className="flex flex-col justify-center items-center">
|
||||||
control={pinForm.control}
|
<FormLabel>Verification Code</FormLabel>
|
||||||
name="pin"
|
<InputOTP
|
||||||
render={({ field }) => (
|
maxLength={6}
|
||||||
<FormItem className="flex flex-col justify-center items-center">
|
value={otpValue}
|
||||||
<FormLabel>Verification Code</FormLabel>
|
onChange={setOtpValue}
|
||||||
<FormControl>
|
autoComplete="off"
|
||||||
<InputOTP maxLength={6} {...field}>
|
>
|
||||||
<InputOTPGroup>
|
<InputOTPGroup>
|
||||||
<InputOTPSlot index={0} />
|
<InputOTPSlot index={0} />
|
||||||
<InputOTPSlot index={1} />
|
<InputOTPSlot index={1} />
|
||||||
<InputOTPSlot index={2} />
|
<InputOTPSlot index={2} />
|
||||||
<InputOTPSlot index={3} />
|
<InputOTPSlot index={3} />
|
||||||
<InputOTPSlot index={4} />
|
<InputOTPSlot index={4} />
|
||||||
<InputOTPSlot index={5} />
|
<InputOTPSlot index={5} />
|
||||||
</InputOTPGroup>
|
</InputOTPGroup>
|
||||||
</InputOTP>
|
</InputOTP>
|
||||||
</FormControl>
|
<FormDescription>
|
||||||
<FormDescription>
|
Enter the 6-digit code from your authenticator app
|
||||||
Enter the 6-digit code from your authenticator app
|
</FormDescription>
|
||||||
</FormDescription>
|
</div>
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
isLoading={isPasswordLoading}
|
isLoading={isPasswordLoading}
|
||||||
|
disabled={otpValue.length !== 6}
|
||||||
>
|
>
|
||||||
Enable 2FA
|
Enable 2FA
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const randomImages = [
|
|||||||
export const ProfileForm = () => {
|
export const ProfileForm = () => {
|
||||||
const _utils = api.useUtils();
|
const _utils = api.useUtils();
|
||||||
const { data, refetch, isLoading } = api.user.get.useQuery();
|
const { data, refetch, isLoading } = api.user.get.useQuery();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync,
|
mutateAsync,
|
||||||
isLoading: isUpdating,
|
isLoading: isUpdating,
|
||||||
@@ -84,12 +85,17 @@ export const ProfileForm = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
form.reset({
|
form.reset(
|
||||||
email: data?.user?.email || "",
|
{
|
||||||
password: "",
|
email: data?.user?.email || "",
|
||||||
image: data?.user?.image || "",
|
password: form.getValues("password") || "",
|
||||||
currentPassword: "",
|
image: data?.user?.image || "",
|
||||||
});
|
currentPassword: form.getValues("currentPassword") || "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keepValues: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (data.user.email) {
|
if (data.user.email) {
|
||||||
generateSHA256Hash(data.user.email).then((hash) => {
|
generateSHA256Hash(data.user.email).then((hash) => {
|
||||||
@@ -97,8 +103,7 @@ export const ProfileForm = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
form.reset();
|
}, [form, data]);
|
||||||
}, [form, form.reset, data]);
|
|
||||||
|
|
||||||
const onSubmit = async (values: Profile) => {
|
const onSubmit = async (values: Profile) => {
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
@@ -110,7 +115,12 @@ export const ProfileForm = () => {
|
|||||||
.then(async () => {
|
.then(async () => {
|
||||||
await refetch();
|
await refetch();
|
||||||
toast.success("Profile Updated");
|
toast.success("Profile Updated");
|
||||||
form.reset();
|
form.reset({
|
||||||
|
email: values.email,
|
||||||
|
password: "",
|
||||||
|
image: values.image,
|
||||||
|
currentPassword: "",
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error updating the profile");
|
toast.error("Error updating the profile");
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Sorted list based off of population of the country / speakers of the language.
|
||||||
|
*/
|
||||||
export const Languages = {
|
export const Languages = {
|
||||||
english: { code: "en", name: "English" },
|
english: { code: "en", name: "English" },
|
||||||
|
spanish: { code: "es", name: "Español" },
|
||||||
|
chineseSimplified: { code: "zh-Hans", name: "简体中文" },
|
||||||
|
chineseTraditional: { code: "zh-Hant", name: "繁體中文" },
|
||||||
|
portuguese: { code: "pt-br", name: "Português" },
|
||||||
|
russian: { code: "ru", name: "Русский" },
|
||||||
|
japanese: { code: "ja", name: "日本語" },
|
||||||
|
german: { code: "de", name: "Deutsch" },
|
||||||
|
korean: { code: "ko", name: "한국어" },
|
||||||
|
french: { code: "fr", name: "Français" },
|
||||||
|
turkish: { code: "tr", name: "Türkçe" },
|
||||||
|
italian: { code: "it", name: "Italiano" },
|
||||||
polish: { code: "pl", name: "Polski" },
|
polish: { code: "pl", name: "Polski" },
|
||||||
ukrainian: { code: "uk", name: "Українська" },
|
ukrainian: { code: "uk", name: "Українська" },
|
||||||
russian: { code: "ru", name: "Русский" },
|
|
||||||
french: { code: "fr", name: "Français" },
|
|
||||||
german: { code: "de", name: "Deutsch" },
|
|
||||||
chineseTraditional: { code: "zh-Hant", name: "繁體中文" },
|
|
||||||
chineseSimplified: { code: "zh-Hans", name: "简体中文" },
|
|
||||||
turkish: { code: "tr", name: "Türkçe" },
|
|
||||||
kazakh: { code: "kz", name: "Қазақ" },
|
|
||||||
persian: { code: "fa", name: "فارسی" },
|
persian: { code: "fa", name: "فارسی" },
|
||||||
korean: { code: "ko", name: "한국어" },
|
dutch: { code: "nl", name: "Nederlands" },
|
||||||
portuguese: { code: "pt-br", name: "Português" },
|
indonesian: { code: "id", name: "Bahasa Indonesia" },
|
||||||
italian: { code: "it", name: "Italiano" },
|
kazakh: { code: "kz", name: "Қазақ" },
|
||||||
japanese: { code: "ja", name: "日本語" },
|
|
||||||
spanish: { code: "es", name: "Español" },
|
|
||||||
norwegian: { code: "no", name: "Norsk" },
|
norwegian: { code: "no", name: "Norsk" },
|
||||||
azerbaijani: { code: "az", name: "Azərbaycan" },
|
azerbaijani: { code: "az", name: "Azərbaycan" },
|
||||||
indonesian: { code: "id", name: "Bahasa Indonesia" },
|
|
||||||
malayalam: { code: "ml", name: "മലയാളം" },
|
malayalam: { code: "ml", name: "മലയാളം" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dokploy",
|
"name": "dokploy",
|
||||||
"version": "v0.21.5",
|
"version": "v0.21.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
"adm-zip": "^0.5.14",
|
"adm-zip": "^0.5.14",
|
||||||
"ai": "^4.0.23",
|
"ai": "^4.0.23",
|
||||||
"bcrypt": "5.1.1",
|
"bcrypt": "5.1.1",
|
||||||
"better-auth": "1.2.4",
|
"better-auth": "1.2.6",
|
||||||
"bl": "6.0.11",
|
"bl": "6.0.11",
|
||||||
"boxen": "^7.1.1",
|
"boxen": "^7.1.1",
|
||||||
"bullmq": "5.4.2",
|
"bullmq": "5.4.2",
|
||||||
|
|||||||
1
apps/dokploy/public/locales/nl/common.json
Normal file
1
apps/dokploy/public/locales/nl/common.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
58
apps/dokploy/public/locales/nl/settings.json
Normal file
58
apps/dokploy/public/locales/nl/settings.json
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"settings.common.save": "Opslaan",
|
||||||
|
"settings.common.enterTerminal": "Terminal",
|
||||||
|
"settings.server.domain.title": "Server Domein",
|
||||||
|
"settings.server.domain.description": "Voeg een domein toe aan jouw server applicatie.",
|
||||||
|
"settings.server.domain.form.domain": "Domein",
|
||||||
|
"settings.server.domain.form.letsEncryptEmail": "Let's Encrypt Email",
|
||||||
|
"settings.server.domain.form.certificate.label": "Certificaat Aanbieder",
|
||||||
|
"settings.server.domain.form.certificate.placeholder": "Select een certificaat",
|
||||||
|
"settings.server.domain.form.certificateOptions.none": "Geen",
|
||||||
|
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt",
|
||||||
|
|
||||||
|
"settings.server.webServer.title": "Web Server",
|
||||||
|
"settings.server.webServer.description": "Herlaad of maak de web server schoon.",
|
||||||
|
"settings.server.webServer.actions": "Acties",
|
||||||
|
"settings.server.webServer.reload": "Herladen",
|
||||||
|
"settings.server.webServer.watchLogs": "Bekijk Logs",
|
||||||
|
"settings.server.webServer.updateServerIp": "Update de Server IP",
|
||||||
|
"settings.server.webServer.server.label": "Server",
|
||||||
|
"settings.server.webServer.traefik.label": "Traefik",
|
||||||
|
"settings.server.webServer.traefik.modifyEnv": "Bewerk Omgeving",
|
||||||
|
"settings.server.webServer.traefik.managePorts": "Extra Poort Mappings",
|
||||||
|
"settings.server.webServer.traefik.managePortsDescription": "Bewerk extra Poorten voor Traefik",
|
||||||
|
"settings.server.webServer.traefik.targetPort": "Doel Poort",
|
||||||
|
"settings.server.webServer.traefik.publishedPort": "Gepubliceerde Poort",
|
||||||
|
"settings.server.webServer.traefik.addPort": "Voeg Poort toe",
|
||||||
|
"settings.server.webServer.traefik.portsUpdated": "Poorten succesvol aangepast",
|
||||||
|
"settings.server.webServer.traefik.portsUpdateError": "Poorten niet succesvol aangepast",
|
||||||
|
"settings.server.webServer.traefik.publishMode": "Publiceer Mode",
|
||||||
|
"settings.server.webServer.storage.label": "Opslag",
|
||||||
|
"settings.server.webServer.storage.cleanUnusedImages": "Maak ongebruikte images schoon",
|
||||||
|
"settings.server.webServer.storage.cleanUnusedVolumes": "Maak ongebruikte volumes schoon",
|
||||||
|
"settings.server.webServer.storage.cleanStoppedContainers": "Maak gestopte containers schoon",
|
||||||
|
"settings.server.webServer.storage.cleanDockerBuilder": "Maak Docker Builder & Systeem schoon",
|
||||||
|
"settings.server.webServer.storage.cleanMonitoring": "Maak monitoor schoon",
|
||||||
|
"settings.server.webServer.storage.cleanAll": "Maak alles schoon",
|
||||||
|
|
||||||
|
"settings.profile.title": "Account",
|
||||||
|
"settings.profile.description": "Veramder details van account.",
|
||||||
|
"settings.profile.email": "Email",
|
||||||
|
"settings.profile.password": "Wachtwoord",
|
||||||
|
"settings.profile.avatar": "Profiel Icoon",
|
||||||
|
|
||||||
|
"settings.appearance.title": "Uiterlijk",
|
||||||
|
"settings.appearance.description": "Verander het thema van je dashboard.",
|
||||||
|
"settings.appearance.theme": "Thema",
|
||||||
|
"settings.appearance.themeDescription": "Selecteer een thema voor je dashboard.",
|
||||||
|
"settings.appearance.themes.light": "Licht",
|
||||||
|
"settings.appearance.themes.dark": "Donker",
|
||||||
|
"settings.appearance.themes.system": "Systeem",
|
||||||
|
"settings.appearance.language": "Taal",
|
||||||
|
"settings.appearance.languageDescription": "Selecteer een taal voor je dashboard.",
|
||||||
|
|
||||||
|
"settings.terminal.connectionSettings": "Verbindings instellingen",
|
||||||
|
"settings.terminal.ipAddress": "IP Address",
|
||||||
|
"settings.terminal.port": "Poort",
|
||||||
|
"settings.terminal.username": "Gebruikersnaam"
|
||||||
|
}
|
||||||
@@ -1 +1,78 @@
|
|||||||
{}
|
{
|
||||||
|
"dashboard.title": "仪表盘",
|
||||||
|
"dashboard.overview": "概览",
|
||||||
|
"dashboard.projects": "项目",
|
||||||
|
"dashboard.servers": "服务器",
|
||||||
|
"dashboard.docker": "Docker",
|
||||||
|
"dashboard.monitoring": "监控",
|
||||||
|
"dashboard.settings": "设置",
|
||||||
|
"dashboard.logout": "退出登录",
|
||||||
|
"dashboard.profile": "个人资料",
|
||||||
|
"dashboard.terminal": "终端",
|
||||||
|
"dashboard.containers": "容器",
|
||||||
|
"dashboard.images": "镜像",
|
||||||
|
"dashboard.volumes": "卷",
|
||||||
|
"dashboard.networks": "网络",
|
||||||
|
"button.create": "创建",
|
||||||
|
"button.edit": "编辑",
|
||||||
|
"button.delete": "删除",
|
||||||
|
"button.cancel": "取消",
|
||||||
|
"button.save": "保存",
|
||||||
|
"button.confirm": "确认",
|
||||||
|
"button.back": "返回",
|
||||||
|
"button.next": "下一步",
|
||||||
|
"button.finish": "完成",
|
||||||
|
"status.running": "运行中",
|
||||||
|
"status.stopped": "已停止",
|
||||||
|
"status.error": "错误",
|
||||||
|
"status.pending": "等待中",
|
||||||
|
"status.success": "成功",
|
||||||
|
"status.failed": "失败",
|
||||||
|
"form.required": "必填",
|
||||||
|
"form.invalid": "无效",
|
||||||
|
"form.submit": "提交",
|
||||||
|
"form.reset": "重置",
|
||||||
|
"notification.success": "操作成功",
|
||||||
|
"notification.error": "操作失败",
|
||||||
|
"notification.warning": "警告",
|
||||||
|
"notification.info": "信息",
|
||||||
|
"time.now": "刚刚",
|
||||||
|
"time.minutes": "分钟前",
|
||||||
|
"time.hours": "小时前",
|
||||||
|
"time.days": "天前",
|
||||||
|
"filter.all": "全部",
|
||||||
|
"filter.active": "活跃",
|
||||||
|
"filter.inactive": "不活跃",
|
||||||
|
"sort.asc": "升序",
|
||||||
|
"sort.desc": "降序",
|
||||||
|
"search.placeholder": "搜索...",
|
||||||
|
"search.noResults": "无结果",
|
||||||
|
"pagination.prev": "上一页",
|
||||||
|
"pagination.next": "下一页",
|
||||||
|
"pagination.of": "共 {0} 页",
|
||||||
|
"error.notFound": "未找到",
|
||||||
|
"error.serverError": "服务器错误",
|
||||||
|
"error.unauthorized": "未授权",
|
||||||
|
"error.forbidden": "禁止访问",
|
||||||
|
"loading": "加载中...",
|
||||||
|
"empty": "暂无数据",
|
||||||
|
"more": "更多",
|
||||||
|
"less": "收起",
|
||||||
|
"project.create": "创建项目",
|
||||||
|
"project.edit": "编辑项目",
|
||||||
|
"project.delete": "删除项目",
|
||||||
|
"project.name": "项目名称",
|
||||||
|
"project.description": "项目描述",
|
||||||
|
"service.create": "创建服务",
|
||||||
|
"service.edit": "编辑服务",
|
||||||
|
"service.delete": "删除服务",
|
||||||
|
"service.name": "服务名称",
|
||||||
|
"service.type": "服务类型",
|
||||||
|
"domain.add": "添加域名",
|
||||||
|
"domain.remove": "移除域名",
|
||||||
|
"environment.variables": "环境变量",
|
||||||
|
"environment.add": "添加环境变量",
|
||||||
|
"environment.edit": "编辑环境变量",
|
||||||
|
"environment.name": "变量名",
|
||||||
|
"environment.value": "变量值"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
{
|
{
|
||||||
"settings.common.save": "保存",
|
"settings.common.save": "保存",
|
||||||
"settings.common.enterTerminal": "进入终端",
|
"settings.common.enterTerminal": "终端",
|
||||||
"settings.server.domain.title": "域名设置",
|
"settings.server.domain.title": "服务器域名",
|
||||||
"settings.server.domain.description": "添加域名到服务器",
|
"settings.server.domain.description": "为您的服务器应用添加域名。",
|
||||||
"settings.server.domain.form.domain": "域名",
|
"settings.server.domain.form.domain": "域名",
|
||||||
"settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 邮箱",
|
"settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 邮箱",
|
||||||
"settings.server.domain.form.certificate.label": "证书",
|
"settings.server.domain.form.certificate.label": "证书提供商",
|
||||||
"settings.server.domain.form.certificate.placeholder": "选择一个证书",
|
"settings.server.domain.form.certificate.placeholder": "选择证书",
|
||||||
"settings.server.domain.form.certificateOptions.none": "无",
|
"settings.server.domain.form.certificateOptions.none": "无",
|
||||||
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt",
|
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt",
|
||||||
|
"settings.server.webServer.title": "Web 服务器",
|
||||||
"settings.server.webServer.title": "服务器设置",
|
"settings.server.webServer.description": "重载或清理 Web 服务器。",
|
||||||
"settings.server.webServer.description": "管理服务器",
|
|
||||||
"settings.server.webServer.actions": "操作",
|
"settings.server.webServer.actions": "操作",
|
||||||
"settings.server.webServer.reload": "重新加载",
|
"settings.server.webServer.reload": "重新加载",
|
||||||
"settings.server.webServer.watchLogs": "查看日志",
|
"settings.server.webServer.watchLogs": "查看日志",
|
||||||
@@ -19,40 +18,50 @@
|
|||||||
"settings.server.webServer.server.label": "服务器",
|
"settings.server.webServer.server.label": "服务器",
|
||||||
"settings.server.webServer.traefik.label": "Traefik",
|
"settings.server.webServer.traefik.label": "Traefik",
|
||||||
"settings.server.webServer.traefik.modifyEnv": "修改环境变量",
|
"settings.server.webServer.traefik.modifyEnv": "修改环境变量",
|
||||||
"settings.server.webServer.traefik.managePorts": "端口转发",
|
"settings.server.webServer.traefik.managePorts": "额外端口映射",
|
||||||
"settings.server.webServer.traefik.managePortsDescription": "添加或删除 Traefik 的其他端口",
|
"settings.server.webServer.traefik.managePortsDescription": "为 Traefik 添加或删除额外端口",
|
||||||
"settings.server.webServer.traefik.targetPort": "目标端口",
|
"settings.server.webServer.traefik.targetPort": "目标端口",
|
||||||
"settings.server.webServer.traefik.publishedPort": "对外端口",
|
"settings.server.webServer.traefik.publishedPort": "发布端口",
|
||||||
"settings.server.webServer.traefik.addPort": "添加端口",
|
"settings.server.webServer.traefik.addPort": "添加端口",
|
||||||
"settings.server.webServer.traefik.portsUpdated": "端口更新成功",
|
"settings.server.webServer.traefik.portsUpdated": "端口更新成功",
|
||||||
"settings.server.webServer.traefik.portsUpdateError": "端口更新失败",
|
"settings.server.webServer.traefik.portsUpdateError": "端口更新失败",
|
||||||
"settings.server.webServer.traefik.publishMode": "端口映射",
|
"settings.server.webServer.traefik.publishMode": "发布模式",
|
||||||
"settings.server.webServer.storage.label": "存储空间",
|
"settings.server.webServer.storage.label": "存储空间",
|
||||||
"settings.server.webServer.storage.cleanUnusedImages": "清理未使用的镜像",
|
"settings.server.webServer.storage.cleanUnusedImages": "清理未使用的镜像",
|
||||||
"settings.server.webServer.storage.cleanUnusedVolumes": "清理未使用的卷",
|
"settings.server.webServer.storage.cleanUnusedVolumes": "清理未使用的卷",
|
||||||
"settings.server.webServer.storage.cleanStoppedContainers": "清理已停止的容器",
|
"settings.server.webServer.storage.cleanStoppedContainers": "清理已停止的容器",
|
||||||
"settings.server.webServer.storage.cleanDockerBuilder": "清理 Docker Builder 与 系统缓存",
|
"settings.server.webServer.storage.cleanDockerBuilder": "清理 Docker Builder 和系统",
|
||||||
"settings.server.webServer.storage.cleanMonitoring": "清理监控数据",
|
"settings.server.webServer.storage.cleanMonitoring": "清理监控数据",
|
||||||
"settings.server.webServer.storage.cleanAll": "清理所有内容",
|
"settings.server.webServer.storage.cleanAll": "清理所有内容",
|
||||||
|
|
||||||
"settings.profile.title": "账户",
|
"settings.profile.title": "账户",
|
||||||
"settings.profile.description": "更改您的个人资料",
|
"settings.profile.description": "在此更改您的个人资料详情。",
|
||||||
"settings.profile.email": "邮箱",
|
"settings.profile.email": "邮箱",
|
||||||
"settings.profile.password": "密码",
|
"settings.profile.password": "密码",
|
||||||
"settings.profile.avatar": "头像",
|
"settings.profile.avatar": "头像",
|
||||||
|
|
||||||
"settings.appearance.title": "外观",
|
"settings.appearance.title": "外观",
|
||||||
"settings.appearance.description": "自定义面板主题",
|
"settings.appearance.description": "自定义您的仪表盘主题。",
|
||||||
"settings.appearance.theme": "主题",
|
"settings.appearance.theme": "主题",
|
||||||
"settings.appearance.themeDescription": "选择面板主题",
|
"settings.appearance.themeDescription": "为您的仪表盘选择主题",
|
||||||
"settings.appearance.themes.light": "明亮",
|
"settings.appearance.themes.light": "明亮",
|
||||||
"settings.appearance.themes.dark": "黑暗",
|
"settings.appearance.themes.dark": "暗黑",
|
||||||
"settings.appearance.themes.system": "系统主题",
|
"settings.appearance.themes.system": "跟随系统",
|
||||||
"settings.appearance.language": "语言",
|
"settings.appearance.language": "语言",
|
||||||
"settings.appearance.languageDescription": "选择面板语言",
|
"settings.appearance.languageDescription": "为您的仪表盘选择语言",
|
||||||
|
"settings.terminal.connectionSettings": "连接设置",
|
||||||
"settings.terminal.connectionSettings": "终端设置",
|
"settings.terminal.ipAddress": "IP 地址",
|
||||||
"settings.terminal.ipAddress": "IP",
|
|
||||||
"settings.terminal.port": "端口",
|
"settings.terminal.port": "端口",
|
||||||
"settings.terminal.username": "用户名"
|
"settings.terminal.username": "用户名",
|
||||||
|
"settings.settings": "设置",
|
||||||
|
"settings.general": "通用设置",
|
||||||
|
"settings.security": "安全",
|
||||||
|
"settings.users": "用户管理",
|
||||||
|
"settings.roles": "角色管理",
|
||||||
|
"settings.permissions": "权限",
|
||||||
|
"settings.api": "API设置",
|
||||||
|
"settings.certificates": "证书管理",
|
||||||
|
"settings.ssh": "SSH密钥",
|
||||||
|
"settings.backups": "备份",
|
||||||
|
"settings.logs": "日志",
|
||||||
|
"settings.updates": "更新",
|
||||||
|
"settings.network": "网络"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,11 +36,11 @@
|
|||||||
"@ai-sdk/mistral": "^1.0.6",
|
"@ai-sdk/mistral": "^1.0.6",
|
||||||
"@ai-sdk/openai": "^1.0.12",
|
"@ai-sdk/openai": "^1.0.12",
|
||||||
"@ai-sdk/openai-compatible": "^0.0.13",
|
"@ai-sdk/openai-compatible": "^0.0.13",
|
||||||
"@better-auth/utils": "0.2.3",
|
"@better-auth/utils": "0.2.4",
|
||||||
"@oslojs/encoding": "1.1.0",
|
"@oslojs/encoding": "1.1.0",
|
||||||
"@oslojs/crypto": "1.0.1",
|
"@oslojs/crypto": "1.0.1",
|
||||||
"drizzle-dbml-generator": "0.10.0",
|
"drizzle-dbml-generator": "0.10.0",
|
||||||
"better-auth": "1.2.4",
|
"better-auth": "1.2.6",
|
||||||
"@faker-js/faker": "^8.4.1",
|
"@faker-js/faker": "^8.4.1",
|
||||||
"@octokit/auth-app": "^6.0.4",
|
"@octokit/auth-app": "^6.0.4",
|
||||||
"@react-email/components": "^0.0.21",
|
"@react-email/components": "^0.0.21",
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ export const paths = (isServer = false) => {
|
|||||||
const MAIN_TRAEFIK_PATH = `${BASE_PATH}/traefik`;
|
const MAIN_TRAEFIK_PATH = `${BASE_PATH}/traefik`;
|
||||||
const DYNAMIC_TRAEFIK_PATH = `${MAIN_TRAEFIK_PATH}/dynamic`;
|
const DYNAMIC_TRAEFIK_PATH = `${MAIN_TRAEFIK_PATH}/dynamic`;
|
||||||
|
|
||||||
|
const CADDY_PATH = `${BASE_PATH}/caddy`;
|
||||||
|
const CADDY_DYNAMIC_PATH = `${CADDY_PATH}/dynamic`;
|
||||||
return {
|
return {
|
||||||
BASE_PATH,
|
BASE_PATH,
|
||||||
MAIN_TRAEFIK_PATH,
|
MAIN_TRAEFIK_PATH,
|
||||||
DYNAMIC_TRAEFIK_PATH,
|
DYNAMIC_TRAEFIK_PATH,
|
||||||
|
CADDY_PATH,
|
||||||
|
CADDY_DYNAMIC_PATH,
|
||||||
LOGS_PATH: `${BASE_PATH}/logs`,
|
LOGS_PATH: `${BASE_PATH}/logs`,
|
||||||
APPLICATIONS_PATH: `${BASE_PATH}/applications`,
|
APPLICATIONS_PATH: `${BASE_PATH}/applications`,
|
||||||
COMPOSE_PATH: `${BASE_PATH}/compose`,
|
COMPOSE_PATH: `${BASE_PATH}/compose`,
|
||||||
|
|||||||
350
packages/server/src/setup/caddy-setup.ts
Normal file
350
packages/server/src/setup/caddy-setup.ts
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import type { ContainerCreateOptions } from "dockerode";
|
||||||
|
import { dump } from "js-yaml";
|
||||||
|
import { paths } from "../constants";
|
||||||
|
import { getRemoteDocker } from "../utils/servers/remote-docker";
|
||||||
|
import type { FileConfig } from "../utils/traefik/file-types";
|
||||||
|
import type { MainTraefikConfig } from "../utils/traefik/types";
|
||||||
|
|
||||||
|
export const TRAEFIK_SSL_PORT =
|
||||||
|
Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443;
|
||||||
|
export const TRAEFIK_PORT =
|
||||||
|
Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80;
|
||||||
|
export const TRAEFIK_HTTP3_PORT =
|
||||||
|
Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443;
|
||||||
|
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.1.2";
|
||||||
|
|
||||||
|
interface TraefikOptions {
|
||||||
|
enableDashboard?: boolean;
|
||||||
|
env?: string[];
|
||||||
|
serverId?: string;
|
||||||
|
additionalPorts?: {
|
||||||
|
targetPort: number;
|
||||||
|
publishedPort: number;
|
||||||
|
}[];
|
||||||
|
force?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initializeCaddy = async ({
|
||||||
|
enableDashboard = false,
|
||||||
|
env,
|
||||||
|
serverId,
|
||||||
|
additionalPorts = [],
|
||||||
|
force = false,
|
||||||
|
}: TraefikOptions = {}) => {
|
||||||
|
const { CADDY_PATH, CADDY_DYNAMIC_PATH } = paths(!!serverId);
|
||||||
|
const imageName = "lucaslorentz/caddy-docker-proxy:ci-alpine";
|
||||||
|
const containerName = "dokploy-caddy";
|
||||||
|
|
||||||
|
const exposedPorts: Record<string, {}> = {
|
||||||
|
[`${TRAEFIK_PORT}/tcp`]: {},
|
||||||
|
[`${TRAEFIK_SSL_PORT}/tcp`]: {},
|
||||||
|
[`${TRAEFIK_HTTP3_PORT}/udp`]: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const portBindings: Record<string, Array<{ HostPort: string }>> = {
|
||||||
|
[`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }],
|
||||||
|
[`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }],
|
||||||
|
[`${TRAEFIK_HTTP3_PORT}/udp`]: [
|
||||||
|
{ HostPort: TRAEFIK_HTTP3_PORT.toString() },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (enableDashboard) {
|
||||||
|
exposedPorts["8080/tcp"] = {};
|
||||||
|
portBindings["8080/tcp"] = [{ HostPort: "8080" }];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const port of additionalPorts) {
|
||||||
|
const portKey = `${port.targetPort}/tcp`;
|
||||||
|
exposedPorts[portKey] = {};
|
||||||
|
portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }];
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings: ContainerCreateOptions = {
|
||||||
|
name: containerName,
|
||||||
|
Image: imageName,
|
||||||
|
NetworkingConfig: {
|
||||||
|
EndpointsConfig: {
|
||||||
|
"dokploy-network": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExposedPorts: exposedPorts,
|
||||||
|
HostConfig: {
|
||||||
|
RestartPolicy: {
|
||||||
|
Name: "always",
|
||||||
|
},
|
||||||
|
Binds: [
|
||||||
|
`${CADDY_PATH}/Caddyfile:/etc/caddy/Caddyfile`,
|
||||||
|
`${CADDY_DYNAMIC_PATH}:/etc/caddy/dynamic`,
|
||||||
|
"/var/run/docker.sock:/var/run/docker.sock",
|
||||||
|
],
|
||||||
|
PortBindings: portBindings,
|
||||||
|
},
|
||||||
|
Env: env,
|
||||||
|
};
|
||||||
|
|
||||||
|
const docker = await getRemoteDocker(serverId);
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
const service = docker.getService("dokploy-caddy");
|
||||||
|
await service?.remove({ force: true });
|
||||||
|
|
||||||
|
let attempts = 0;
|
||||||
|
const maxAttempts = 5;
|
||||||
|
while (attempts < maxAttempts) {
|
||||||
|
try {
|
||||||
|
await docker.listServices({
|
||||||
|
filters: { name: ["dokploy-caddy"] },
|
||||||
|
});
|
||||||
|
console.log("Waiting for service cleanup...");
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
attempts++;
|
||||||
|
} catch (_e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_err) {
|
||||||
|
console.log("No existing service to remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then try to remove any existing container
|
||||||
|
const container = docker.getContainer(containerName);
|
||||||
|
try {
|
||||||
|
const inspect = await container.inspect();
|
||||||
|
if (inspect.State.Status === "running" && !force) {
|
||||||
|
console.log("Caddy already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await container.remove({ force: true });
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
} catch (_err) {
|
||||||
|
console.log("No existing container to remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and start the new container
|
||||||
|
try {
|
||||||
|
await docker.createContainer(settings);
|
||||||
|
const newContainer = docker.getContainer(containerName);
|
||||||
|
await newContainer.start();
|
||||||
|
console.log("Caddy container started successfully");
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error?.json?.message?.includes("port is already allocated")) {
|
||||||
|
console.log("Ports still in use, waiting longer for cleanup...");
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||||
|
// Try one more time
|
||||||
|
await docker.createContainer(settings);
|
||||||
|
const newContainer = docker.getContainer(containerName);
|
||||||
|
await newContainer.start();
|
||||||
|
console.log("Caddy container started successfully after retry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to initialize Caddy:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDefaultServerCaddyConfig = () => {
|
||||||
|
const { CADDY_DYNAMIC_PATH } = paths();
|
||||||
|
const configFilePath = path.join(CADDY_DYNAMIC_PATH, "Caddyfile");
|
||||||
|
|
||||||
|
if (existsSync(configFilePath)) {
|
||||||
|
console.log("Default caddy config already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appName = "dokploy";
|
||||||
|
const serviceURLDefault = `http://${appName}:${process.env.PORT || 3000}`;
|
||||||
|
const config: FileConfig = {
|
||||||
|
http: {
|
||||||
|
routers: {
|
||||||
|
[`${appName}-router-app`]: {
|
||||||
|
rule: `Host(\`${appName}.docker.localhost\`) && PathPrefix(\`/\`)`,
|
||||||
|
service: `${appName}-service-app`,
|
||||||
|
entryPoints: ["web"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
services: {
|
||||||
|
[`${appName}-service-app`]: {
|
||||||
|
loadBalancer: {
|
||||||
|
servers: [{ url: serviceURLDefault }],
|
||||||
|
passHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const yamlStr = dump(config);
|
||||||
|
mkdirSync(CADDY_DYNAMIC_PATH, { recursive: true });
|
||||||
|
writeFileSync(path.join(CADDY_DYNAMIC_PATH, `Caddyfile`), yamlStr, "utf8");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDefaultCaddyConfig = () => {
|
||||||
|
const configObject: MainTraefikConfig = {
|
||||||
|
providers: {
|
||||||
|
...(process.env.NODE_ENV === "development"
|
||||||
|
? {
|
||||||
|
docker: {
|
||||||
|
defaultRule:
|
||||||
|
"Host(`{{ trimPrefix `/` .Name }}.docker.localhost`)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
swarm: {
|
||||||
|
exposedByDefault: false,
|
||||||
|
watch: true,
|
||||||
|
},
|
||||||
|
docker: {
|
||||||
|
exposedByDefault: false,
|
||||||
|
watch: true,
|
||||||
|
network: "dokploy-network",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
file: {
|
||||||
|
directory: "/etc/dokploy/traefik/dynamic",
|
||||||
|
watch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
entryPoints: {
|
||||||
|
web: {
|
||||||
|
address: `:${TRAEFIK_PORT}`,
|
||||||
|
},
|
||||||
|
websecure: {
|
||||||
|
address: `:${TRAEFIK_SSL_PORT}`,
|
||||||
|
http3: {
|
||||||
|
advertisedPort: TRAEFIK_HTTP3_PORT,
|
||||||
|
},
|
||||||
|
...(process.env.NODE_ENV === "production" && {
|
||||||
|
http: {
|
||||||
|
tls: {
|
||||||
|
certResolver: "letsencrypt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
insecure: true,
|
||||||
|
},
|
||||||
|
...(process.env.NODE_ENV === "production" && {
|
||||||
|
certificatesResolvers: {
|
||||||
|
letsencrypt: {
|
||||||
|
acme: {
|
||||||
|
email: "test@localhost.com",
|
||||||
|
storage: "/etc/dokploy/traefik/dynamic/acme.json",
|
||||||
|
httpChallenge: {
|
||||||
|
entryPoint: "web",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const yamlStr = dump(configObject);
|
||||||
|
|
||||||
|
return yamlStr;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDefaultServerTraefikConfig = () => {
|
||||||
|
const configObject: MainTraefikConfig = {
|
||||||
|
providers: {
|
||||||
|
swarm: {
|
||||||
|
exposedByDefault: false,
|
||||||
|
watch: true,
|
||||||
|
},
|
||||||
|
docker: {
|
||||||
|
exposedByDefault: false,
|
||||||
|
watch: true,
|
||||||
|
network: "dokploy-network",
|
||||||
|
},
|
||||||
|
file: {
|
||||||
|
directory: "/etc/dokploy/traefik/dynamic",
|
||||||
|
watch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
entryPoints: {
|
||||||
|
web: {
|
||||||
|
address: `:${TRAEFIK_PORT}`,
|
||||||
|
},
|
||||||
|
websecure: {
|
||||||
|
address: `:${TRAEFIK_SSL_PORT}`,
|
||||||
|
http3: {
|
||||||
|
advertisedPort: TRAEFIK_HTTP3_PORT,
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
tls: {
|
||||||
|
certResolver: "letsencrypt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
insecure: true,
|
||||||
|
},
|
||||||
|
certificatesResolvers: {
|
||||||
|
letsencrypt: {
|
||||||
|
acme: {
|
||||||
|
email: "test@localhost.com",
|
||||||
|
storage: "/etc/dokploy/traefik/dynamic/acme.json",
|
||||||
|
httpChallenge: {
|
||||||
|
entryPoint: "web",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const yamlStr = dump(configObject);
|
||||||
|
|
||||||
|
return yamlStr;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDefaultCaddyConfig = () => {
|
||||||
|
const { CADDY_PATH, CADDY_DYNAMIC_PATH } = paths();
|
||||||
|
const mainConfig = path.join(CADDY_PATH, "Caddyfile");
|
||||||
|
const acmeJsonPath = path.join(CADDY_DYNAMIC_PATH, "acme.json");
|
||||||
|
|
||||||
|
if (existsSync(acmeJsonPath)) {
|
||||||
|
chmodSync(acmeJsonPath, "600");
|
||||||
|
}
|
||||||
|
if (existsSync(mainConfig)) {
|
||||||
|
console.log("Main config already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const yamlStr = getDefaultCaddyConfig();
|
||||||
|
mkdirSync(CADDY_PATH, { recursive: true });
|
||||||
|
writeFileSync(mainConfig, yamlStr, "utf8");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDefaultMiddlewares = () => {
|
||||||
|
const defaultMiddlewares = {
|
||||||
|
http: {
|
||||||
|
middlewares: {
|
||||||
|
"redirect-to-https": {
|
||||||
|
redirectScheme: {
|
||||||
|
scheme: "https",
|
||||||
|
permanent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const yamlStr = dump(defaultMiddlewares);
|
||||||
|
return yamlStr;
|
||||||
|
};
|
||||||
|
export const createDefaultMiddlewares = () => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
|
const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
||||||
|
if (existsSync(middlewaresPath)) {
|
||||||
|
console.log("Default middlewares already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const yamlStr = getDefaultMiddlewares();
|
||||||
|
mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true });
|
||||||
|
writeFileSync(middlewaresPath, yamlStr, "utf8");
|
||||||
|
};
|
||||||
@@ -23,7 +23,17 @@ export const runWebServerBackup = async (backup: BackupSchedule) => {
|
|||||||
try {
|
try {
|
||||||
await execAsync(`mkdir -p ${tempDir}/filesystem`);
|
await execAsync(`mkdir -p ${tempDir}/filesystem`);
|
||||||
|
|
||||||
const postgresCommand = `docker exec $(docker ps --filter "name=dokploy-postgres" -q) pg_dump -v -Fc -U dokploy -d dokploy > ${tempDir}/database.sql`;
|
// First get the container ID
|
||||||
|
const { stdout: containerId } = await execAsync(
|
||||||
|
"docker ps --filter 'name=dokploy-postgres' -q",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!containerId) {
|
||||||
|
throw new Error("PostgreSQL container not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then run pg_dump with the container ID
|
||||||
|
const postgresCommand = `docker exec ${containerId.trim()} pg_dump -v -Fc -U dokploy -d dokploy > '${tempDir}/database.sql'`;
|
||||||
await execAsync(postgresCommand);
|
await execAsync(postgresCommand);
|
||||||
|
|
||||||
await execAsync(`cp -r ${BASE_PATH}/* ${tempDir}/filesystem/`);
|
await execAsync(`cp -r ${BASE_PATH}/* ${tempDir}/filesystem/`);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export const buildRailpack = async (
|
|||||||
for (const envVar of envVariables) {
|
for (const envVar of envVariables) {
|
||||||
const [key, value] = envVar.split("=");
|
const [key, value] = envVar.split("=");
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
buildArgs.push("--secret", `id=${key},env=${key}`);
|
buildArgs.push("--secret", `id=${key},env='${key}'`);
|
||||||
env[key] = value;
|
env[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ export const getRailpackCommand = (
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (const env of envVariables) {
|
for (const env of envVariables) {
|
||||||
prepareArgs.push("--env", env);
|
prepareArgs.push("--env", `'${env}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate secrets hash for layer invalidation
|
// Calculate secrets hash for layer invalidation
|
||||||
@@ -164,7 +164,7 @@ export const getRailpackCommand = (
|
|||||||
for (const envVar of envVariables) {
|
for (const envVar of envVariables) {
|
||||||
const [key, value] = envVar.split("=");
|
const [key, value] = envVar.split("=");
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
buildArgs.push("--secret", `id=${key},env=${key}`);
|
buildArgs.push("--secret", `id=${key},env='${key}'`);
|
||||||
exportEnvs.push(`export ${key}=${value}`);
|
exportEnvs.push(`export ${key}=${value}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,6 +249,11 @@ export const addDomainToCompose = async (
|
|||||||
labels.unshift("traefik.enable=true");
|
labels.unshift("traefik.enable=true");
|
||||||
}
|
}
|
||||||
labels.unshift(...httpLabels);
|
labels.unshift(...httpLabels);
|
||||||
|
if (!compose.isolatedDeployment) {
|
||||||
|
if (!labels.includes("traefik.docker.network=dokploy-network")) {
|
||||||
|
labels.unshift("traefik.docker.network=dokploy-network");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!compose.isolatedDeployment) {
|
if (!compose.isolatedDeployment) {
|
||||||
|
|||||||
@@ -264,7 +264,11 @@ export const getGitlabRepositories = async (gitlabId?: string) => {
|
|||||||
const groupName = gitlabProvider.groupName?.toLowerCase();
|
const groupName = gitlabProvider.groupName?.toLowerCase();
|
||||||
|
|
||||||
if (groupName) {
|
if (groupName) {
|
||||||
return full_path.toLowerCase().includes(groupName) && kind === "group";
|
const isIncluded = groupName
|
||||||
|
.split(",")
|
||||||
|
.some((name) => full_path.toLowerCase().includes(name));
|
||||||
|
|
||||||
|
return isIncluded && kind === "group";
|
||||||
}
|
}
|
||||||
return kind === "user";
|
return kind === "user";
|
||||||
});
|
});
|
||||||
@@ -431,7 +435,9 @@ export const testGitlabConnection = async (
|
|||||||
const { full_path, kind } = repo.namespace;
|
const { full_path, kind } = repo.namespace;
|
||||||
|
|
||||||
if (groupName) {
|
if (groupName) {
|
||||||
return full_path.toLowerCase().includes(groupName) && kind === "group";
|
return groupName
|
||||||
|
.split(",")
|
||||||
|
.some((name) => full_path.toLowerCase().includes(name));
|
||||||
}
|
}
|
||||||
return kind === "user";
|
return kind === "user";
|
||||||
});
|
});
|
||||||
|
|||||||
88
pnpm-lock.yaml
generated
88
pnpm-lock.yaml
generated
@@ -17,7 +17,7 @@ importers:
|
|||||||
version: 1.9.4
|
version: 1.9.4
|
||||||
'@commitlint/cli':
|
'@commitlint/cli':
|
||||||
specifier: ^19.3.0
|
specifier: ^19.3.0
|
||||||
version: 19.3.0(@types/node@18.19.42)(typescript@5.7.2)
|
version: 19.3.0(@types/node@18.19.42)(typescript@5.8.3)
|
||||||
'@commitlint/config-conventional':
|
'@commitlint/config-conventional':
|
||||||
specifier: ^19.2.2
|
specifier: ^19.2.2
|
||||||
version: 19.2.2
|
version: 19.2.2
|
||||||
@@ -266,8 +266,8 @@ importers:
|
|||||||
specifier: 5.1.1
|
specifier: 5.1.1
|
||||||
version: 5.1.1(encoding@0.1.13)
|
version: 5.1.1(encoding@0.1.13)
|
||||||
better-auth:
|
better-auth:
|
||||||
specifier: 1.2.4
|
specifier: 1.2.6
|
||||||
version: 1.2.4(typescript@5.5.3)
|
version: 1.2.6
|
||||||
bl:
|
bl:
|
||||||
specifier: 6.0.11
|
specifier: 6.0.11
|
||||||
version: 6.0.11
|
version: 6.0.11
|
||||||
@@ -607,8 +607,8 @@ importers:
|
|||||||
specifier: ^0.0.13
|
specifier: ^0.0.13
|
||||||
version: 0.0.13(zod@3.23.8)
|
version: 0.0.13(zod@3.23.8)
|
||||||
'@better-auth/utils':
|
'@better-auth/utils':
|
||||||
specifier: 0.2.3
|
specifier: 0.2.4
|
||||||
version: 0.2.3
|
version: 0.2.4
|
||||||
'@faker-js/faker':
|
'@faker-js/faker':
|
||||||
specifier: ^8.4.1
|
specifier: ^8.4.1
|
||||||
version: 8.4.1
|
version: 8.4.1
|
||||||
@@ -640,8 +640,8 @@ importers:
|
|||||||
specifier: 5.1.1
|
specifier: 5.1.1
|
||||||
version: 5.1.1(encoding@0.1.13)
|
version: 5.1.1(encoding@0.1.13)
|
||||||
better-auth:
|
better-auth:
|
||||||
specifier: 1.2.4
|
specifier: 1.2.6
|
||||||
version: 1.2.4(typescript@5.5.3)
|
version: 1.2.6
|
||||||
bl:
|
bl:
|
||||||
specifier: 6.0.11
|
specifier: 6.0.11
|
||||||
version: 6.0.11
|
version: 6.0.11
|
||||||
@@ -924,11 +924,11 @@ packages:
|
|||||||
'@balena/dockerignore@1.0.2':
|
'@balena/dockerignore@1.0.2':
|
||||||
resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==}
|
resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==}
|
||||||
|
|
||||||
'@better-auth/utils@0.2.3':
|
'@better-auth/utils@0.2.4':
|
||||||
resolution: {integrity: sha512-Ap1GaSmo6JYhJhxJOpUB0HobkKPTNzfta+bLV89HfpyCAHN7p8ntCrmNFHNAVD0F6v0mywFVEUg1FUhNCc81Rw==}
|
resolution: {integrity: sha512-ayiX87Xd5sCHEplAdeMgwkA0FgnXsEZBgDn890XHHwSWNqqRZDYOq3uj2Ei2leTv1I2KbG5HHn60Ah1i2JWZjQ==}
|
||||||
|
|
||||||
'@better-fetch/fetch@1.1.15':
|
'@better-fetch/fetch@1.1.18':
|
||||||
resolution: {integrity: sha512-0Bl8YYj1f8qCTNHeSn5+1DWv2hy7rLBrQ8rS8Y9XYloiwZEfc3k4yspIG0llRxafxqhGCwlGRg+F8q1HZRCMXA==}
|
resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==}
|
||||||
|
|
||||||
'@biomejs/biome@1.9.4':
|
'@biomejs/biome@1.9.4':
|
||||||
resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==}
|
resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==}
|
||||||
@@ -3836,11 +3836,11 @@ packages:
|
|||||||
before-after-hook@2.2.3:
|
before-after-hook@2.2.3:
|
||||||
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
||||||
|
|
||||||
better-auth@1.2.4:
|
better-auth@1.2.6:
|
||||||
resolution: {integrity: sha512-/ZK2jbUjm8JwdeCLFrUWUBmexPyI9PkaLVXWLWtN60sMDHTY8B5G72wcHglo1QMFBaw4G0qFkP5ayl9k6XfDaA==}
|
resolution: {integrity: sha512-RVy6nfNCXpohx49zP2ChUO3zN0nvz5UXuETJIhWU+dshBKpFMk4P4hAQauM3xqTJdd9hfeB5y+segmG1oYGTJQ==}
|
||||||
|
|
||||||
better-call@1.0.3:
|
better-call@1.0.7:
|
||||||
resolution: {integrity: sha512-DUKImKoDIy5UtCvQbHTg0wuBRse6gu1Yvznn7+1B3I5TeY8sclRPFce0HI+4WF2bcb+9PqmkET8nXZubrHQh9A==}
|
resolution: {integrity: sha512-p5kEthErx3HsW9dCCvvEx+uuEdncn0ZrlqrOG3TkR1aVYgynpwYbTVU90nY8/UwfMhROzqZWs8vryainSQxrNg==}
|
||||||
|
|
||||||
binary-extensions@2.3.0:
|
binary-extensions@2.3.0:
|
||||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
@@ -7093,8 +7093,8 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
typescript@5.7.2:
|
typescript@5.8.3:
|
||||||
resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==}
|
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -7210,14 +7210,6 @@ packages:
|
|||||||
v8-compile-cache-lib@3.0.1:
|
v8-compile-cache-lib@3.0.1:
|
||||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||||
|
|
||||||
valibot@1.0.0-beta.15:
|
|
||||||
resolution: {integrity: sha512-BKy8XosZkDHWmYC+cJG74LBzP++Gfntwi33pP3D3RKztz2XV9jmFWnkOi21GoqARP8wAWARwhV6eTr1JcWzjGw==}
|
|
||||||
peerDependencies:
|
|
||||||
typescript: '>=5'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
typescript:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
vfile-message@4.0.2:
|
vfile-message@4.0.2:
|
||||||
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
|
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
|
||||||
|
|
||||||
@@ -7567,11 +7559,12 @@ snapshots:
|
|||||||
|
|
||||||
'@balena/dockerignore@1.0.2': {}
|
'@balena/dockerignore@1.0.2': {}
|
||||||
|
|
||||||
'@better-auth/utils@0.2.3':
|
'@better-auth/utils@0.2.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
typescript: 5.8.3
|
||||||
uncrypto: 0.1.3
|
uncrypto: 0.1.3
|
||||||
|
|
||||||
'@better-fetch/fetch@1.1.15': {}
|
'@better-fetch/fetch@1.1.18': {}
|
||||||
|
|
||||||
'@biomejs/biome@1.9.4':
|
'@biomejs/biome@1.9.4':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -7678,11 +7671,11 @@ snapshots:
|
|||||||
style-mod: 4.1.2
|
style-mod: 4.1.2
|
||||||
w3c-keyname: 2.2.8
|
w3c-keyname: 2.2.8
|
||||||
|
|
||||||
'@commitlint/cli@19.3.0(@types/node@18.19.42)(typescript@5.7.2)':
|
'@commitlint/cli@19.3.0(@types/node@18.19.42)(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@commitlint/format': 19.3.0
|
'@commitlint/format': 19.3.0
|
||||||
'@commitlint/lint': 19.2.2
|
'@commitlint/lint': 19.2.2
|
||||||
'@commitlint/load': 19.2.0(@types/node@18.19.42)(typescript@5.7.2)
|
'@commitlint/load': 19.2.0(@types/node@18.19.42)(typescript@5.8.3)
|
||||||
'@commitlint/read': 19.2.1
|
'@commitlint/read': 19.2.1
|
||||||
'@commitlint/types': 19.0.3
|
'@commitlint/types': 19.0.3
|
||||||
execa: 8.0.1
|
execa: 8.0.1
|
||||||
@@ -7729,15 +7722,15 @@ snapshots:
|
|||||||
'@commitlint/rules': 19.0.3
|
'@commitlint/rules': 19.0.3
|
||||||
'@commitlint/types': 19.0.3
|
'@commitlint/types': 19.0.3
|
||||||
|
|
||||||
'@commitlint/load@19.2.0(@types/node@18.19.42)(typescript@5.7.2)':
|
'@commitlint/load@19.2.0(@types/node@18.19.42)(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@commitlint/config-validator': 19.0.3
|
'@commitlint/config-validator': 19.0.3
|
||||||
'@commitlint/execute-rule': 19.0.0
|
'@commitlint/execute-rule': 19.0.0
|
||||||
'@commitlint/resolve-extends': 19.1.0
|
'@commitlint/resolve-extends': 19.1.0
|
||||||
'@commitlint/types': 19.0.3
|
'@commitlint/types': 19.0.3
|
||||||
chalk: 5.3.0
|
chalk: 5.3.0
|
||||||
cosmiconfig: 9.0.0(typescript@5.7.2)
|
cosmiconfig: 9.0.0(typescript@5.8.3)
|
||||||
cosmiconfig-typescript-loader: 5.0.0(@types/node@18.19.42)(cosmiconfig@9.0.0(typescript@5.7.2))(typescript@5.7.2)
|
cosmiconfig-typescript-loader: 5.0.0(@types/node@18.19.42)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3)
|
||||||
lodash.isplainobject: 4.0.6
|
lodash.isplainobject: 4.0.6
|
||||||
lodash.merge: 4.6.2
|
lodash.merge: 4.6.2
|
||||||
lodash.uniq: 4.5.0
|
lodash.uniq: 4.5.0
|
||||||
@@ -10547,27 +10540,24 @@ snapshots:
|
|||||||
|
|
||||||
before-after-hook@2.2.3: {}
|
before-after-hook@2.2.3: {}
|
||||||
|
|
||||||
better-auth@1.2.4(typescript@5.5.3):
|
better-auth@1.2.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/utils': 0.2.3
|
'@better-auth/utils': 0.2.4
|
||||||
'@better-fetch/fetch': 1.1.15
|
'@better-fetch/fetch': 1.1.18
|
||||||
'@noble/ciphers': 0.6.0
|
'@noble/ciphers': 0.6.0
|
||||||
'@noble/hashes': 1.7.1
|
'@noble/hashes': 1.7.1
|
||||||
'@simplewebauthn/browser': 13.1.0
|
'@simplewebauthn/browser': 13.1.0
|
||||||
'@simplewebauthn/server': 13.1.1
|
'@simplewebauthn/server': 13.1.1
|
||||||
better-call: 1.0.3
|
better-call: 1.0.7
|
||||||
defu: 6.1.4
|
defu: 6.1.4
|
||||||
jose: 5.9.6
|
jose: 5.9.6
|
||||||
kysely: 0.27.6
|
kysely: 0.27.6
|
||||||
nanostores: 0.11.3
|
nanostores: 0.11.3
|
||||||
valibot: 1.0.0-beta.15(typescript@5.5.3)
|
|
||||||
zod: 3.24.1
|
zod: 3.24.1
|
||||||
transitivePeerDependencies:
|
|
||||||
- typescript
|
|
||||||
|
|
||||||
better-call@1.0.3:
|
better-call@1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-fetch/fetch': 1.1.15
|
'@better-fetch/fetch': 1.1.18
|
||||||
rou3: 0.5.1
|
rou3: 0.5.1
|
||||||
set-cookie-parser: 2.7.1
|
set-cookie-parser: 2.7.1
|
||||||
uncrypto: 0.1.3
|
uncrypto: 0.1.3
|
||||||
@@ -10942,21 +10932,21 @@ snapshots:
|
|||||||
|
|
||||||
core-js@3.39.0: {}
|
core-js@3.39.0: {}
|
||||||
|
|
||||||
cosmiconfig-typescript-loader@5.0.0(@types/node@18.19.42)(cosmiconfig@9.0.0(typescript@5.7.2))(typescript@5.7.2):
|
cosmiconfig-typescript-loader@5.0.0(@types/node@18.19.42)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.19.42
|
'@types/node': 18.19.42
|
||||||
cosmiconfig: 9.0.0(typescript@5.7.2)
|
cosmiconfig: 9.0.0(typescript@5.8.3)
|
||||||
jiti: 1.21.6
|
jiti: 1.21.6
|
||||||
typescript: 5.7.2
|
typescript: 5.8.3
|
||||||
|
|
||||||
cosmiconfig@9.0.0(typescript@5.7.2):
|
cosmiconfig@9.0.0(typescript@5.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
env-paths: 2.2.1
|
env-paths: 2.2.1
|
||||||
import-fresh: 3.3.0
|
import-fresh: 3.3.0
|
||||||
js-yaml: 4.1.0
|
js-yaml: 4.1.0
|
||||||
parse-json: 5.2.0
|
parse-json: 5.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.7.2
|
typescript: 5.8.3
|
||||||
|
|
||||||
cpu-features@0.0.10:
|
cpu-features@0.0.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -14171,7 +14161,7 @@ snapshots:
|
|||||||
|
|
||||||
typescript@5.5.3: {}
|
typescript@5.5.3: {}
|
||||||
|
|
||||||
typescript@5.7.2: {}
|
typescript@5.8.3: {}
|
||||||
|
|
||||||
ufo@1.5.4: {}
|
ufo@1.5.4: {}
|
||||||
|
|
||||||
@@ -14292,10 +14282,6 @@ snapshots:
|
|||||||
v8-compile-cache-lib@3.0.1:
|
v8-compile-cache-lib@3.0.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
valibot@1.0.0-beta.15(typescript@5.5.3):
|
|
||||||
optionalDependencies:
|
|
||||||
typescript: 5.5.3
|
|
||||||
|
|
||||||
vfile-message@4.0.2:
|
vfile-message@4.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
|
|||||||
Reference in New Issue
Block a user