fix: add parser Public Suffix List for naming domain file config and fix failed to remove old legacy file config
This commit is contained in:
1 parent
af92445c6c
commit
17cbc2fb98
5 files changed
+148
-1
No files matched your search
Generated
+19
@@ -45,6 +45,7 @@
|
||||
"react-dom": "19.2.4",
|
||||
"recharts": "^3.8.1",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tldts": "^7.4.2",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -11834,6 +11835,24 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/tldts": {
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.2.tgz",
|
||||
"integrity": "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tldts-core": "^7.4.2"
|
||||
},
|
||||
"bin": {
|
||||
"tldts": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tldts-core": {
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.2.tgz",
|
||||
"integrity": "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"react-dom": "19.2.4",
|
||||
"recharts": "^3.8.1",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tldts": "^7.4.2",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getDomain } from "tldts";
|
||||
|
||||
export function sanitizeDomainFileName(domainName: string): string {
|
||||
return domainName.toLowerCase().replace(/[^a-z0-9.-]/g, "-");
|
||||
}
|
||||
@@ -18,7 +20,27 @@ function normalizeAnchorDomain(domainName: string): string {
|
||||
return domainName.trim().toLowerCase().replace(/^\*\./, "");
|
||||
}
|
||||
|
||||
export function getDomainConfigAnchor(domainNames: string[]): string {
|
||||
function resolveRegistrableDomain(domainName: string): string {
|
||||
const parsed = getDomain(domainName, {
|
||||
allowIcannDomains: true,
|
||||
allowPrivateDomains: true,
|
||||
});
|
||||
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
const labels = domainName.split(".").filter(Boolean);
|
||||
if (labels.length >= 2) {
|
||||
return labels.slice(-2).join(".");
|
||||
}
|
||||
|
||||
return domainName;
|
||||
}
|
||||
|
||||
export function getLegacyTwoLabelDomainConfigAnchor(
|
||||
domainNames: string[],
|
||||
): string {
|
||||
const normalized = Array.from(
|
||||
new Set(domainNames.map(normalizeAnchorDomain).filter(Boolean)),
|
||||
);
|
||||
@@ -48,6 +70,30 @@ export function getDomainConfigAnchor(domainNames: string[]): string {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
export function getDomainConfigAnchor(domainNames: string[]): string {
|
||||
const normalized = Array.from(
|
||||
new Set(domainNames.map(normalizeAnchorDomain).filter(Boolean)),
|
||||
);
|
||||
|
||||
if (normalized.length === 0) {
|
||||
return "shared-domain";
|
||||
}
|
||||
|
||||
normalized.sort((left, right) => {
|
||||
const leftLabels = left.split(".").length;
|
||||
const rightLabels = right.split(".").length;
|
||||
|
||||
if (leftLabels !== rightLabels) {
|
||||
return leftLabels - rightLabels;
|
||||
}
|
||||
|
||||
return left.localeCompare(right);
|
||||
});
|
||||
|
||||
const candidate = normalized[0] ?? "shared-domain";
|
||||
return resolveRegistrableDomain(candidate);
|
||||
}
|
||||
|
||||
export function buildManagedNginxSharedFileBase(
|
||||
containerName: string,
|
||||
domainNames: string[],
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
buildManagedNginxFileBase,
|
||||
buildManagedNginxSharedFileBase,
|
||||
getDomainConfigAnchor,
|
||||
getLegacyTwoLabelDomainConfigAnchor,
|
||||
sanitizeDomainFileName,
|
||||
sanitizeContainerFileName,
|
||||
} from "./names";
|
||||
@@ -385,6 +386,14 @@ export async function provisionNginxConfig(
|
||||
configMode === "SHARED"
|
||||
? `${sanitizeContainerFileName(containerName)}--${sanitizeDomainFileName(getDomainConfigAnchor(domainNames))}`
|
||||
: null;
|
||||
const legacyBuggyAnchorSafeName =
|
||||
configMode === "SHARED"
|
||||
? sanitizeDomainFileName(getLegacyTwoLabelDomainConfigAnchor(domainNames))
|
||||
: null;
|
||||
const legacyBuggySharedSafeName =
|
||||
configMode === "SHARED"
|
||||
? `${sanitizeContainerFileName(containerName)}--${sanitizeDomainFileName(getLegacyTwoLabelDomainConfigAnchor(domainNames))}`
|
||||
: null;
|
||||
const legacyServiceSharedSafeName =
|
||||
configMode === "SHARED"
|
||||
? `doktainer-${sanitizeContainerFileName(containerName)}`
|
||||
@@ -425,6 +434,20 @@ export async function provisionNginxConfig(
|
||||
`rm -f ${escapeDoubleQuotedShellArg(`/etc/nginx/sites-enabled/${legacySharedSafeName}.conf`)} ${escapeDoubleQuotedShellArg(`/etc/nginx/sites-available/${legacySharedSafeName}.conf`)}`,
|
||||
);
|
||||
}
|
||||
if (legacyBuggyAnchorSafeName && legacyBuggyAnchorSafeName !== safeName) {
|
||||
cleanupCommands.push(
|
||||
`rm -f ${escapeDoubleQuotedShellArg(`/etc/nginx/sites-enabled/${legacyBuggyAnchorSafeName}.conf`)} ${escapeDoubleQuotedShellArg(`/etc/nginx/sites-available/${legacyBuggyAnchorSafeName}.conf`)}`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
legacyBuggySharedSafeName &&
|
||||
legacyBuggySharedSafeName !== safeName &&
|
||||
legacyBuggySharedSafeName !== legacySharedSafeName
|
||||
) {
|
||||
cleanupCommands.push(
|
||||
`rm -f ${escapeDoubleQuotedShellArg(`/etc/nginx/sites-enabled/${legacyBuggySharedSafeName}.conf`)} ${escapeDoubleQuotedShellArg(`/etc/nginx/sites-available/${legacyBuggySharedSafeName}.conf`)}`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
legacyServiceSharedSafeName &&
|
||||
legacyServiceSharedSafeName !== safeName
|
||||
@@ -498,6 +521,14 @@ export async function removeManagedNginxProxyConfig(options: {
|
||||
options.containerName && configMode === "SHARED"
|
||||
? `${sanitizeContainerFileName(options.containerName)}--${sanitizeDomainFileName(getDomainConfigAnchor(domainNames))}`
|
||||
: null;
|
||||
const legacyBuggyAnchorSafeName =
|
||||
configMode === "SHARED"
|
||||
? sanitizeDomainFileName(getLegacyTwoLabelDomainConfigAnchor(domainNames))
|
||||
: null;
|
||||
const legacyBuggySharedSafeName =
|
||||
options.containerName && configMode === "SHARED"
|
||||
? `${sanitizeContainerFileName(options.containerName)}--${sanitizeDomainFileName(getLegacyTwoLabelDomainConfigAnchor(domainNames))}`
|
||||
: null;
|
||||
const legacyServiceSharedSafeName =
|
||||
options.containerName && configMode === "SHARED"
|
||||
? `doktainer-${sanitizeContainerFileName(options.containerName)}`
|
||||
@@ -520,6 +551,30 @@ export async function removeManagedNginxProxyConfig(options: {
|
||||
`/etc/nginx/sites-available/${legacySharedSafeName}.conf`,
|
||||
);
|
||||
}
|
||||
if (legacyBuggyAnchorSafeName && legacyBuggyAnchorSafeName !== managedSafeName) {
|
||||
await removePrivilegedFile(
|
||||
options.server,
|
||||
`/etc/nginx/sites-enabled/${legacyBuggyAnchorSafeName}.conf`,
|
||||
);
|
||||
await removePrivilegedFile(
|
||||
options.server,
|
||||
`/etc/nginx/sites-available/${legacyBuggyAnchorSafeName}.conf`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
legacyBuggySharedSafeName &&
|
||||
legacyBuggySharedSafeName !== managedSafeName &&
|
||||
legacyBuggySharedSafeName !== legacySharedSafeName
|
||||
) {
|
||||
await removePrivilegedFile(
|
||||
options.server,
|
||||
`/etc/nginx/sites-enabled/${legacyBuggySharedSafeName}.conf`,
|
||||
);
|
||||
await removePrivilegedFile(
|
||||
options.server,
|
||||
`/etc/nginx/sites-available/${legacyBuggySharedSafeName}.conf`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
legacyServiceSharedSafeName &&
|
||||
legacyServiceSharedSafeName !== managedSafeName
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
buildManagedNginxFileBase,
|
||||
buildManagedNginxSharedFileBase,
|
||||
getDomainConfigAnchor,
|
||||
getLegacyTwoLabelDomainConfigAnchor,
|
||||
} from "../../src/server/services/domain-provisioning/names";
|
||||
|
||||
test("isolated nginx config uses a single domain file name", () => {
|
||||
@@ -48,3 +49,28 @@ test("shared nginx config anchor keeps service files separate across roots", ()
|
||||
"example.com",
|
||||
);
|
||||
});
|
||||
|
||||
test("shared nginx config anchor respects public suffix domains", () => {
|
||||
assert.equal(
|
||||
getDomainConfigAnchor(["stream.appku.web.id"]),
|
||||
"appku.web.id",
|
||||
);
|
||||
|
||||
assert.equal(getDomainConfigAnchor(["api.appku.co.id"]), "appku.co.id");
|
||||
assert.equal(
|
||||
getDomainConfigAnchor(["foo.example.co.uk"]),
|
||||
"example.co.uk",
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
buildManagedNginxSharedFileBase("livestream-app", [
|
||||
"stream.appku.web.id",
|
||||
]),
|
||||
"appku.web.id",
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
getLegacyTwoLabelDomainConfigAnchor(["stream.appku.web.id"]),
|
||||
"web.id",
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user