diff --git a/messages/en-US.json b/messages/en-US.json index f7be36b6..83ad9a06 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1860,5 +1860,38 @@ }, "priority": "Priority", "priorityDescription": "Higher priority routes are evaluated first. Priority = 100 means automatic ordering (system decides). Use another number to enforce manual priority.", - "instanceName": "Instance Name" + "instanceName": "Instance Name", + "pathMatchModalTitle": "Configure Path Matching", + "pathMatchModalDescription": "Set up how incoming requests should be matched based on their path.", + "pathMatchType": "Match Type", + "pathMatchPrefix": "Prefix", + "pathMatchExact": "Exact", + "pathMatchRegex": "Regex", + "pathMatchValue": "Path Value", + "clear": "Clear", + "saveChanges": "Save Changes", + "pathMatchRegexPlaceholder": "^/api/.*", + "pathMatchDefaultPlaceholder": "/path", + "pathMatchPrefixHelp": "Example: /api matches /api, /api/users, etc.", + "pathMatchExactHelp": "Example: /api matches only /api", + "pathMatchRegexHelp": "Example: ^/api/.* matches /api/anything", + "pathRewriteModalTitle": "Configure Path Rewriting", + "pathRewriteModalDescription": "Transform the matched path before forwarding to the target.", + "pathRewriteType": "Rewrite Type", + "pathRewritePrefixOption": "Prefix - Replace prefix", + "pathRewriteExactOption": "Exact - Replace entire path", + "pathRewriteRegexOption": "Regex - Pattern replacement", + "pathRewriteStripPrefixOption": "Strip Prefix - Remove prefix", + "pathRewriteValue": "Rewrite Value", + "pathRewriteRegexPlaceholder": "/new/$1", + "pathRewriteDefaultPlaceholder": "/new-path", + "pathRewritePrefixHelp": "Replace the matched prefix with this value", + "pathRewriteExactHelp": "Replace the entire path with this value when the path matches exactly", + "pathRewriteRegexHelp": "Use capture groups like $1, $2 for replacement", + "pathRewriteStripPrefixHelp": "Leave empty to strip prefix or provide new prefix", + "pathRewritePrefix": "Prefix", + "pathRewriteExact": "Exact", + "pathRewriteRegex": "Regex", + "pathRewriteStrip": "Strip", + "pathRewriteStripLabel": "strip" } diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index f91e1857..4055c7a0 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -164,9 +164,6 @@ export async function getTraefikConfig( port: row.port, internalPort: row.internalPort, enabled: row.targetEnabled, - rewritePath: row.rewritePath, - rewritePathType: row.rewritePathType, - priority: row.priority, site: { siteId: row.siteId, type: row.siteType, @@ -268,8 +265,8 @@ export async function getTraefikConfig( // Handle path rewriting middleware if ( - resource.rewritePath && - resource.path && + resource.rewritePath !== null && + resource.path !== null && resource.pathMatchType && resource.rewritePathType ) { diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index acf6c705..48161a47 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -203,9 +203,6 @@ export async function getTraefikConfig( port: row.port, internalPort: row.internalPort, enabled: row.targetEnabled, - rewritePath: row.rewritePath, - rewritePathType: row.rewritePathType, - priority: row.priority, site: { siteId: row.siteId, type: row.siteType, @@ -330,8 +327,8 @@ export async function getTraefikConfig( // Handle path rewriting middleware if ( - resource.rewritePath && - resource.path && + resource.rewritePath !== null && + resource.path !== null && resource.pathMatchType && resource.rewritePathType ) { diff --git a/src/components/PathMatchRenameModal.tsx b/src/components/PathMatchRenameModal.tsx index b06d776b..357f2cf9 100644 --- a/src/components/PathMatchRenameModal.tsx +++ b/src/components/PathMatchRenameModal.tsx @@ -1,10 +1,10 @@ import { Settings } from "lucide-react"; import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue } from "@/components/ui/select"; import { Badge } from "@app/components/ui/badge"; @@ -12,275 +12,324 @@ import { Label } from "@app/components/ui/label"; import { useEffect, useState } from "react"; import { Input } from "./ui/input"; import { Button } from "./ui/button"; -import { Credenza, CredenzaContent, CredenzaDescription, CredenzaFooter, CredenzaHeader, CredenzaTitle, CredenzaTrigger } from "./Credenza"; - +import { + Credenza, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle, + CredenzaTrigger +} from "./Credenza"; +import { useTranslations } from "next-intl"; export function PathMatchModal({ - value, - onChange, - trigger, + value, + onChange, + trigger }: { - value: { path: string | null; pathMatchType: string | null }; - onChange: (config: { path: string | null; pathMatchType: string | null }) => void; - trigger: React.ReactNode; + value: { path: string | null; pathMatchType: string | null }; + onChange: (config: { + path: string | null; + pathMatchType: string | null; + }) => void; + trigger: React.ReactNode; }) { - const [open, setOpen] = useState(false); - const [matchType, setMatchType] = useState(value?.pathMatchType || "prefix"); - const [path, setPath] = useState(value?.path || ""); + const t = useTranslations(); - useEffect(() => { - if (open) { - setMatchType(value?.pathMatchType || "prefix"); - setPath(value?.path || ""); - } - }, [open, value]); + const [open, setOpen] = useState(false); + const [matchType, setMatchType] = useState( + value?.pathMatchType || "prefix" + ); + const [path, setPath] = useState(value?.path || ""); - const handleSave = () => { - onChange({ pathMatchType: matchType as any, path: path.trim() }); - setOpen(false); - }; + useEffect(() => { + if (open) { + setMatchType(value?.pathMatchType || "prefix"); + setPath(value?.path || ""); + } + }, [open, value]); - const handleClear = () => { - onChange({ pathMatchType: null, path: null }); - setOpen(false); - }; + const handleSave = () => { + onChange({ pathMatchType: matchType as any, path: path.trim() }); + setOpen(false); + }; - const getPlaceholder = () => (matchType === "regex" ? "^/api/.*" : "/path"); + const handleClear = () => { + onChange({ pathMatchType: null, path: null }); + setOpen(false); + }; - const getHelpText = () => { - switch (matchType) { - case "prefix": - return "Example: /api matches /api, /api/users, etc."; - case "exact": - return "Example: /api matches only /api"; - case "regex": - return "Example: ^/api/.* matches /api/anything"; - default: - return ""; - } - }; + const getPlaceholder = () => (matchType === "regex" ? t("pathMatchRegexPlaceholder") : t("pathMatchDefaultPlaceholder")); - return ( - - {trigger} - - - Configure Path Matching - - Set up how incoming requests should be matched based on their path. - - -
-
- - -
-
- - setPath(e.target.value)} - /> -

{getHelpText()}

-
-
- - {value?.path && ( - - )} - - -
-
- ); + const getHelpText = () => { + switch (matchType) { + case "prefix": + return t("pathMatchPrefixHelp"); + case "exact": + return t("pathMatchExactHelp"); + case "regex": + return t("pathMatchRegexHelp"); + default: + return ""; + } + }; + + return ( + + {trigger} + + + {t("pathMatchModalTitle")} + + {t("pathMatchModalDescription")} + + +
+
+ + +
+
+ + setPath(e.target.value)} + /> +

+ {getHelpText()} +

+
+
+ + {value?.path && ( + + )} + + +
+
+ ); } - export function PathRewriteModal({ - value, - onChange, - trigger, - disabled, + value, + onChange, + trigger, + disabled }: { - value: { rewritePath: string | null; rewritePathType: string | null }; - onChange: (config: { rewritePath: string | null; rewritePathType: string | null }) => void; - trigger: React.ReactNode; - disabled?: boolean; + value: { rewritePath: string | null; rewritePathType: string | null }; + onChange: (config: { + rewritePath: string | null; + rewritePathType: string | null; + }) => void; + trigger: React.ReactNode; + disabled?: boolean; }) { - const [open, setOpen] = useState(false); - const [rewriteType, setRewriteType] = useState(value?.rewritePathType || "prefix"); - const [rewritePath, setRewritePath] = useState(value?.rewritePath || ""); + const t = useTranslations(); + const [open, setOpen] = useState(false); + const [rewriteType, setRewriteType] = useState( + value?.rewritePathType || "prefix" + ); + const [rewritePath, setRewritePath] = useState(value?.rewritePath || ""); - useEffect(() => { - if (open) { - setRewriteType(value?.rewritePathType || "prefix"); - setRewritePath(value?.rewritePath || ""); - } - }, [open, value]); + useEffect(() => { + if (open) { + setRewriteType(value?.rewritePathType || "prefix"); + setRewritePath(value?.rewritePath || ""); + } + }, [open, value]); - const handleSave = () => { - onChange({ rewritePathType: rewriteType as any, rewritePath: rewritePath.trim() }); - setOpen(false); - }; + const handleSave = () => { + onChange({ + rewritePathType: rewriteType as any, + rewritePath: rewritePath.trim() + }); + setOpen(false); + }; - const handleClear = () => { - onChange({ rewritePathType: null, rewritePath: null }); - setOpen(false); - }; + const handleClear = () => { + onChange({ rewritePathType: null, rewritePath: null }); + setOpen(false); + }; - const getPlaceholder = () => { - switch (rewriteType) { - case "regex": - return "/new/$1"; - case "stripPrefix": - return ""; - default: - return "/new-path"; - } - }; + const getPlaceholder = () => { + switch (rewriteType) { + case "regex": + return t("pathRewriteRegexPlaceholder"); + case "stripPrefix": + return ""; + default: + return t("pathRewriteDefaultPlaceholder"); + } + }; - const getHelpText = () => { - switch (rewriteType) { - case "prefix": - return "Replace the matched prefix with this value"; - case "exact": - return "Replace the entire path with this value"; - case "regex": - return "Use capture groups like $1, $2 for replacement"; - case "stripPrefix": - return "Leave empty to strip prefix or provide new prefix"; - default: - return ""; - } - }; + const getHelpText = () => { + switch (rewriteType) { + case "prefix": + return t("pathRewritePrefixHelp"); + case "exact": + return t("pathRewriteExactHelp"); + case "regex": + return t("pathRewriteRegexHelp"); + case "stripPrefix": + return t("pathRewriteStripPrefixHelp"); + default: + return ""; + } + }; - return ( - !disabled && setOpen(v)}> - - {trigger} - - - - Configure Path Rewriting - - Transform the matched path before forwarding to the target. - - -
-
- - -
-
- - setRewritePath(e.target.value)} - /> -

