mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-09-14 11:06:15 +05:00
fix(dns): represent the Infomaniak apex as a root dot
Verified against a live Infomaniak account: the API returns `source: "."` for
apex records, not "" as assumed.
Reading them back produced a doubled dot ("..example.com"), and writing "" meant
an apex upsert never matched the existing record, so it would have created a
duplicate apex record instead of updating it.
toSource now emits "." for the apex and toFqdn accepts ".", "" and "@" so a
hand-written record still round-trips.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ff3ebefc70
commit
2bc22c520d
@ -100,11 +100,9 @@ describe("infomaniakClient.listRecords", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("treats an @ source as the apex", async () => {
|
||||
it.each([".", "", "@"])("treats a %s source as the apex", async (source) => {
|
||||
mockFetch.mockResolvedValue(
|
||||
ikSuccess([
|
||||
{ id: 12, type: "A", source: "@", target: "1.2.3.4", ttl: 300 },
|
||||
]),
|
||||
ikSuccess([{ id: 12, type: "A", source, target: "1.2.3.4", ttl: 300 }]),
|
||||
);
|
||||
|
||||
const records = await infomaniakClient.listRecords(config, "example.com");
|
||||
@ -200,7 +198,7 @@ describe("infomaniakClient.upsertRecord", () => {
|
||||
expect(lastBody().ttl).toBe(300);
|
||||
});
|
||||
|
||||
it("writes an empty source for an apex record and strips the trailing dot", async () => {
|
||||
it("writes a root dot as the source for an apex record and strips the trailing dot", async () => {
|
||||
mockFetch
|
||||
.mockResolvedValueOnce(ikSuccess([]))
|
||||
.mockResolvedValueOnce(ikSuccess({ id: 43 }));
|
||||
@ -212,7 +210,38 @@ describe("infomaniakClient.upsertRecord", () => {
|
||||
content: "1.2.3.4",
|
||||
});
|
||||
|
||||
expect(lastBody().source).toBe("");
|
||||
expect(lastBody().source).toBe(".");
|
||||
});
|
||||
|
||||
it("matches the existing apex record instead of creating a duplicate", async () => {
|
||||
mockFetch
|
||||
.mockResolvedValueOnce(
|
||||
ikSuccess([
|
||||
{
|
||||
id: 8,
|
||||
type: "TXT",
|
||||
source: ".",
|
||||
target: '"v=spf1 -all"',
|
||||
ttl: 3600,
|
||||
},
|
||||
]),
|
||||
)
|
||||
.mockResolvedValueOnce(ikSuccess({ id: 8 }));
|
||||
|
||||
const result = await infomaniakClient.upsertRecord(config, {
|
||||
zoneId: "example.com",
|
||||
type: "TXT",
|
||||
name: "example.com",
|
||||
content: "v=spf1 -all",
|
||||
});
|
||||
|
||||
expect(result).toEqual({ id: "8" });
|
||||
const [url, init] = lastCall();
|
||||
expect(init.method).toBe("PUT");
|
||||
expect(url).toBe(
|
||||
"https://api.infomaniak.com/2/zones/example.com/records/8",
|
||||
);
|
||||
expect(lastBody().target).toBe('"v=spf1 -all"');
|
||||
});
|
||||
|
||||
it("quotes a TXT target on write", async () => {
|
||||
|
||||
@ -54,19 +54,22 @@ const ikFetch = async <T>(
|
||||
return body.data as T;
|
||||
};
|
||||
|
||||
// Infomaniak's "source" holds the subdomain only, relative to the zone, and is
|
||||
// empty for the apex.
|
||||
// Infomaniak's "source" holds the subdomain only, relative to the zone. The apex
|
||||
// is a bare root dot; "" and "@" are accepted too so a hand-written record still
|
||||
// round-trips.
|
||||
const APEX_SOURCES = new Set(["", ".", "@"]);
|
||||
|
||||
const toSource = (name: string, zone: string) => {
|
||||
const fqdn = name.replace(/\.$/, "");
|
||||
if (fqdn === zone) {
|
||||
return "";
|
||||
return ".";
|
||||
}
|
||||
const suffix = `.${zone}`;
|
||||
return fqdn.endsWith(suffix) ? fqdn.slice(0, -suffix.length) : fqdn;
|
||||
};
|
||||
|
||||
const toFqdn = (source: string, zone: string) =>
|
||||
source === "" || source === "@" ? zone : `${source}.${zone}`;
|
||||
APEX_SOURCES.has(source) ? zone : `${source}.${zone}`;
|
||||
|
||||
// TXT targets are stored quoted; keep Dokploy's view of them unquoted so that
|
||||
// editing a record does not stack a new pair of quotes on every save.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user