mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Enhance DNS error handling with user-friendly messages and add tests for TXT and CNAME record verification
This commit is contained in:
@@ -14,7 +14,44 @@ export default class Domain extends DomainCommon {
|
||||
data.domain,
|
||||
(err: Error | null, addresses: string[]) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
logger.debug(
|
||||
`DNS CNAME lookup failed for domain ${data.domain}: ${err.message}`,
|
||||
);
|
||||
|
||||
// Handle specific DNS error types with user-friendly messages
|
||||
if (err.message.includes("ENODATA") || err.message.includes("queryCname ENODATA")) {
|
||||
reject(
|
||||
new BadDataException(
|
||||
`No CNAME records found for domain "${data.domain}". Please ensure you have added the CNAME record and wait for DNS propagation (up to 72 hours).`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.message.includes("ENOTFOUND") || err.message.includes("queryCname ENOTFOUND")) {
|
||||
reject(
|
||||
new BadDataException(
|
||||
`Domain "${data.domain}" not found. Please check if the domain is correct and accessible.`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.message.includes("ETIMEDOUT") || err.message.includes("queryCname ETIMEDOUT")) {
|
||||
reject(
|
||||
new BadDataException(
|
||||
`DNS lookup timeout for domain "${data.domain}". Please try again later.`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generic DNS error fallback
|
||||
reject(
|
||||
new BadDataException(
|
||||
`Unable to verify CNAME record for domain "${data.domain}". DNS Error: ${err.message}. Please check your DNS configuration and try again.`,
|
||||
),
|
||||
);
|
||||
} else if (addresses.length > 0) {
|
||||
resolve(addresses);
|
||||
} else {
|
||||
@@ -44,7 +81,41 @@ export default class Domain extends DomainCommon {
|
||||
domain.toString(),
|
||||
(err: Error | null, data: Array<Array<string>>) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
logger.debug(
|
||||
`DNS TXT lookup failed for domain ${domain.toString()}: ${err.message}`,
|
||||
);
|
||||
|
||||
// Handle specific DNS error types with user-friendly messages
|
||||
if (err.message.includes("ENODATA") || err.message.includes("queryTxt ENODATA")) {
|
||||
return reject(
|
||||
new BadDataException(
|
||||
`No TXT records found for domain "${domain.toString()}". Please ensure you have added the TXT record and wait for DNS propagation (up to 72 hours).`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (err.message.includes("ENOTFOUND") || err.message.includes("queryTxt ENOTFOUND")) {
|
||||
return reject(
|
||||
new BadDataException(
|
||||
`Domain "${domain.toString()}" not found. Please check if the domain is correct and accessible.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (err.message.includes("ETIMEDOUT") || err.message.includes("queryTxt ETIMEDOUT")) {
|
||||
return reject(
|
||||
new BadDataException(
|
||||
`DNS lookup timeout for domain "${domain.toString()}". Please try again later.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Generic DNS error fallback
|
||||
return reject(
|
||||
new BadDataException(
|
||||
`Unable to verify TXT record for domain "${domain.toString()}". DNS Error: ${err.message}. Please check your DNS configuration and try again.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug("Verify TXT Record");
|
||||
|
||||
92
Common/Tests/Server/Types/Domain.test.ts
Normal file
92
Common/Tests/Server/Types/Domain.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import Domain from "../../../Server/Types/Domain";
|
||||
import BadDataException from "../../../Types/Exception/BadDataException";
|
||||
|
||||
describe("Domain TXT Record Verification", () => {
|
||||
jest.setTimeout(30000); // 30 seconds timeout for DNS tests
|
||||
|
||||
test("should throw user-friendly error for ENODATA", async () => {
|
||||
// Testing with a domain that exists but has no TXT records
|
||||
const domain = "nonexistentsubdomain-test.google.com";
|
||||
const verificationText = "test-verification-text";
|
||||
|
||||
await expect(
|
||||
Domain.verifyTxtRecord(domain, verificationText)
|
||||
).rejects.toThrow(BadDataException);
|
||||
|
||||
try {
|
||||
await Domain.verifyTxtRecord(domain, verificationText);
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(BadDataException);
|
||||
if (error instanceof BadDataException) {
|
||||
expect(error.message).toContain("Domain \"nonexistentsubdomain-test.google.com\" not found. Please check if the domain is correct and accessible.");
|
||||
expect(error.message).toContain(domain);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("should throw user-friendly error for non-existent domain", async () => {
|
||||
// Testing with a domain that doesn't exist
|
||||
const domain = "thisisadomainthatdoesnotexistanywhere12345.nonexistent";
|
||||
const verificationText = "test-verification-text";
|
||||
|
||||
await expect(
|
||||
Domain.verifyTxtRecord(domain, verificationText)
|
||||
).rejects.toThrow(BadDataException);
|
||||
|
||||
try {
|
||||
await Domain.verifyTxtRecord(domain, verificationText);
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(BadDataException);
|
||||
if (error instanceof BadDataException) {
|
||||
expect(error.message).toContain("not found");
|
||||
expect(error.message).toContain(domain);
|
||||
// Should not contain technical DNS error codes
|
||||
expect(error.message).not.toContain("ENOTFOUND");
|
||||
expect(error.message).not.toContain("queryTxt");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Domain CNAME Record Verification", () => {
|
||||
jest.setTimeout(30000); // 30 seconds timeout for DNS tests
|
||||
|
||||
test("should throw user-friendly error for CNAME ENODATA", async () => {
|
||||
// Testing with a domain that exists but has no CNAME records (e.g., A record only domain)
|
||||
const domain = "google.com"; // This is an A record, not CNAME
|
||||
|
||||
await expect(
|
||||
Domain.getCnameRecords({ domain })
|
||||
).rejects.toThrow(BadDataException);
|
||||
|
||||
try {
|
||||
await Domain.getCnameRecords({ domain });
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(BadDataException);
|
||||
if (error instanceof BadDataException) {
|
||||
expect(error.message).toContain("CNAME");
|
||||
expect(error.message).toContain(domain);
|
||||
// Should not contain technical DNS error codes in user message
|
||||
expect(error.message).not.toContain("queryCname");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("should get CNAME records for valid CNAME domain", async () => {
|
||||
// This test might be flaky depending on DNS changes, but let's try with a known CNAME
|
||||
const domain = "www.github.com"; // This usually has CNAME records
|
||||
|
||||
try {
|
||||
const cnameRecords = await Domain.getCnameRecords({ domain });
|
||||
expect(Array.isArray(cnameRecords)).toBe(true);
|
||||
expect(cnameRecords.length).toBeGreaterThan(0);
|
||||
} catch (error) {
|
||||
// If this fails, it should still provide a user-friendly error
|
||||
expect(error).toBeInstanceOf(BadDataException);
|
||||
if (error instanceof BadDataException) {
|
||||
expect(error.message).not.toContain("queryCname");
|
||||
expect(error.message).not.toContain("ENODATA");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user