{getHelpText()}

-
-
- - {value?.rewritePath && ( - - )} - - -
-
- ); + return ( + !disabled && setOpen(v)}> + {trigger} + + + {t("pathRewriteModalTitle")} + + {t("pathRewriteModalDescription")} + + +
+
+ + +
+
+ + setRewritePath(e.target.value)} + /> +

+ {getHelpText()} +

+
+
+ + {value?.rewritePath && ( + + )} + + +
+
+ ); } export function PathMatchDisplay({ - value, + value }: { - value: { path: string | null; pathMatchType: string | null }; + value: { path: string | null; pathMatchType: string | null }; }) { - if (!value?.path) return null; + const t = useTranslations(); + + if (!value?.path) return null; - const getTypeLabel = (type: string | null) => { - const labels: Record = { - prefix: "Prefix", - exact: "Exact", - regex: "Regex", + const getTypeLabel = (type: string | null) => { + const labels: Record = { + prefix: t("pathMatchPrefix"), + exact: t("pathMatchExact"), + regex: t("pathMatchRegex") + }; + return labels[type || ""] || type; }; - return labels[type || ""] || type; - }; - return ( -
- - {getTypeLabel(value.pathMatchType)} - - - {value.path} - - -
- ); + return ( +
+ + {getTypeLabel(value.pathMatchType)} + + + {value.path} + + +
+ ); } - export function PathRewriteDisplay({ - value, + value }: { - value: { rewritePath: string | null; rewritePathType: string | null }; + value: { rewritePath: string | null; rewritePathType: string | null }; }) { - if (!value?.rewritePath && value?.rewritePathType !== "stripPrefix") return null; + const t = useTranslations(); + + if (!value?.rewritePath && value?.rewritePathType !== "stripPrefix") + return null; - const getTypeLabel = (type: string | null) => { - const labels: Record = { - prefix: "Prefix", - exact: "Exact", - regex: "Regex", - stripPrefix: "Strip", + const getTypeLabel = (type: string | null) => { + const labels: Record = { + prefix: t("pathRewritePrefix"), + exact: t("pathRewriteExact"), + regex: t("pathRewriteRegex"), + stripPrefix: t("pathRewriteStrip") + }; + return labels[type || ""] || type; }; - return labels[type || ""] || type; - }; - return ( -
- - {getTypeLabel(value.rewritePathType)} - - - {value.rewritePath || (strip)} - - -
- ); + return ( +
+ + {getTypeLabel(value.rewritePathType)} + + + {value.rewritePath || ( + + ({t("pathRewriteStripLabel")}) + + )} + + +
+ ); }