mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
Merge pull request #3684 from Dokploy/fix/add-punycode
feat(traefik): add support for internationalized domain names (IDN)
This commit is contained in:
@@ -275,3 +275,51 @@ 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`)");
|
||||
});
|
||||
|
||||
test("Russian Cyrillic label with .ru TLD is converted to punycode", async () => {
|
||||
const router = await createRouterConfig(
|
||||
baseApp,
|
||||
{ ...baseDomain, host: "сайт.ru" },
|
||||
"web",
|
||||
);
|
||||
|
||||
// сайт in punycode is xn--80aswg
|
||||
expect(router.rule).toContain("Host(`xn--80aswg.ru`)");
|
||||
expect(router.rule).not.toContain("сайт");
|
||||
});
|
||||
|
||||
test("Subdomain with Russian IDN TLD converts non-ASCII part to punycode", async () => {
|
||||
const router = await createRouterConfig(
|
||||
baseApp,
|
||||
{ ...baseDomain, host: "app.тест.рф" },
|
||||
"web",
|
||||
);
|
||||
|
||||
// app stays ASCII, тест.рф becomes xn--e1aybc.xn--p1ai
|
||||
expect(router.rule).toContain("Host(`app.xn--e1aybc.xn--p1ai`)");
|
||||
expect(router.rule).not.toContain("тест.рф");
|
||||
});
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user