feat(traefik): add support for internationalized domain names (IDN)

- Implemented a function to convert IDNs to ASCII punycode format, ensuring compatibility with Traefik requirements.
- Added tests to verify the conversion of IDNs and the handling of ASCII domains in router configurations.
This commit is contained in:
Mauricio Siu
2026-02-10 22:42:44 -06:00
parent 5c36ca3986
commit 7184b7d4b2
2 changed files with 40 additions and 1 deletions

View File

@@ -275,3 +275,27 @@ test("CertificateType on websecure entrypoint", async () => {
expect(router.tls?.certResolver).toBe("letsencrypt");
});
/** IDN/Punycode */
test("Internationalized domain name is converted to punycode", async () => {
const router = await createRouterConfig(
baseApp,
{ ...baseDomain, host: "тест.рф" },
"web",
);
// тест.рф in punycode is xn--e1aybc.xn--p1ai
expect(router.rule).toContain("Host(`xn--e1aybc.xn--p1ai`)");
expect(router.rule).not.toContain("тест.рф");
});
test("ASCII domain remains unchanged", async () => {
const router = await createRouterConfig(
baseApp,
{ ...baseDomain, host: "example.com" },
"web",
);
expect(router.rule).toContain("Host(`example.com`)");
});

View File

@@ -104,6 +104,20 @@ export const removeDomain = async (
}
};
/**
* Converts an internationalized domain name (IDN) to ASCII punycode format.
* Traefik requires domain names in ASCII format, so non-ASCII characters
* must be converted (e.g., "тест.рф" → "xn--e1aybc.xn--p1ai").
*/
const toPunycode = (host: string): string => {
try {
return new URL(`http://${host}`).hostname;
} catch {
// If URL parsing fails, return the original host
return host;
}
};
export const createRouterConfig = async (
app: ApplicationNested,
domain: Domain,
@@ -114,8 +128,9 @@ export const createRouterConfig = async (
const { host, path, https, uniqueConfigKey, internalPath, stripPath } =
domain;
const punycodeHost = toPunycode(host);
const routerConfig: HttpRouter = {
rule: `Host(\`${host}\`)${path !== null && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`,
rule: `Host(\`${punycodeHost}\`)${path !== null && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`,
service: `${appName}-service-${uniqueConfigKey}`,
middlewares: [],
entryPoints: [entryPoint],