♻️ Show popup for invalid redirect resource domain

This commit is contained in:
Fred KISSIE
2026-09-23 21:22:43 +02:00
parent 0b0f62d8de
commit 5430e5687c
6 changed files with 60 additions and 19 deletions
+2 -1
View File
@@ -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",
+3 -1
View File
@@ -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({
@@ -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,
+44 -15
View File
@@ -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({
<Form {...form}>
<form action={formAction} id="redirect-form">
<SettingsFormGrid>
<SettingsFormCell span="full">
<SettingsFormCell
span="full"
className="flex flex-col items-start gap-4"
>
{resourceMissingDomain && (
<Alert variant="neutral">
<InfoIcon className="h-4 w-4" />
<AlertDescription>
{t(
"redirectDomainSelectedDomainNotFound"
)}
</AlertDescription>
</Alert>
)}
<FormField
control={form.control}
name="enabled"
@@ -380,8 +405,12 @@ export default function RedirectForm({
"redirectEnabledDescription"
)}
checked={
!resourceMissingDomain &&
field.value
}
disabled={
resourceMissingDomain
}
onCheckedChange={
field.onChange
}
+3 -1
View File
@@ -269,7 +269,9 @@ export default function RedirectsTable({
return (
<div className="flex items-center gap-2 min-w-0">
<InfoPopup
info={t("rdirectDomainNotFoundDescription")}
info={t(
"redirectDomainNotFoundDescription"
)}
text={t("domainNotFound")}
/>
</div>
+7 -1
View File
@@ -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 = {