Repository navigation
feat(dns): add Hetzner DNS provider - #5940
outeiroDev wants to merge 1 commit into
Conversation
Manage records in Hetzner DNS zones through the Zones API of the Hetzner Cloud API. Like Route53, each Dokploy record maps to an RRSet, and TXT values are quoted and chunked as hcloud-go's zoneutil does.
c4e72b9 to
cc7e5b1
Compare
| content: rrset.records | ||
| .map((record) => unquoteValue(rrset.type, record.value)) | ||
| .join("\n"), |
There was a problem hiding this comment.
Hetzner joins every value in an RRSet with newlines, but DnsRecordPanel only enables its multiline editor for route53. A Hetzner record with several values opens in a single-line input. Changing that field removes the line breaks before set_records replaces the whole RRSet, potentially replacing separate TXT values with one joined value or making address records invalid. Enable the multiline editor for hetzner too.
| content: rrset.records | ||
| .map((record) => unquoteValue(rrset.type, record.value)) | ||
| .join("\n"), | ||
| ttl: rrset.ttl ?? DEFAULT_TTL, |
There was a problem hiding this comment.
Editing changes inherited TTLs
null means the RRSet inherits its zone's TTL, not that its TTL is 300. If the zone's default is 3600, this displays 300 instead. The editor sends that number back unchanged, and updateRecord writes an explicit TTL of 300 even when the user only changed the content. Read the zone's actual default and preserve inherited TTLs when editing.
| const escaped = value.replaceAll('"', '\\"'); | ||
| const chunks: string[] = []; | ||
| for (let i = 0; i < escaped.length; i += TXT_CHUNK_SIZE) { | ||
| chunks.push(`"${escaped.slice(i, i + TXT_CHUNK_SIZE)}"`); |
There was a problem hiding this comment.
formatTxtValue escapes quotes but leaves literal backslashes untouched, then splits the escaped text at arbitrary positions. A value containing 254 a characters followed by "b puts the escape backslash at the end of the first chunk, where it escapes the closing quote. A plain value such as a\b also loses its backslash through parseTxtValue.
Escape backslashes and quotes together, keep each escape sequence inside one chunk, and add tests for both cases.
| await hzFetch(config, `/zones/${encodeURIComponent(zoneId)}/rrsets`, { | ||
| method: "POST", | ||
| body: JSON.stringify({ | ||
| name, | ||
| type: record.type, | ||
| ttl: record.ttl ?? null, | ||
| records, | ||
| }), | ||
| }); |
There was a problem hiding this comment.
Changing an existing A or AAAA record to CNAME keeps the same name but enters this create-before-delete branch. The new CNAME cannot coexist with the old address records, so creation fails before the old RRSet is removed. Changing CNAME to A or AAAA has the same problem. Handle these same-name type changes separately, with recovery if creating the replacement fails.
What is this PR about?
Adds Hetzner as a DNS provider, using the Zones API of the Hetzner Cloud API (
https://api.hetzner.cloud/v1) with a project API token (Read & Write).<name>/<type>(Hetzner's own RRSet id), multiple values one per line.add_records(creates the RRSet if missing, appends only new values); editing in place usesset_records+change_ttl; renaming creates the new RRSet and deletes the old one.FormatTXTRecord/ParseTXTRecordfromhcloud-go'szoneutil. Shown unquoted in Dokploy.meta.pagination.next_page(max 50 per page).hetznerto theDnsProviderTypeenum.Tests in
__test__/dns/hetzner.test.tscover zone listing and pagination, record mapping, apex@, TXT formatting, upsert merge, update/rename, delete and error messages.Checklist
Before submitting this PR, please make sure that:
canarybranch.Issues related (if applicable)
closes #5939
Screenshots (if applicable)
The PR needs fixes before merging because editing records with several values and changing records to or from CNAME are broken.
Summary
Adds Hetzner DNS support through the Hetzner Cloud API.
Reviews (1) · Last reviewed commit: "feat(dns): add Hetzner DNS provider" · Reviewed by Greptile