From 5430e5687c2191eba1d9b2fe9f7fde4d757b1133 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 23 Sep 2026 21:22:43 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Show=20popup=20for=20inval?= =?UTF-8?q?id=20redirect=20resource=20domain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- messages/en-US.json | 3 +- server/routers/redirect/getRedirect.ts | 4 +- .../settings/redirects/[niceId]/page.tsx | 1 + src/components/RedirectForm.tsx | 59 ++++++++++++++----- src/components/RedirectsTable.tsx | 4 +- src/components/resource-selector.tsx | 8 ++- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index d3b3079ea..39327ae7b 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -2580,7 +2580,8 @@ "billingModifyLicenses": "Modify License Subscription", "domainNotFound": "Domain Not Found", "domainNotFoundDescription": "This resource is disabled because the domain no longer exists our system. Please set a new domain for this resource.", - "rdirectDomainNotFoundDescription": "This redirect is disabled because the domain no longer exists our system. Please set a new domain for this redirect or its attached resource domain.", + "redirectDomainNotFoundDescription": "This redirect is disabled because the domain no longer exists our system. Please set a new domain for this redirect or its attached resource domain.", + "redirectDomainSelectedDomainNotFound": "This redirect is disabled because its domain no longer exists. Select a different domain, or attach the redirect to a resource that has a valid domain.", "failed": "Failed", "createNewOrgDescription": "Create a new organization", "organization": "Organization", diff --git a/server/routers/redirect/getRedirect.ts b/server/routers/redirect/getRedirect.ts index f2eae2961..a2a6c4a4f 100644 --- a/server/routers/redirect/getRedirect.ts +++ b/server/routers/redirect/getRedirect.ts @@ -29,6 +29,7 @@ export type GetRedirectResponse = { resourceId: number | null; resourceName: string | null; resourceNiceId: string | null; + resourceDomainId: string | null; resourceFullDomain: string | null; resourceSsl: boolean | null; resourceWildcard: boolean | null; @@ -59,7 +60,8 @@ const redirectColumns = { resourceSsl: resources.ssl, resourceWildcard: resources.wildcard, domainId: redirects.domainId, - baseDomain: domains.baseDomain + baseDomain: domains.baseDomain, + resourceDomainId: resources.domainId }; const paramsSchema = z.strictObject({ diff --git a/src/app/[orgId]/settings/redirects/[niceId]/page.tsx b/src/app/[orgId]/settings/redirects/[niceId]/page.tsx index 8ceb1fc9e..7a0f11f21 100644 --- a/src/app/[orgId]/settings/redirects/[niceId]/page.tsx +++ b/src/app/[orgId]/settings/redirects/[niceId]/page.tsx @@ -40,6 +40,7 @@ export default async function EditRedirectPage(props: EditRedirectPageProps) { const initialResource = redirect.resourceId && redirect.resourceNiceId ? { + domainId: redirect.resourceDomainId, resourceId: redirect.resourceId, niceId: redirect.resourceNiceId, name: redirect.resourceName ?? redirect.resourceNiceId, diff --git a/src/components/RedirectForm.tsx b/src/components/RedirectForm.tsx index c9932410c..f57e6b9c3 100644 --- a/src/components/RedirectForm.tsx +++ b/src/components/RedirectForm.tsx @@ -1,6 +1,13 @@ "use client"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; +import DomainPicker from "@app/components/DomainPicker"; +import { + PathMatchDisplay, + PathMatchModal, + PathRewriteDisplay, + PathRewriteModal +} from "@app/components/PathMatchRenameModal"; import { SettingsContainer, SettingsFormCell, @@ -40,34 +47,34 @@ import { import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; -import { - isValidDestinationHost, - isValidRegex -} from "@server/routers/redirect/validation"; -import { build } from "@server/build"; import { cn } from "@app/lib/cn"; -import { CaretSortIcon } from "@radix-ui/react-icons"; import { zodResolver } from "@hookform/resolvers/zod"; +import { CaretSortIcon } from "@radix-ui/react-icons"; +import { build } from "@server/build"; import type { CreateRedirectResponse, GetRedirectResponse } from "@server/routers/redirect"; +import { + isValidDestinationHost, + isValidRegex +} from "@server/routers/redirect/validation"; import type { AxiosResponse } from "axios"; +import { InfoIcon, Plus } from "lucide-react"; import { useTranslations } from "next-intl"; +import Link from "next/link"; import { useRouter } from "next/navigation"; import { useActionState, useEffect, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { ResourceSelector, type SelectedResource } from "./resource-selector"; import { - PathMatchDisplay, - PathMatchModal, - PathRewriteDisplay, - PathRewriteModal -} from "@app/components/PathMatchRenameModal"; -import { Plus } from "lucide-react"; -import DomainPicker from "@app/components/DomainPicker"; -import Link from "next/link"; + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "./ui/tooltip"; +import { Alert, AlertDescription } from "./ui/alert"; const DEFAULT_PATH_MATCH_TYPE = "regex" as const; const DEFAULT_PRIORITY = 100; @@ -213,6 +220,11 @@ export default function RedirectForm({ const attachTo = form.watch("attachTo"); const ssl = form.watch("ssl"); + const resourceMissingDomain = + attachTo === "resource" && + Boolean(selectedResource) && + !selectedResource?.domainId; + const sourceFullDomain = attachTo === "domain" ? domainFullDomain @@ -364,7 +376,20 @@ export default function RedirectForm({
- + + {resourceMissingDomain && ( + + + + {t( + "redirectDomainSelectedDomainNotFound" + )} + + + )} diff --git a/src/components/resource-selector.tsx b/src/components/resource-selector.tsx index 5dad2bf0e..5a4704e44 100644 --- a/src/components/resource-selector.tsx +++ b/src/components/resource-selector.tsx @@ -17,7 +17,13 @@ import { useDebounce } from "use-debounce"; export type SelectedResource = Pick< ListResourcesResponse["resources"][number], - "name" | "resourceId" | "fullDomain" | "niceId" | "ssl" | "wildcard" + | "name" + | "resourceId" + | "fullDomain" + | "niceId" + | "ssl" + | "wildcard" + | "domainId" >; export type ResourceSelectorProps = {