Allow add/edit domain to upsert tenant DNS on Cloudflare or Arvan.
Update DNS writes @, www, business, customer, and api records on the chosen provider instead of Arvan only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
d1f60b5313
commit
f42e6fe2a4
@@ -28,6 +28,37 @@ export class CloudflareDnsService {
|
||||
created: string[];
|
||||
updated: string[];
|
||||
skipped: string[];
|
||||
}> {
|
||||
return this.ensureCnameRecords(hostRaw, (host) => [
|
||||
{ relative: '@', content: TARGET },
|
||||
{ relative: 'www', content: host },
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full Meshkee tenant: @ + www + business + customer + api, DNS-only.
|
||||
*/
|
||||
async ensureTenantRecords(hostRaw: string): Promise<{
|
||||
created: string[];
|
||||
updated: string[];
|
||||
skipped: string[];
|
||||
}> {
|
||||
return this.ensureCnameRecords(hostRaw, (host) => [
|
||||
{ relative: '@', content: TARGET },
|
||||
{ relative: 'www', content: host },
|
||||
{ relative: 'business', content: 'business.meshkee.com' },
|
||||
{ relative: 'customer', content: 'customer.meshkee.com' },
|
||||
{ relative: 'api', content: 'api.meshkee.com' },
|
||||
]);
|
||||
}
|
||||
|
||||
private async ensureCnameRecords(
|
||||
hostRaw: string,
|
||||
desiredFor: (apex: string) => Array<{ relative: string; content: string }>,
|
||||
): Promise<{
|
||||
created: string[];
|
||||
updated: string[];
|
||||
skipped: string[];
|
||||
}> {
|
||||
const host = hostRaw.trim().toLowerCase();
|
||||
if (!host) {
|
||||
@@ -47,15 +78,10 @@ export class CloudflareDnsService {
|
||||
const updated: string[] = [];
|
||||
const skipped: string[] = [];
|
||||
|
||||
const desired: Array<{ type: 'CNAME'; relative: string; content: string }> = [
|
||||
{ type: 'CNAME', relative: '@', content: TARGET },
|
||||
{ type: 'CNAME', relative: 'www', content: host },
|
||||
];
|
||||
|
||||
for (const item of desired) {
|
||||
const label = `${item.type} ${item.relative}`;
|
||||
for (const item of desiredFor(host)) {
|
||||
const label = `CNAME ${item.relative}`;
|
||||
const match = existing.find(
|
||||
(rec) => rec.type === item.type && this.relativeName(rec.name, host) === item.relative,
|
||||
(rec) => rec.type === 'CNAME' && this.relativeName(rec.name, host) === item.relative,
|
||||
);
|
||||
|
||||
if (match && this.sameCname(match, item.content) && match.proxied === false) {
|
||||
@@ -63,10 +89,7 @@ export class CloudflareDnsService {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apex CNAME cannot coexist with A/AAAA.
|
||||
if (item.relative === '@') {
|
||||
await this.deleteConflictingApexAddress(zoneId, token, existing, host);
|
||||
}
|
||||
await this.deleteConflictingAddress(zoneId, token, existing, host, item.relative);
|
||||
|
||||
if (match) {
|
||||
const patch = await this.request(
|
||||
@@ -74,7 +97,7 @@ export class CloudflareDnsService {
|
||||
`/zones/${zoneId}/dns_records/${match.id}`,
|
||||
token,
|
||||
{
|
||||
type: item.type,
|
||||
type: 'CNAME',
|
||||
name: item.relative,
|
||||
content: item.content,
|
||||
ttl: TTL,
|
||||
@@ -91,7 +114,7 @@ export class CloudflareDnsService {
|
||||
}
|
||||
|
||||
const post = await this.request('POST', `/zones/${zoneId}/dns_records`, token, {
|
||||
type: item.type,
|
||||
type: 'CNAME',
|
||||
name: item.relative,
|
||||
content: item.content,
|
||||
ttl: TTL,
|
||||
@@ -163,21 +186,23 @@ export class CloudflareDnsService {
|
||||
return items;
|
||||
}
|
||||
|
||||
private async deleteConflictingApexAddress(
|
||||
private async deleteConflictingAddress(
|
||||
zoneId: string,
|
||||
token: string,
|
||||
existing: CfRecord[],
|
||||
zone: string,
|
||||
relative: string,
|
||||
) {
|
||||
const conflicts = existing.filter(
|
||||
(rec) =>
|
||||
(rec.type === 'A' || rec.type === 'AAAA') && this.relativeName(rec.name, zone) === '@',
|
||||
(rec.type === 'A' || rec.type === 'AAAA') &&
|
||||
this.relativeName(rec.name, zone) === relative,
|
||||
);
|
||||
for (const rec of conflicts) {
|
||||
const del = await this.request('DELETE', `/zones/${zoneId}/dns_records/${rec.id}`, token);
|
||||
if (!del.ok) {
|
||||
throw new ServiceUnavailableException(
|
||||
`Cloudflare failed to replace ${rec.type} @ (${del.status})${del.message ? `: ${del.message}` : ''}`,
|
||||
`Cloudflare failed to replace ${rec.type} ${relative} (${del.status})${del.message ? `: ${del.message}` : ''}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user