diff --git a/.vscode/settings.json b/.vscode/settings.json index 99357f236..463ce8e24 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,8 @@ "editor.codeActionsOnSave": { "source.fixAll.biome": "explicit", "source.organizeImports.biome": "explicit" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" } } diff --git a/Dockerfile b/Dockerfile index ed936508f..4cab0e8f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -66,7 +66,7 @@ COPY --from=buildpacksio/pack:0.39.1 /usr/local/bin/pack /usr/local/bin/pack EXPOSE 3000 -HEALTHCHECK --interval=10s --timeout=3s --retries=10 \ +HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=5 \ CMD curl -fs http://localhost:3000/api/trpc/settings.health || exit 1 CMD ["sh", "-c", "pnpm run wait-for-postgres && exec pnpm start"] diff --git a/apps/dokploy/__test__/templates/config.template.test.ts b/apps/dokploy/__test__/templates/config.template.test.ts index 202abdf2d..52be798d7 100644 --- a/apps/dokploy/__test__/templates/config.template.test.ts +++ b/apps/dokploy/__test__/templates/config.template.test.ts @@ -494,4 +494,49 @@ describe("processTemplate", () => { expect(result.mounts).toHaveLength(1); }); }); + + describe("isolated deployment config", () => { + it("should default to isolated=true when not specified", () => { + const template: CompleteTemplate = { + metadata: {} as any, + variables: {}, + config: { + domains: [], + env: {}, + }, + }; + + expect(template.config.isolated).toBeUndefined(); + // undefined !== false => isolatedDeployment = true + expect(template.config.isolated !== false).toBe(true); + }); + + it("should be isolated when isolated=true is explicitly set", () => { + const template: CompleteTemplate = { + metadata: {} as any, + variables: {}, + config: { + isolated: true, + domains: [], + env: {}, + }, + }; + + expect(template.config.isolated !== false).toBe(true); + }); + + it("should disable isolated deployment when isolated=false", () => { + const template: CompleteTemplate = { + metadata: {} as any, + variables: {}, + config: { + isolated: false, + domains: [], + env: {}, + }, + }; + + expect(template.config.isolated !== false).toBe(false); + }); + }); }); diff --git a/apps/dokploy/__test__/templates/helpers.template.test.ts b/apps/dokploy/__test__/templates/helpers.template.test.ts index f2af2717b..e95bbf072 100644 --- a/apps/dokploy/__test__/templates/helpers.template.test.ts +++ b/apps/dokploy/__test__/templates/helpers.template.test.ts @@ -30,9 +30,7 @@ describe("helpers functions", () => { const domain = processValue("${domain}", {}, mockSchema); expect(domain.startsWith(`${mockSchema.projectName}-`)).toBeTruthy(); expect( - domain.endsWith( - `${mockSchema.serverIp.replaceAll(".", "-")}.traefik.me`, - ), + domain.endsWith(`${mockSchema.serverIp.replaceAll(".", "-")}.sslip.io`), ).toBeTruthy(); }); }); diff --git a/apps/dokploy/components/dashboard/application/domains/columns.tsx b/apps/dokploy/components/dashboard/application/domains/columns.tsx index cd8254aa0..b88443dcc 100644 --- a/apps/dokploy/components/dashboard/application/domains/columns.tsx +++ b/apps/dokploy/components/dashboard/application/domains/columns.tsx @@ -21,9 +21,9 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import type { RouterOutputs } from "@/utils/api"; -import type { ValidationStates } from "./show-domains"; -import { AddDomain } from "./handle-domain"; import { DnsHelperModal } from "./dns-helper-modal"; +import { AddDomain } from "./handle-domain"; +import type { ValidationStates } from "./show-domains"; export type Domain = | RouterOutputs["domain"]["byApplicationId"][0] @@ -168,7 +168,7 @@ export const createColumns = ({ {domain.certificateType} )} - {!domain.host.includes("traefik.me") && ( + {!domain.host.includes("sslip.io") && ( @@ -256,7 +256,7 @@ export const createColumns = ({ return (
- {!domain.host.includes("traefik.me") && ( + {!domain.host.includes("sslip.io") && ( { const https = form.watch("https"); const domainType = form.watch("domainType"); const host = form.watch("host"); - const isTraefikMeDomain = host?.includes("traefik.me") || false; + const isTraefikMeDomain = host?.includes("sslip.io") || false; useEffect(() => { if (data) { @@ -513,7 +513,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => { render={({ field }) => ( {!canGenerateTraefikMeDomains && - field.value.includes("traefik.me") && ( + field.value.includes("sslip.io") && ( You need to set an IP address in your{" "} { ? "Remote Servers -> Server -> Edit Server -> Update IP Address" : "Web Server -> Server -> Update Server IP"} {" "} - to make your traefik.me domain work. + to make your sslip.io domain work. )} {isTraefikMeDomain && ( - Note: traefik.me is a public HTTP + Note: sslip.io is a public HTTP service and does not support SSL/HTTPS. HTTPS and certificate options will not have any effect. @@ -567,7 +567,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => { sideOffset={5} className="max-w-[10rem]" > -

Generate traefik.me domain

+

Generate sslip.io domain

diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx index e971f9ab7..af8d691c0 100644 --- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx @@ -425,7 +425,7 @@ export const ShowDomains = ({ id, type }: Props) => { )}
- {!item.host.includes("traefik.me") && ( + {!item.host.includes("sslip.io") && ( { return (
- -
-
-
- ( - -
- Repository URL - {field.value?.startsWith("https://") && ( - - - View Repository - - )} -
- - - - -
- )} - /> -
- {sshKeys && sshKeys.length > 0 ? ( - ( - - - SSH Key - - - - - - - )} - /> - ) : ( - + +
+ ( + +
+ Repository URL + {field.value?.startsWith("https://") && ( + + + View Repository + + )} +
+ + + + +
)} -
-
+ /> + {sshKeys && sshKeys.length > 0 ? ( ( - - Branch + + + SSH Key + + - + - )} /> -
+ ) : ( + + )} + + ( + + Branch + + + + + + )} + /> ( - + Build Path @@ -223,7 +220,7 @@ export const SaveGitProvider = ({ applicationId }: Props) => { control={form.control} name="watchPaths" render={({ field }) => ( - +
Watch Paths diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx index 02cae2c4a..0781f55a8 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx @@ -5,6 +5,7 @@ import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { GiteaIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -72,7 +73,10 @@ const GiteaProviderSchema = z.object({ owner: z.string().min(1, "Owner is required"), }) .required(), - branch: z.string().min(1, "Branch is required"), + branch: z + .string() + .min(1, "Branch is required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), giteaId: z.string().min(1, "Gitea Provider is required"), watchPaths: z.array(z.string()).default([]), enableSubmodules: z.boolean().optional(), diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx index 6bce2d243..b4f069ee2 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx @@ -5,6 +5,7 @@ import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { GithubIcon } from "@/components/icons/data-tools-icons"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -55,7 +56,10 @@ const GithubProviderSchema = z.object({ owner: z.string().min(1, "Owner is required"), }) .required(), - branch: z.string().min(1, "Branch is required"), + branch: z + .string() + .min(1, "Branch is required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), githubId: z.string().min(1, "Github Provider is required"), watchPaths: z.array(z.string()).optional(), triggerType: z.enum(["push", "tag"]).default("push"), diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx index b49a1658f..d867654b5 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx @@ -5,6 +5,7 @@ import { useEffect, useMemo } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { GitlabIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -58,7 +59,10 @@ const GitlabProviderSchema = z.object({ id: z.number().nullable(), }) .required(), - branch: z.string().min(1, "Branch is required"), + branch: z + .string() + .min(1, "Branch is required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), gitlabId: z.string().min(1, "Gitlab Provider is required"), watchPaths: z.array(z.string()).optional(), enableSubmodules: z.boolean().default(false), diff --git a/apps/dokploy/components/dashboard/application/general/show.tsx b/apps/dokploy/components/dashboard/application/general/show.tsx index 01fc9e84a..474d75201 100644 --- a/apps/dokploy/components/dashboard/application/general/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/show.tsx @@ -58,7 +58,7 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { Deploy Settings - + {canDeploy && ( { > {canUpdateService && ( -
+
Autodeploy { )} {canUpdateService && ( -
+
Clean Cache { if (data) { @@ -162,7 +162,7 @@ export const AddPreviewDomain = ({ {isTraefikMeDomain && ( - Note: traefik.me is a public HTTP + Note: sslip.io is a public HTTP service and does not support SSL/HTTPS. HTTPS and certificate options will not have any effect. @@ -202,7 +202,7 @@ export const AddPreviewDomain = ({ sideOffset={5} className="max-w-[10rem]" > -

Generate traefik.me domain

+

Generate sslip.io domain

diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index d2840cd67..13b9a1603 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -88,7 +88,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { const form = useForm({ defaultValues: { env: "", - wildcardDomain: "*.traefik.me", + wildcardDomain: "*.sslip.io", port: 3000, previewLimit: 3, previewLabels: [], @@ -102,7 +102,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { const previewHttps = form.watch("previewHttps"); const wildcardDomain = form.watch("wildcardDomain"); - const isTraefikMeDomain = wildcardDomain?.includes("traefik.me") || false; + const isTraefikMeDomain = wildcardDomain?.includes("sslip.io") || false; useEffect(() => { setIsEnabled(data?.isPreviewDeploymentsActive || false); @@ -114,7 +114,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { env: data.previewEnv || "", buildArgs: data.previewBuildArgs || "", buildSecrets: data.previewBuildSecrets || "", - wildcardDomain: data.previewWildcard || "*.traefik.me", + wildcardDomain: data.previewWildcard || "*.sslip.io", port: data.previewPort || 3000, previewLabels: data.previewLabels || [], previewLimit: data.previewLimit || 3, @@ -173,7 +173,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
{isTraefikMeDomain && ( - Note: traefik.me is a public HTTP service and + Note: sslip.io is a public HTTP service and does not support SSL/HTTPS. HTTPS and certificate options will not have any effect. @@ -192,7 +192,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { Wildcard Domain - + diff --git a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx index 36ddb53f1..3fd3089de 100644 --- a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx @@ -80,6 +80,7 @@ export const commonCronExpressions = [ const formSchema = z .object({ name: z.string().min(1, "Name is required"), + description: z.string().optional(), cronExpression: z.string().min(1, "Cron expression is required"), shellType: z.enum(["bash", "sh"]).default("bash"), command: z.string(), @@ -224,6 +225,7 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { resolver: standardSchemaResolver(formSchema), defaultValues: { name: "", + description: "", cronExpression: "", shellType: "bash", command: "", @@ -263,6 +265,7 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { if (scheduleId && schedule) { form.reset({ name: schedule.name, + description: schedule.description || "", cronExpression: schedule.cronExpression, shellType: schedule.shellType, command: schedule.command, @@ -479,6 +482,26 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { )} /> + ( + + Description + + + + + Optional description of what this schedule does + + + + )} + /> + { {schedule.enabled ? "Enabled" : "Disabled"}
+ {schedule.description && ( +

+ {schedule.description} +

+ )}
diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx index e9d024fd3..845f12830 100644 --- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx +++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx @@ -49,12 +49,12 @@ export const ComposeFileEditor = ({ composeId }: Props) => { const composeFile = form.watch("composeFile"); useEffect(() => { - if (data && !composeFile) { + if (data) { form.reset({ composeFile: data.composeFile || "", }); } - }, [form, form.reset, data]); + }, [form, data]); useEffect(() => { if (data?.composeFile !== undefined) { diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx index 3e099251e..745f72d3b 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx @@ -5,6 +5,7 @@ import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { BitbucketIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -57,7 +58,10 @@ const BitbucketProviderSchema = z.object({ slug: z.string().optional(), }) .required(), - branch: z.string().min(1, "Branch is required"), + branch: z + .string() + .min(1, "Branch is required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), bitbucketId: z.string().min(1, "Bitbucket Provider is required"), watchPaths: z.array(z.string()).optional(), enableSubmodules: z.boolean().default(false), diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx index 7878225a9..7ebb7edb9 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx @@ -6,6 +6,7 @@ import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { GitIcon } from "@/components/icons/data-tools-icons"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -41,7 +42,10 @@ const GitProviderSchema = z.object({ repositoryURL: z.string().min(1, { message: "Repository URL is required", }), - branch: z.string().min(1, "Branch required"), + branch: z + .string() + .min(1, "Branch required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), sshKey: z.string().optional(), watchPaths: z.array(z.string()).optional(), enableSubmodules: z.boolean().default(false), diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx index 7ea71fc89..7515af723 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx @@ -1,10 +1,11 @@ import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema"; -import { CheckIcon, ChevronsUpDown, Plus, X, HelpCircle } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; import Link from "next/link"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { GiteaIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -57,7 +58,10 @@ const GiteaProviderSchema = z.object({ owner: z.string().min(1, "Owner is required"), }) .required(), - branch: z.string().min(1, "Branch is required"), + branch: z + .string() + .min(1, "Branch is required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), giteaId: z.string().min(1, "Gitea Provider is required"), watchPaths: z.array(z.string()).optional(), enableSubmodules: z.boolean().default(false), diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx index 827ce1a8a..10075fb5c 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx @@ -1,3 +1,4 @@ +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema"; import { CheckIcon, ChevronsUpDown, HelpCircle, X } from "lucide-react"; import Link from "next/link"; @@ -55,7 +56,10 @@ const GithubProviderSchema = z.object({ owner: z.string().min(1, "Owner is required"), }) .required(), - branch: z.string().min(1, "Branch is required"), + branch: z + .string() + .min(1, "Branch is required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), githubId: z.string().min(1, "Github Provider is required"), watchPaths: z.array(z.string()).optional(), triggerType: z.enum(["push", "tag"]).default("push"), diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx index 63de87d8f..a81774fec 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx @@ -5,6 +5,7 @@ import { useEffect, useMemo } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { GitlabIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -58,7 +59,10 @@ const GitlabProviderSchema = z.object({ gitlabPathNamespace: z.string().min(1), }) .required(), - branch: z.string().min(1, "Branch is required"), + branch: z + .string() + .min(1, "Branch is required") + .regex(VALID_BRANCH_REGEX, "Invalid branch name"), gitlabId: z.string().min(1, "Gitlab Provider is required"), watchPaths: z.array(z.string()).optional(), enableSubmodules: z.boolean().default(false), diff --git a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx index 7b212acb9..8ae6453fd 100644 --- a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx +++ b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx @@ -288,7 +288,6 @@ export const RestoreBackup = ({ toast.error("Please select a database type"); return; } - console.log({ data }); setIsDeploying(true); }; diff --git a/apps/dokploy/components/dashboard/docker/logs/analyze-logs.tsx b/apps/dokploy/components/dashboard/docker/logs/analyze-logs.tsx index 267735eac..d40697437 100644 --- a/apps/dokploy/components/dashboard/docker/logs/analyze-logs.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/analyze-logs.tsx @@ -1,5 +1,14 @@ "use client"; -import { Bot, Loader2, RotateCcw, Settings, X } from "lucide-react"; +import copy from "copy-to-clipboard"; +import { + Bot, + Check, + Copy, + Loader2, + RotateCcw, + Settings, + X, +} from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import ReactMarkdown from "react-markdown"; @@ -30,6 +39,7 @@ const MAX_LOG_LINES = 200; export function AnalyzeLogs({ logs, context }: Props) { const [open, setOpen] = useState(false); const [aiId, setAiId] = useState(""); + const [copied, setCopied] = useState(false); const { data: providers } = api.ai.getEnabledProviders.useQuery(undefined, { enabled: open, }); @@ -52,6 +62,15 @@ export function AnalyzeLogs({ logs, context }: Props) { mutate({ aiId, logs: logsText, context }); }; + const handleCopy = () => { + if (!data?.analysis) return; + const success = copy(data.analysis); + if (success) { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } + }; + return ( +
- +
Created diff --git a/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx b/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx index 2245e724e..3dd6814a2 100644 --- a/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx +++ b/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx @@ -1,10 +1,10 @@ +import { toast } from "sonner"; import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input"; import { UpdateDatabasePassword } from "@/components/shared/update-database-password"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import { toast } from "sonner"; interface Props { redisId: string; diff --git a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx index acfd241ff..13525a37c 100644 --- a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx +++ b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx @@ -25,7 +25,6 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { NumberInput } from "@/components/ui/input"; import { Dialog, DialogContent, @@ -34,6 +33,7 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; +import { NumberInput } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Progress } from "@/components/ui/progress"; import { Switch } from "@/components/ui/switch"; diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx index 9520a7f7a..33b297006 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx @@ -1,3 +1,5 @@ +import { HelpCircle } from "lucide-react"; +import { toast } from "sonner"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { @@ -7,8 +9,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { HelpCircle } from "lucide-react"; -import { toast } from "sonner"; interface Props { serverId?: string; diff --git a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx index 073ed663d..e54140bbf 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { EnterpriseFeatureLocked } from "@/components/proprietary/enterprise-feature-gate"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; @@ -26,7 +27,6 @@ import { FormMessage, } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; -import { EnterpriseFeatureLocked } from "@/components/proprietary/enterprise-feature-gate"; import { api, type RouterOutputs } from "@/utils/api"; /** Shape returned by project.allForPermissions (admin only). Used for the permissions UI. */ diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 29c7be5eb..077ac9124 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -141,14 +141,14 @@ export const WebDomain = () => { { return ( - + Domain { name="letsEncryptEmail" render={({ field }) => { return ( - + Let's Encrypt Email { name="certificateType" render={({ field }) => { return ( - + Certificate Provider + @@ -161,7 +168,7 @@ export async function getServerSideProps(_context: GetServerSidePropsContext) { if (!IS_CLOUD) { return { redirect: { - permanent: true, + permanent: false, destination: "/", }, }; diff --git a/apps/dokploy/pages/swagger.tsx b/apps/dokploy/pages/swagger.tsx index b461a85ec..2aeb0eff3 100644 --- a/apps/dokploy/pages/swagger.tsx +++ b/apps/dokploy/pages/swagger.tsx @@ -82,7 +82,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { if (!user) { return { redirect: { - permanent: true, + permanent: false, destination: "/", }, }; @@ -103,7 +103,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { if (!userPermissions?.api.read) { return { redirect: { - permanent: true, + permanent: false, destination: "/", }, }; diff --git a/apps/dokploy/scripts/migrate-auth-secret.ts b/apps/dokploy/scripts/migrate-auth-secret.ts new file mode 100644 index 000000000..5a71678d9 --- /dev/null +++ b/apps/dokploy/scripts/migrate-auth-secret.ts @@ -0,0 +1,97 @@ +/** + * Use this command to automatically migrate the auth secret: curl -sSL https://dokploy.com/security/0.29.3.sh | bash + * Migration script: re-encrypt 2FA secrets after rotating BETTER_AUTH_SECRET. + * + * Usage: + * OLD_SECRET= NEW_SECRET= npx tsx apps/dokploy/scripts/migrate-auth-secret.ts + * + * Both OLD_SECRET and NEW_SECRET are required. + * Run this BEFORE restarting Dokploy with the new secret. + */ +import { db } from "@dokploy/server/db"; +import { twoFactor } from "@dokploy/server/db/schema"; +import { symmetricDecrypt, symmetricEncrypt } from "better-auth/crypto"; +import { eq } from "drizzle-orm"; + +const OLD_SECRET = process.env.OLD_SECRET as string; +const NEW_SECRET = process.env.NEW_SECRET as string; + +if (!OLD_SECRET || !NEW_SECRET) { + console.error( + "❌ OLD_SECRET and NEW_SECRET environment variables are required.", + ); + console.error( + " Usage: OLD_SECRET= NEW_SECRET= npx tsx apps/dokploy/scripts/migrate-auth-secret.ts", + ); + process.exit(1); +} + +if (OLD_SECRET === NEW_SECRET) { + console.error("❌ OLD_SECRET and NEW_SECRET must be different."); + process.exit(1); +} + +async function reEncrypt( + value: string, + oldSecret: string, + newSecret: string, +): Promise { + const plaintext = await symmetricDecrypt({ key: oldSecret, data: value }); + return symmetricEncrypt({ key: newSecret, data: plaintext }); +} + +async function main() { + console.log("🔍 Fetching 2FA records..."); + const records = await db.select().from(twoFactor); + + if (records.length === 0) { + console.log("✅ No 2FA records found, nothing to migrate."); + return; + } + + console.log(`📦 Found ${records.length} 2FA record(s) to migrate.`); + + let migrated = 0; + let failed = 0; + + await db.transaction(async (tx) => { + for (const record of records) { + try { + const [newSecret, newBackupCodes] = await Promise.all([ + reEncrypt(record.secret, OLD_SECRET, NEW_SECRET), + reEncrypt(record.backupCodes, OLD_SECRET, NEW_SECRET), + ]); + + await tx + .update(twoFactor) + .set({ secret: newSecret, backupCodes: newBackupCodes }) + .where(eq(twoFactor.id, record.id)); + + migrated++; + } catch (err) { + console.error( + `❌ Failed to migrate record ${record.id} (userId: ${record.userId}):`, + err, + ); + failed++; + throw err; // rollback the whole transaction + } + } + }); + + console.log(`✅ Migrated ${migrated} record(s) successfully.`); + + if (failed > 0) { + console.error( + `❌ ${failed} record(s) failed — transaction was rolled back.`, + ); + process.exit(1); + } else { + process.exit(0); + } +} + +main().catch((err) => { + console.error("❌ Migration failed:", err); + process.exit(1); +}); diff --git a/apps/dokploy/server/api/routers/ai.ts b/apps/dokploy/server/api/routers/ai.ts index 3a299235a..81e03fe26 100644 --- a/apps/dokploy/server/api/routers/ai.ts +++ b/apps/dokploy/server/api/routers/ai.ts @@ -25,8 +25,8 @@ import { findProjectById } from "@dokploy/server/services/project"; import { getProviderHeaders, getProviderName, - selectAIProvider, type Model, + selectAIProvider, } from "@dokploy/server/utils/ai/select-ai-provider"; import { TRPCError } from "@trpc/server"; import { generateText } from "ai"; diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index d395bdffc..254818478 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -640,7 +640,7 @@ export const composeRouter = createTRPCRouter({ name: input.id, sourceType: "raw", appName: appName, - isolatedDeployment: true, + isolatedDeployment: template.config.config?.isolated !== false, }); await addNewService(ctx, compose.composeId); @@ -700,11 +700,14 @@ export const composeRouter = createTRPCRouter({ getTags: protectedProcedure .input(z.object({ baseUrl: z.string().optional() })) .query(async ({ input }) => { - const githubTemplates = await fetchTemplatesList(input.baseUrl); - - const allTags = githubTemplates.flatMap((template) => template.tags); - const uniqueTags = _.uniq(allTags); - return uniqueTags; + try { + const githubTemplates = await fetchTemplatesList(input.baseUrl); + const allTags = githubTemplates.flatMap((template) => template.tags); + return _.uniq(allTags); + } catch (error) { + console.warn("Failed to fetch template tags:", error); + return []; + } }), disconnectGitProvider: protectedProcedure .input(apiFindCompose) diff --git a/apps/dokploy/server/api/routers/libsql.ts b/apps/dokploy/server/api/routers/libsql.ts index 47798393e..77fff4e59 100644 --- a/apps/dokploy/server/api/routers/libsql.ts +++ b/apps/dokploy/server/api/routers/libsql.ts @@ -6,6 +6,7 @@ import { findEnvironmentById, findLibsqlById, findProjectById, + getAccessibleServerIds, getContainerLogs, IS_CLOUD, rebuildDatabase, @@ -16,7 +17,6 @@ import { stopService, stopServiceRemote, updateLibsqlById, - getAccessibleServerIds, } from "@dokploy/server"; import { addNewService, diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 263fa53f0..56350d285 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -9,6 +9,7 @@ import { findEnvironmentById, findMySqlById, findProjectById, + getAccessibleServerIds, getContainerLogs, getServiceContainerCommand, IS_CLOUD, @@ -20,7 +21,6 @@ import { stopService, stopServiceRemote, updateMySqlById, - getAccessibleServerIds, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index 33d8fd3f4..0b263100d 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -9,6 +9,7 @@ import { findEnvironmentById, findPostgresById, findProjectById, + getAccessibleServerIds, getContainerLogs, getMountPath, getServiceContainerCommand, @@ -21,7 +22,6 @@ import { stopService, stopServiceRemote, updatePostgresById, - getAccessibleServerIds, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 5095c43f1..2e35aee2a 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -856,8 +856,6 @@ export const projectRouter = createTRPCRouter({ ctx.session.activeOrganizationId, ).then((value) => value.environment); - console.log("targetProject", targetProject); - if (input.includeServices) { const servicesToDuplicate = input.selectedServices || []; diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index a1e912e0b..a8664f664 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -8,6 +8,7 @@ import { findEnvironmentById, findProjectById, findRedisById, + getAccessibleServerIds, getContainerLogs, getServiceContainerCommand, IS_CLOUD, @@ -19,7 +20,6 @@ import { stopService, stopServiceRemote, updateRedisById, - getAccessibleServerIds, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 85f23ec0e..310363efd 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -5,6 +5,7 @@ import { findServerById, findServersByUserId, findUserById, + getAccessibleServerIds, getPublicIpWithFallback, haveActiveServices, IS_CLOUD, @@ -14,7 +15,6 @@ import { serverValidate, setupMonitoring, updateServerById, - getAccessibleServerIds, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { hasValidLicense } from "@dokploy/server/services/proprietary/license-key"; diff --git a/apps/dokploy/server/wss/listen-deployment.ts b/apps/dokploy/server/wss/listen-deployment.ts index cd9eefed6..d01c4d6d4 100644 --- a/apps/dokploy/server/wss/listen-deployment.ts +++ b/apps/dokploy/server/wss/listen-deployment.ts @@ -1,6 +1,7 @@ import { spawn } from "node:child_process"; import type http from "node:http"; import { findServerById, IS_CLOUD, validateRequest } from "@dokploy/server"; +import { encodeBase64 } from "@dokploy/server/utils/docker/utils"; import { readValidDirectory } from "@dokploy/server/wss/utils"; import { Client } from "ssh2"; import { WebSocketServer } from "ws"; @@ -70,9 +71,9 @@ export const setupDeploymentLogsWebSocketServer = ( sshClient = new Client(); sshClient .on("ready", () => { - const command = ` - tail -n +1 -f ${logPath}; - `; + const encodedPath = encodeBase64(logPath); + const command = `tail -n +1 -f "$(echo '${encodedPath}' | base64 -d)"`; + sshClient!.exec(command, (err, stream) => { if (err) { sshClient!.end(); diff --git a/apps/dokploy/templates/utils/index.ts b/apps/dokploy/templates/utils/index.ts index d7b967110..5dc0e15bc 100644 --- a/apps/dokploy/templates/utils/index.ts +++ b/apps/dokploy/templates/utils/index.ts @@ -29,7 +29,7 @@ export const generateRandomDomain = ({ const hash = randomBytes(3).toString("hex"); const slugIp = serverIp.replaceAll(".", "-"); - return `${projectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`; + return `${projectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.sslip.io`; }; export const generateHash = (projectName: string, quantity = 3): string => { diff --git a/apps/dokploy/utils/schema.ts b/apps/dokploy/utils/schema.ts index addbbc344..3d2c75188 100644 --- a/apps/dokploy/utils/schema.ts +++ b/apps/dokploy/utils/schema.ts @@ -28,7 +28,13 @@ export const uploadFileToContainerSchema = zfd.formData({ .min(1) .regex(/^[a-zA-Z0-9.\-_]+$/, "Invalid container ID"), file: zfd.file(), - destinationPath: z.string().min(1), + destinationPath: z + .string() + .min(1) + .regex( + /^[a-zA-Z0-9.\-_/]+$/, + "Invalid destination path: only alphanumeric characters, dots, dashes, underscores, and forward slashes are allowed", + ), serverId: z.string().optional(), }); diff --git a/package.json b/package.json index 8e0141a7b..6836e020d 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,16 @@ "overrides": { "esbuild": "0.20.2" }, + "peerDependencyRules": { + "ignoreMissing": [ + "prisma", + "@prisma/client", + "@prisma/engines", + "@electric-sql/pglite", + "typescript", + "drizzle-kit" + ] + }, "onlyBuiltDependencies": [ "@scarf/scarf", "@tree-sitter-grammars/tree-sitter-yaml", @@ -62,9 +72,7 @@ "sharp", "ssh2", "tree-sitter", - "tree-sitter-json", - "@prisma/engines", - "prisma" + "tree-sitter-json" ] } } diff --git a/packages/server/package.json b/packages/server/package.json index 1f519b494..d6ff03ad4 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -65,7 +65,7 @@ "micromatch": "4.0.8", "nanoid": "3.3.11", "node-os-utils": "2.0.1", - "node-pty": "1.0.0", + "node-pty": "1.1.0", "node-schedule": "2.1.1", "nodemailer": "6.9.14", "octokit": "3.1.2", diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index f440215ae..706a0dbec 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -83,11 +83,6 @@ const getDockerConfig = (): Docker => { export const docker = getDockerConfig(); -// When not set, use the legacy default so 2FA remains working for users who -// enabled it before BETTER_AUTH_SECRET was introduced. -export const BETTER_AUTH_SECRET = - process.env.BETTER_AUTH_SECRET || "better-auth-secret-123456789"; - export const paths = (isServer = false) => { const BASE_PATH = isServer || process.env.NODE_ENV === "production" diff --git a/packages/server/src/db/constants.ts b/packages/server/src/db/constants.ts index c4396726e..a8867c62c 100644 --- a/packages/server/src/db/constants.ts +++ b/packages/server/src/db/constants.ts @@ -9,7 +9,7 @@ export const { POSTGRES_PORT = "5432", } = process.env; -function readSecret(path: string): string { +export function readSecret(path: string): string { try { return fs.readFileSync(path, "utf8").trim(); } catch { diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index a7067f63f..59dfd3716 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -1,3 +1,4 @@ +import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; import { relations } from "drizzle-orm"; import { bigint, @@ -432,17 +433,22 @@ export const apiSaveBuildType = createSchema .required() .merge(createSchema.pick({ publishDirectory: true, isStaticSpa: true })); +const branchField = z + .string() + .min(1) + .regex(VALID_BRANCH_REGEX, "Invalid branch name"); + export const apiSaveGithubProvider = createSchema .pick({ applicationId: true, repository: true, - branch: true, owner: true, buildPath: true, githubId: true, }) .required() .extend({ + branch: branchField, triggerType: z.enum(["push", "tag"]).default("push"), }) .required() @@ -451,7 +457,6 @@ export const apiSaveGithubProvider = createSchema export const apiSaveGitlabProvider = createSchema .pick({ applicationId: true, - gitlabBranch: true, gitlabBuildPath: true, gitlabOwner: true, gitlabRepository: true, @@ -460,11 +465,11 @@ export const apiSaveGitlabProvider = createSchema gitlabPathNamespace: true, }) .required() + .extend({ gitlabBranch: branchField }) .merge(createSchema.pick({ enableSubmodules: true, watchPaths: true })); export const apiSaveBitbucketProvider = createSchema .pick({ - bitbucketBranch: true, bitbucketBuildPath: true, bitbucketOwner: true, bitbucketRepository: true, @@ -473,18 +478,19 @@ export const apiSaveBitbucketProvider = createSchema applicationId: true, }) .required() + .extend({ bitbucketBranch: branchField }) .merge(createSchema.pick({ enableSubmodules: true, watchPaths: true })); export const apiSaveGiteaProvider = createSchema .pick({ applicationId: true, - giteaBranch: true, giteaBuildPath: true, giteaOwner: true, giteaRepository: true, giteaId: true, }) .required() + .extend({ giteaBranch: branchField }) .merge(createSchema.pick({ enableSubmodules: true, watchPaths: true })); export const apiSaveDockerProvider = createSchema @@ -499,7 +505,6 @@ export const apiSaveDockerProvider = createSchema export const apiSaveGitProvider = createSchema .pick({ - customGitBranch: true, applicationId: true, customGitBuildPath: true, customGitUrl: true, @@ -507,6 +512,7 @@ export const apiSaveGitProvider = createSchema enableSubmodules: true, }) .required() + .extend({ customGitBranch: branchField }) .merge( createSchema.pick({ customGitSSHKeyId: true, diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index ee9ca662a..68db88f80 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -44,11 +44,22 @@ export const registryRelations = relations(registry, ({ many }) => ({ }), })); +// Registry URLs must be hostname[:port] only — no shell metacharacters +// Empty string is allowed (means default/Docker Hub registry) +const registryUrlSchema = z + .string() + .refine( + (val) => + val === "" || + /^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?(:\d{1,5})?$/.test(val), + "Registry URL must be a valid hostname or hostname:port (e.g. registry.example.com or localhost:5000)", + ); + const createSchema = createInsertSchema(registry, { registryName: z.string().min(1), username: z.string().min(1), password: z.string().min(1), - registryUrl: z.string(), + registryUrl: registryUrlSchema, organizationId: z.string().min(1), registryId: z.string().min(1), registryType: z.enum(["cloud"]), @@ -61,7 +72,7 @@ export const apiCreateRegistry = createSchema registryName: z.string().min(1), username: z.string().min(1), password: z.string().min(1), - registryUrl: z.string(), + registryUrl: registryUrlSchema, registryType: z.enum(["cloud"]), imagePrefix: z.string().nullable().optional(), }) @@ -74,7 +85,7 @@ export const apiTestRegistry = createSchema.pick({}).extend({ registryName: z.string().optional(), username: z.string().min(1), password: z.string().min(1), - registryUrl: z.string(), + registryUrl: registryUrlSchema, registryType: z.enum(["cloud"]), imagePrefix: z.string().nullable().optional(), serverId: z.string().optional(), diff --git a/packages/server/src/db/schema/schedule.ts b/packages/server/src/db/schema/schedule.ts index c0449a192..671233acc 100644 --- a/packages/server/src/db/schema/schedule.ts +++ b/packages/server/src/db/schema/schedule.ts @@ -24,6 +24,7 @@ export const schedules = pgTable("schedule", { .primaryKey() .$defaultFn(() => nanoid()), name: text("name").notNull(), + description: text("description"), cronExpression: text("cronExpression").notNull(), appName: text("appName") .notNull() diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 717c20246..bb113c73d 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -108,7 +108,7 @@ export * from "./utils/notifications/docker-cleanup"; export * from "./utils/notifications/dokploy-restart"; export * from "./utils/notifications/server-threshold"; export * from "./utils/notifications/utils"; -export * from "./verification/send-verification-email"; +export * from "./utils/git-branch-validation"; export * from "./utils/process/execAsync"; export * from "./utils/process/spawnAsync"; export * from "./utils/providers/bitbucket"; @@ -134,4 +134,5 @@ export * from "./utils/traefik/types"; export * from "./utils/traefik/web-server"; export * from "./utils/volume-backups/index"; export * from "./utils/watch-paths/should-deploy"; +export * from "./verification/send-verification-email"; export * from "./wss/utils"; diff --git a/packages/server/src/lib/auth-secret.ts b/packages/server/src/lib/auth-secret.ts new file mode 100644 index 000000000..8e709758f --- /dev/null +++ b/packages/server/src/lib/auth-secret.ts @@ -0,0 +1,28 @@ +import { readSecret } from "../db/constants"; + +const HARDCODED_LEGACY_SECRET = "better-auth-secret-123456789"; + +const { BETTER_AUTH_SECRET, BETTER_AUTH_SECRET_FILE } = process.env; + +function resolveBetterAuthSecret(): string { + if (BETTER_AUTH_SECRET) { + return BETTER_AUTH_SECRET; + } + if (BETTER_AUTH_SECRET_FILE) { + return readSecret(BETTER_AUTH_SECRET_FILE); + } + if (process.env.NODE_ENV !== "test") { + console.warn(` +⚠️ [DEPRECATED AUTH CONFIG] +BETTER_AUTH_SECRET is not set via environment variable or Docker secret. +Falling back to the insecure hardcoded default — this is a CRITICAL SECURITY RISK. +This mode WILL BE REMOVED in a future release. + +Please migrate to Docker Secrets: + curl -sSL https://dokploy.com/security/0.29.3.sh | bash +`); + } + return HARDCODED_LEGACY_SECRET; +} + +export const betterAuthSecret = resolveBetterAuthSecret(); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 069be48cc..c8dbf1807 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -7,7 +7,7 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { APIError } from "better-auth/api"; import { admin, organization, twoFactor } from "better-auth/plugins"; import { and, desc, eq } from "drizzle-orm"; -import { BETTER_AUTH_SECRET, IS_CLOUD } from "../constants"; +import { IS_CLOUD } from "../constants"; import { db } from "../db"; import * as schema from "../db/schema"; import { @@ -27,6 +27,7 @@ import { } from "../verification/send-verification-email"; import { getPublicIpWithFallback } from "../wss/utils"; import { ac, adminRole, memberRole, ownerRole } from "./access-control"; +import { betterAuthSecret } from "./auth-secret"; const { handler, api } = betterAuth({ database: drizzleAdapter(db, { @@ -38,8 +39,9 @@ const { handler, api } = betterAuth({ "/organization/create", "/organization/update", "/organization/delete", + ...(!IS_CLOUD ? ["/verify-email"] : []), ], - secret: BETTER_AUTH_SECRET, + secret: betterAuthSecret, ...(!IS_CLOUD ? { advanced: { diff --git a/packages/server/src/services/ai.ts b/packages/server/src/services/ai.ts index 6e90d82d0..61451b15e 100644 --- a/packages/server/src/services/ai.ts +++ b/packages/server/src/services/ai.ts @@ -229,7 +229,7 @@ export const suggestVariants = async ({ Domain Rules - For each service that needs to be exposed to the internet: 1. Define a domain with: - - host: {service-name}-{random-3-chars-hex}-${ip ? ip.replaceAll(".", "-") : ""}.traefik.me + - host: {service-name}-{random-3-chars-hex}-${ip ? ip.replaceAll(".", "-") : ""}.sslip.io - port: the internal port the service runs on - serviceName: the name of the service in the docker-compose 2. Make sure the service is properly configured to work with the specified port diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index e49adbb94..226230fbc 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -655,6 +655,8 @@ export const getAllContainerStats = async (serverId?: string) => { } }; +const destinationPathRegex = /^[a-zA-Z0-9.\-_/]+$/; + export const uploadFileToContainer = async ( containerId: string, fileBuffer: Buffer, @@ -667,7 +669,12 @@ export const uploadFileToContainer = async ( throw new Error("Invalid container ID"); } - // Ensure destination path starts with / + if (!destinationPathRegex.test(destinationPath)) { + throw new Error( + "Invalid destination path: shell metacharacters are not allowed", + ); + } + const normalizedPath = destinationPath.startsWith("/") ? destinationPath : `/${destinationPath}`; diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index 1556afbc4..20f64259b 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -136,7 +136,7 @@ export const createPreviewDeployment = async ( where: eq(organization.id, application.environment.project.organizationId), }); const generateDomain = await generateWildcardDomain( - application.previewWildcard || "*.traefik.me", + application.previewWildcard || "*.sslip.io", appName, application.server?.ipAddress || "", org?.ownerId || "", @@ -238,7 +238,7 @@ const generateWildcardDomain = async ( throw new Error('The base domain must start with "*."'); } const hash = `${appName}`; - if (baseDomain.includes("traefik.me")) { + if (baseDomain.includes("sslip.io")) { let ip = ""; if (process.env.NODE_ENV === "development") { diff --git a/packages/server/src/services/registry.ts b/packages/server/src/services/registry.ts index ad18e0b66..65ba80921 100644 --- a/packages/server/src/services/registry.ts +++ b/packages/server/src/services/registry.ts @@ -85,7 +85,7 @@ export const removeRegistry = async (registryId: string) => { } if (!IS_CLOUD) { - await execAsync(`docker logout ${response.registryUrl}`); + await execAsync(`docker logout ${shEscape(response.registryUrl)}`); } return response; diff --git a/packages/server/src/templates/github.ts b/packages/server/src/templates/github.ts index a935b2155..5b5c4ade8 100644 --- a/packages/server/src/templates/github.ts +++ b/packages/server/src/templates/github.ts @@ -21,6 +21,7 @@ export interface CompleteTemplate { [key: string]: string; }; config: { + isolated?: boolean; domains: Array<{ serviceName: string; port: number; @@ -55,25 +56,22 @@ interface TemplateMetadata { export async function fetchTemplatesList( baseUrl = "https://templates.dokploy.com", ): Promise { - try { - const response = await fetch(`${baseUrl}/meta.json`); - if (!response.ok) { - throw new Error(`Failed to fetch templates: ${response.statusText}`); - } - const templates = (await response.json()) as TemplateMetadata[]; - return templates.map((template) => ({ - id: template.id, - name: template.name, - description: template.description, - version: template.version, - logo: template.logo, - links: template.links, - tags: template.tags, - })); - } catch (error) { - console.error("Error fetching templates list:", error); - throw error; + const response = await fetch(`${baseUrl}/meta.json`, { + signal: AbortSignal.timeout(10000), + }); + if (!response.ok) { + throw new Error(`Failed to fetch templates: ${response.statusText}`); } + const templates = (await response.json()) as TemplateMetadata[]; + return templates.map((template) => ({ + id: template.id, + name: template.name, + description: template.description, + version: template.version, + logo: template.logo, + links: template.links, + tags: template.tags, + })); } /** @@ -83,27 +81,26 @@ export async function fetchTemplateFiles( templateId: string, baseUrl = "https://templates.dokploy.com", ): Promise<{ config: CompleteTemplate; dockerCompose: string }> { - try { - // Fetch both files in parallel - const [templateYmlResponse, dockerComposeResponse] = await Promise.all([ - fetch(`${baseUrl}/blueprints/${templateId}/template.toml`), - fetch(`${baseUrl}/blueprints/${templateId}/docker-compose.yml`), - ]); + const timeout = AbortSignal.timeout(10000); + const [templateYmlResponse, dockerComposeResponse] = await Promise.all([ + fetch(`${baseUrl}/blueprints/${templateId}/template.toml`, { + signal: timeout, + }), + fetch(`${baseUrl}/blueprints/${templateId}/docker-compose.yml`, { + signal: timeout, + }), + ]); - if (!templateYmlResponse.ok || !dockerComposeResponse.ok) { - throw new Error("Template files not found"); - } - - const [templateYml, dockerCompose] = await Promise.all([ - templateYmlResponse.text(), - dockerComposeResponse.text(), - ]); - - const config = parse(templateYml) as CompleteTemplate; - - return { config, dockerCompose }; - } catch (error) { - console.error(`Error fetching template ${templateId}:`, error); - throw error; + if (!templateYmlResponse.ok || !dockerComposeResponse.ok) { + throw new Error("Template files not found"); } + + const [templateYml, dockerCompose] = await Promise.all([ + templateYmlResponse.text(), + dockerComposeResponse.text(), + ]); + + const config = parse(templateYml) as CompleteTemplate; + + return { config, dockerCompose }; } diff --git a/packages/server/src/templates/index.ts b/packages/server/src/templates/index.ts index db67cb36f..772a95c64 100644 --- a/packages/server/src/templates/index.ts +++ b/packages/server/src/templates/index.ts @@ -38,15 +38,15 @@ export const generateRandomDomain = ({ const slugIp = serverIp.replaceAll(".", "-").replaceAll(":", "-"); // Domain labels have a max length of 63 characters - // Reserve space for: hash (6) + separators (1-2) + ip section + dot + traefik.me (10) - // Approx: 6 + 2 + (variable ip length) + 11 = ~19-30 chars for other parts + // Reserve space for: hash (6) + separators (1-2) + ip section + dot + sslip.io (8) + // Approx: 6 + 2 + (variable ip length) + 9 = ~19-30 chars for other parts const maxProjectNameLength = 40; const truncatedProjectName = projectName.length > maxProjectNameLength ? projectName.substring(0, maxProjectNameLength) : projectName; - return `${truncatedProjectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`; + return `${truncatedProjectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.sslip.io`; }; export const generateHash = (length = 8): string => { diff --git a/packages/server/src/templates/processors.ts b/packages/server/src/templates/processors.ts index ce1553095..bd170e104 100644 --- a/packages/server/src/templates/processors.ts +++ b/packages/server/src/templates/processors.ts @@ -45,6 +45,7 @@ export interface CompleteTemplate { }; variables: Record; config: { + isolated?: boolean; domains: DomainConfig[]; env: | Record diff --git a/packages/server/src/utils/backups/web-server.ts b/packages/server/src/utils/backups/web-server.ts index 19975cd6f..712cc0809 100644 --- a/packages/server/src/utils/backups/web-server.ts +++ b/packages/server/src/utils/backups/web-server.ts @@ -18,7 +18,7 @@ function formatBytes(bytes?: number) { if (bytes === 0) return "0 B"; const sizes = ["B", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(bytes) / Math.log(1024)); - const value = bytes / Math.pow(1024, i); + const value = bytes / 1024 ** i; return `${value.toFixed(2)} ${sizes[i]} (${bytes} bytes)`; } diff --git a/packages/server/src/utils/git-branch-validation.ts b/packages/server/src/utils/git-branch-validation.ts new file mode 100644 index 000000000..71451390d --- /dev/null +++ b/packages/server/src/utils/git-branch-validation.ts @@ -0,0 +1,3 @@ +// Valid git branch names per git-check-ref-format rules. +// Rejects shell metacharacters that would enable command injection. +export const VALID_BRANCH_REGEX = /^[a-zA-Z0-9._\-/]+$/; diff --git a/packages/server/src/utils/traefik/application.ts b/packages/server/src/utils/traefik/application.ts index c883ca44d..101574ed5 100644 --- a/packages/server/src/utils/traefik/application.ts +++ b/packages/server/src/utils/traefik/application.ts @@ -218,7 +218,11 @@ export const writeConfigRemote = async ( try { const { DYNAMIC_TRAEFIK_PATH } = paths(true); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); - await execAsyncRemote(serverId, `echo '${traefikConfig}' > ${configPath}`); + const encoded = encodeBase64(traefikConfig); + await execAsyncRemote( + serverId, + `echo "${encoded}" | base64 -d > "${configPath}"`, + ); } catch (e) { console.error("Error saving the YAML config file:", e); } diff --git a/packages/server/src/wss/utils.ts b/packages/server/src/wss/utils.ts index d54197ad7..0ea7485f9 100644 --- a/packages/server/src/wss/utils.ts +++ b/packages/server/src/wss/utils.ts @@ -40,6 +40,10 @@ export const readValidDirectory = ( directory: string, serverId?: string | null, ) => { + if (!/^[\w/. -]{1,500}$/.test(directory)) { + return false; + } + const { BASE_PATH } = paths(!!serverId); const resolvedBase = path.resolve(BASE_PATH); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 132d3b20e..55f10de34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,7 +51,7 @@ importers: version: 4.12.2 inngest: specifier: 3.40.1 - version: 3.40.1(h3@1.15.1)(hono@4.12.2)(next@16.2.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.9.3) + version: 3.40.1(encoding@0.1.13)(h3@1.15.1)(hono@4.12.2)(next@16.2.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.9.3) pino: specifier: 9.4.0 version: 9.4.0 @@ -115,10 +115,10 @@ importers: version: 2.0.30(zod@4.3.6) '@better-auth/api-key': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(71a760b327c31dd12606432855d01199)) '@better-auth/sso': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))(better-call@2.0.2(zod@4.3.6)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(71a760b327c31dd12606432855d01199))(better-call@2.0.2(zod@4.3.6)) '@codemirror/autocomplete': specifier: ^6.18.6 version: 6.20.0 @@ -277,10 +277,10 @@ importers: version: 3.7.1(ai@6.0.97(zod@4.3.6))(zod@4.3.6) bcrypt: specifier: 5.1.1 - version: 5.1.1 + version: 5.1.1(encoding@0.1.13) better-auth: specifier: 1.5.4 - version: 1.5.4(febde88eaf587188179e6ecc47119e50) + version: 1.5.4(71a760b327c31dd12606432855d01199) bl: specifier: 6.0.11 version: 6.0.11 @@ -354,8 +354,8 @@ importers: specifier: 2.0.1 version: 2.0.1 node-pty: - specifier: 1.0.0 - version: 1.0.0 + specifier: 1.1.0 + version: 1.1.0 node-schedule: specifier: 2.1.1 version: 2.1.1 @@ -630,10 +630,10 @@ importers: version: 2.0.30(zod@4.3.6) '@better-auth/api-key': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9)) '@better-auth/sso': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))(better-call@2.0.2(zod@4.3.6)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9))(better-call@2.0.2(zod@4.3.6)) '@better-auth/utils': specifier: 0.3.1 version: 0.3.1 @@ -669,10 +669,10 @@ importers: version: 3.7.1(ai@6.0.97(zod@4.3.6))(zod@4.3.6) bcrypt: specifier: 5.1.1 - version: 5.1.1 + version: 5.1.1(encoding@0.1.13) better-auth: specifier: 1.5.4 - version: 1.5.4(334901c35c1fcda64bb596793b2e4934) + version: 1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9) better-call: specifier: 2.0.2 version: 2.0.2(zod@4.3.6) @@ -713,8 +713,8 @@ importers: specifier: 2.0.1 version: 2.0.1 node-pty: - specifier: 1.0.0 - version: 1.0.0 + specifier: 1.1.0 + version: 1.1.0 node-schedule: specifier: 2.1.1 version: 2.1.1 @@ -775,7 +775,7 @@ importers: devDependencies: '@better-auth/cli': specifier: 1.4.21 - version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)) + version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0(socks@2.8.8))(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)) '@types/adm-zip': specifier: ^0.5.7 version: 0.5.7 @@ -3430,95 +3430,111 @@ packages: '@react-email/body@0.0.8': resolution: {integrity: sha512-gqdkNYlIaIw0OdpWu8KjIcQSIFvx7t2bZpXVxMMvBS859Ia1+1X3b5RNbjI3S1ZqLddUf7owOHkO4MiXGE+nxg==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/button@0.0.15': resolution: {integrity: sha512-9Zi6SO3E8PoHYDfcJTecImiHLyitYWmIRs0HE3Ogra60ZzlWP2EXu+AZqwQnhXuq+9pbgwBWNWxB5YPetNPTNA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/code-block@0.0.5': resolution: {integrity: sha512-mmInpZsSIkNaYC1y40/S0XXrIqbTzrpllP6J1JMJuDOBG8l5T7pNl4V+gwfsSTvy9hVsuzQFmhHK8kVb1UXv3A==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/code-inline@0.0.2': resolution: {integrity: sha512-0cmgbbibFeOJl0q04K9jJlPDuJ+SEiX/OG6m3Ko7UOkG3TqjRD8Dtvkij6jNDVfUh/zESpqJCP2CxrCLLMUjdA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/column@0.0.10': resolution: {integrity: sha512-MnP8Mnwipr0X3XtdD6jMLckb0sI5/IlS6Kl/2F6/rsSWBJy5Gg6nizlekTdkwDmy0kNSe3/1nGU0Zqo98pl63Q==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/components@0.0.21': resolution: {integrity: sha512-fwGfH7FF+iuq+IdPcbEO5HoF0Pakk9big+fFW9+3kiyvbSNuo8Io1rhPTMLd8q41XomN4g7mgWovdAeS/8PHrA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/container@0.0.12': resolution: {integrity: sha512-HFu8Pu5COPFfeZxSL+wKv/TV5uO/sp4zQ0XkRCdnGkj/xoq0lqOHVDL4yC2Pu6fxXF/9C3PHDA++5uEYV5WVJw==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/font@0.0.6': resolution: {integrity: sha512-sZZFvEZ4U3vNCAZ8wXqIO3DuGJR2qE/8m2fEH+tdqwa532zGO3zW+UlCTg0b9455wkJSzEBeaWik0IkNvjXzxw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/head@0.0.9': resolution: {integrity: sha512-dF3Uv1qy3oh+IU2atXdv5Xk0hk2udOlMb1A/MNGngC0eHyoEV9ThA0XvhN7mm5x9dDLkVamoWUKXDtmkiuSRqQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/heading@0.0.12': resolution: {integrity: sha512-eB7mpnAvDmwvQLoPuwEiPRH4fPXWe6ltz6Ptbry2BlI88F0a2k11Ghb4+sZHBqg7vVw/MKbqEgtLqr3QJ/KfCQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/hr@0.0.8': resolution: {integrity: sha512-JLVvpCg2wYKEB+n/PGCggWG9fRU5e4lxsGdpK5SDLsCL0ic3OLKSpHMfeE+ZSuw0GixAVVQN7F64PVJHQkd4MQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/html@0.0.8': resolution: {integrity: sha512-arII3wBNLpeJtwyIJXPaILm5BPKhA+nvdC1F9QkuKcOBJv2zXctn8XzPqyGqDfdplV692ulNJP7XY55YqbKp6w==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/img@0.0.8': resolution: {integrity: sha512-jx/rPuKo31tV18fu7P5rRqelaH5wkhg83Dq7uLwJpfqhbi4KFBGeBfD0Y3PiLPPoh+WvYf+Adv9W2ghNW8nOMQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/link@0.0.8': resolution: {integrity: sha512-nVikuTi8WJHa6Baad4VuRUbUCa/7EtZ1Qy73TRejaCHn+vhetc39XGqHzKLNh+Z/JFL8Hv9g+4AgG16o2R0ogQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/markdown@0.0.10': resolution: {integrity: sha512-MH0xO+NJ4IuJcx9nyxbgGKAMXyudFjCZ0A2GQvuWajemW9qy2hgnJ3mW3/z5lwcenG+JPn7JyO/iZpizQ7u1tA==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/preview@0.0.9': resolution: {integrity: sha512-2fyAA/zzZYfYmxfyn3p2YOIU30klyA6Dq4ytyWq4nfzQWWglt5hNDE0cMhObvRtfjM9ghMSVtoELAb0MWiF/kw==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 @@ -3532,24 +3548,28 @@ packages: '@react-email/row@0.0.8': resolution: {integrity: sha512-JsB6pxs/ZyjYpEML3nbwJRGAerjcN/Pa/QG48XUwnT/MioDWrUuyQuefw+CwCrSUZ2P1IDrv2tUD3/E3xzcoKw==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/section@0.0.12': resolution: {integrity: sha512-UCD/N/BeOTN4h3VZBUaFdiSem6HnpuxD1Q51TdBFnqeNqS5hBomp8LWJJ9s4gzwHWk1XPdNfLA3I/fJwulJshg==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/tailwind@0.0.18': resolution: {integrity: sha512-ob8CXX/Pqq1U8YfL5OJTL48WJkixizyoXMMRYTiDLDN9LVLU7lSLtcK9kOD9CgFbO2yUPQr7/5+7gnQJ+cXa8Q==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 '@react-email/text@0.0.8': resolution: {integrity: sha512-uvN2TNWMrfC9wv/LLmMLbbEN1GrMWZb9dBK14eYxHHAEHCeyvGb5ePZZ2MPyzO7Y5yTC+vFEnCEr76V+hWMxCQ==} engines: {node: '>=18.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: react: ^18.2.0 @@ -5347,6 +5367,9 @@ packages: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -5790,6 +5813,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} @@ -5836,6 +5863,7 @@ packages: inngest@3.40.1: resolution: {integrity: sha512-SC9Ly28i8NI+WymttE8Jk41L9r/wHXWOnlQoy7e7yoQyZI+R2C4S77DpFwzgEaqGT/H8puc1VDli84RoaffXBg==} engines: {node: '>=14'} + deprecated: 'CRITICAL SECURITY: upgrade to >=3.54.0' peerDependencies: '@sveltejs/kit': '>=1.27.3' '@vercel/node': '>=2.15.9' @@ -5890,6 +5918,10 @@ packages: resolution: {integrity: sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ==} engines: {node: '>=12.22.0'} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + ip-regex@5.0.0: resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6571,6 +6603,9 @@ packages: node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-addon-api@8.5.0: resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} engines: {node: ^18 || ^20 || >= 21} @@ -6615,8 +6650,8 @@ packages: resolution: {integrity: sha512-rH2N3qHZETLhdgTGhMMCE8zU3gsWO4we1MFtrSiAI7tYWrnJRc6dk2PseV4co3Lb0v/MbRONLQI2biHQYbpTpg==} engines: {node: '>=18.0.0'} - node-pty@1.0.0: - resolution: {integrity: sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==} + node-pty@1.1.0: + resolution: {integrity: sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==} node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -7557,6 +7592,14 @@ packages: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} engines: {node: '>=8.0.0'} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks@2.8.8: + resolution: {integrity: sha512-NlGELfPrgX2f1TAAcz0WawlLn+0r3FyhhCRpFFK2CemXenPYvzMWWZINv3eDNo9ucdwme7oCHRY0Jnbs4aIkog==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -8009,6 +8052,7 @@ packages: uuid@10.0.0: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@11.1.0: @@ -8017,10 +8061,12 @@ packages: uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true valibot@1.2.0: @@ -8618,21 +8664,21 @@ snapshots: '@balena/dockerignore@1.0.2': {} - '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))': + '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - better-auth: 1.5.4(334901c35c1fcda64bb596793b2e4934) + better-auth: 1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9) zod: 4.3.6 - '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))': + '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(71a760b327c31dd12606432855d01199))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - better-auth: 1.5.4(febde88eaf587188179e6ecc47119e50) + better-auth: 1.5.4(71a760b327c31dd12606432855d01199) zod: 4.3.6 - '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1))': + '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0(socks@2.8.8))(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1))': dependencies: '@babel/core': 7.29.0 '@babel/preset-react': 7.28.5(@babel/core@7.29.0) @@ -8644,7 +8690,7 @@ snapshots: '@mrleebo/prisma-ast': 0.13.1 '@prisma/client': 5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) '@types/pg': 8.16.0 - better-auth: 1.4.21(b1bc00b9e18c5e6af4e13b03fc4304ac) + better-auth: 1.4.21(97e31320bc7dc8a33b04861de973b388) better-sqlite3: 12.6.2 c12: 3.3.3 chalk: 5.6.2 @@ -8765,11 +8811,11 @@ snapshots: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - '@better-auth/mongo-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0)': + '@better-auth/mongo-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0(socks@2.8.8))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - mongodb: 7.1.0 + mongodb: 7.1.0(socks@2.8.8) '@better-auth/prisma-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))': dependencies: @@ -8778,24 +8824,24 @@ snapshots: '@prisma/client': 5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) prisma: 7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) - '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))(better-call@2.0.2(zod@4.3.6))': + '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9))(better-call@2.0.2(zod@4.3.6))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.4(334901c35c1fcda64bb596793b2e4934) + better-auth: 1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9) better-call: 2.0.2(zod@4.3.6) fast-xml-parser: 5.5.1 jose: 6.1.3 samlify: 2.10.2 zod: 4.3.6 - '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))(better-call@2.0.2(zod@4.3.6))': + '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(71a760b327c31dd12606432855d01199))(better-call@2.0.2(zod@4.3.6))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.4(febde88eaf587188179e6ecc47119e50) + better-auth: 1.5.4(71a760b327c31dd12606432855d01199) better-call: 2.0.2(zod@4.3.6) fast-xml-parser: 5.5.1 jose: 6.1.3 @@ -9422,12 +9468,12 @@ snapshots: '@lezer/highlight': 1.2.3 '@lezer/lr': 1.4.8 - '@mapbox/node-pre-gyp@1.0.11': + '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': dependencies: detect-libc: 2.1.2 https-proxy-agent: 5.0.1 make-dir: 3.1.0 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 @@ -9717,7 +9763,7 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@opentelemetry/auto-instrumentations-node@0.56.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/auto-instrumentations-node@0.56.1(@opentelemetry/api@1.9.0)(encoding@0.1.13)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) @@ -9764,7 +9810,7 @@ snapshots: '@opentelemetry/resource-detector-aws': 1.12.0(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-azure': 0.6.1(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-container': 0.6.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resource-detector-gcp': 0.33.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-gcp': 0.33.1(@opentelemetry/api@1.9.0)(encoding@0.1.13) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-node': 0.57.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: @@ -10301,13 +10347,13 @@ snapshots: '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/resource-detector-gcp@0.33.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/resource-detector-gcp@0.33.1(@opentelemetry/api@1.9.0)(encoding@0.1.13)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 - gcp-metadata: 6.1.1 + gcp-metadata: 6.1.1(encoding@0.1.13) transitivePeerDependencies: - encoding - supports-color @@ -12478,9 +12524,9 @@ snapshots: dependencies: tweetnacl: 0.14.5 - bcrypt@5.1.1: + bcrypt@5.1.1(encoding@0.1.13): dependencies: - '@mapbox/node-pre-gyp': 1.0.11 + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) node-addon-api: 5.1.0 transitivePeerDependencies: - encoding @@ -12488,7 +12534,7 @@ snapshots: before-after-hook@2.2.3: {} - better-auth@1.4.21(b1bc00b9e18c5e6af4e13b03fc4304ac): + better-auth@1.4.21(97e31320bc7dc8a33b04861de973b388): dependencies: '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)) @@ -12507,7 +12553,7 @@ snapshots: better-sqlite3: 12.6.2 drizzle-kit: 0.31.9 drizzle-orm: 0.41.0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) - mongodb: 7.1.0 + mongodb: 7.1.0(socks@2.8.8) mysql2: 3.15.3 next: 16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) pg: 8.18.0 @@ -12516,13 +12562,13 @@ snapshots: react-dom: 18.2.0(react@18.2.0) vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) - better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934): + better-auth@1.5.4(48b68ecaf84f5e14652b8d87fbbd7ca9): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))) '@better-auth/kysely-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11) '@better-auth/memory-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1) - '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0) + '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0(socks@2.8.8)) '@better-auth/prisma-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) '@better-auth/telemetry': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)) '@better-auth/utils': 0.3.1 @@ -12540,7 +12586,7 @@ snapshots: better-sqlite3: 12.6.2 drizzle-kit: 0.31.9 drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) - mongodb: 7.1.0 + mongodb: 7.1.0(socks@2.8.8) mysql2: 3.15.3 next: 16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) pg: 8.18.0 @@ -12551,13 +12597,13 @@ snapshots: transitivePeerDependencies: - '@cloudflare/workers-types' - better-auth@1.5.4(febde88eaf587188179e6ecc47119e50): + better-auth@1.5.4(71a760b327c31dd12606432855d01199): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))) '@better-auth/kysely-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11) '@better-auth/memory-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1) - '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0) + '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0(socks@2.8.8)) '@better-auth/prisma-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) '@better-auth/telemetry': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)) '@better-auth/utils': 0.3.1 @@ -12575,7 +12621,7 @@ snapshots: better-sqlite3: 12.6.2 drizzle-kit: 0.31.9 drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) - mongodb: 7.1.0 + mongodb: 7.1.0(socks@2.8.8) mysql2: 3.15.3 next: 16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) pg: 8.18.0 @@ -12979,9 +13025,9 @@ snapshots: dependencies: luxon: 3.7.2 - cross-fetch@4.1.0: + cross-fetch@4.1.0(encoding@0.1.13): dependencies: - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -13287,6 +13333,11 @@ snapshots: empathic@2.0.0: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -13504,20 +13555,20 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 - gaxios@6.7.1: + gaxios@6.7.1(encoding@0.1.13): dependencies: extend: 3.0.2 https-proxy-agent: 7.0.6 is-stream: 2.0.1 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) uuid: 9.0.1 transitivePeerDependencies: - encoding - supports-color - gcp-metadata@6.1.1: + gcp-metadata@6.1.1(encoding@0.1.13): dependencies: - gaxios: 6.7.1 + gaxios: 6.7.1(encoding@0.1.13) google-logging-utils: 0.0.2 json-bigint: 1.0.0 transitivePeerDependencies: @@ -13791,6 +13842,11 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -13825,13 +13881,13 @@ snapshots: inline-style-parser@0.2.7: {} - inngest@3.40.1(h3@1.15.1)(hono@4.12.2)(next@16.2.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.9.3): + inngest@3.40.1(encoding@0.1.13)(h3@1.15.1)(hono@4.12.2)(next@16.2.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(typescript@5.9.3): dependencies: '@bufbuild/protobuf': 2.11.0 '@inngest/ai': 0.1.7 '@jpwilliams/waitgroup': 2.1.1 '@opentelemetry/api': 1.9.0 - '@opentelemetry/auto-instrumentations-node': 0.56.1(@opentelemetry/api@1.9.0) + '@opentelemetry/auto-instrumentations-node': 0.56.1(@opentelemetry/api@1.9.0)(encoding@0.1.13) '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-http': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) @@ -13840,7 +13896,7 @@ snapshots: '@types/debug': 4.1.12 canonicalize: 1.0.8 chalk: 4.1.2 - cross-fetch: 4.1.0 + cross-fetch: 4.1.0(encoding@0.1.13) debug: 4.4.3 hash.js: 1.1.7 json-stringify-safe: 5.0.1 @@ -13897,6 +13953,9 @@ snapshots: transitivePeerDependencies: - supports-color + ip-address@10.2.0: + optional: true + ip-regex@5.0.0: {} iron-webcrypto@1.2.1: {} @@ -14582,11 +14641,13 @@ snapshots: '@types/whatwg-url': 13.0.0 whatwg-url: 14.2.0 - mongodb@7.1.0: + mongodb@7.1.0(socks@2.8.8): dependencies: '@mongodb-js/saslprep': 1.4.6 bson: 7.2.0 mongodb-connection-string-url: 7.0.1 + optionalDependencies: + socks: 2.8.8 ms@2.1.3: {} @@ -14630,7 +14691,8 @@ snapshots: dependencies: lru.min: 1.1.4 - nan@2.25.0: {} + nan@2.25.0: + optional: true nanoid@3.3.11: {} @@ -14689,6 +14751,8 @@ snapshots: node-addon-api@5.1.0: {} + node-addon-api@7.1.1: {} + node-addon-api@8.5.0: optional: true @@ -14701,9 +14765,11 @@ snapshots: node-fetch-native@1.6.7: {} - node-fetch@2.7.0: + node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 node-forge@1.3.3: {} @@ -14719,9 +14785,9 @@ snapshots: node-os-utils@2.0.1: {} - node-pty@1.0.0: + node-pty@1.1.0: dependencies: - nan: 2.25.0 + node-addon-api: 7.1.1 node-releases@2.0.27: {} @@ -15750,6 +15816,15 @@ snapshots: slugify@1.6.6: {} + smart-buffer@4.2.0: + optional: true + + socks@2.8.8: + dependencies: + ip-address: 10.2.0 + smart-buffer: 4.2.0 + optional: true + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0