199 lines
6.8 KiB
TypeScript
199 lines
6.8 KiB
TypeScript
|
|
import * as plugins from './cloudflare.plugins.js';
|
||
|
|
import { logger } from './cloudflare.logger.js';
|
||
|
|
import { CloudflareRecord } from './cloudflare.classes.record.js';
|
||
|
|
|
||
|
|
export class RecordManager {
|
||
|
|
constructor(private cfAccount: any) {}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Lists all DNS records for a domain
|
||
|
|
* @param domainNameArg - The domain name to list records for
|
||
|
|
* @returns Array of CloudflareRecord instances
|
||
|
|
*/
|
||
|
|
public async listRecords(domainNameArg: string): Promise<CloudflareRecord[]> {
|
||
|
|
try {
|
||
|
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||
|
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||
|
|
const records: plugins.ICloudflareTypes['Record'][] = [];
|
||
|
|
|
||
|
|
// Collect all records using async iterator
|
||
|
|
for await (const record of this.cfAccount.apiAccount.dns.records.list({
|
||
|
|
zone_id: zoneId,
|
||
|
|
})) {
|
||
|
|
records.push(record);
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.log('info', `Found ${records.length} DNS records for ${domainNameArg}`);
|
||
|
|
|
||
|
|
// Convert to CloudflareRecord instances
|
||
|
|
return records.map(record => CloudflareRecord.createFromApiObject(record));
|
||
|
|
} catch (error) {
|
||
|
|
logger.log('error', `Failed to list records for ${domainNameArg}: ${error.message}`);
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gets a specific DNS record by domain and type
|
||
|
|
* @param domainNameArg - The domain name
|
||
|
|
* @param typeArg - The DNS record type (A, AAAA, CNAME, TXT, etc.)
|
||
|
|
* @returns CloudflareRecord instance or undefined if not found
|
||
|
|
*/
|
||
|
|
public async getRecord(
|
||
|
|
domainNameArg: string,
|
||
|
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||
|
|
): Promise<CloudflareRecord | undefined> {
|
||
|
|
try {
|
||
|
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||
|
|
const recordArray = await this.listRecords(domain.zoneName);
|
||
|
|
|
||
|
|
const filteredRecords = recordArray.filter((recordArg) => {
|
||
|
|
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
||
|
|
});
|
||
|
|
|
||
|
|
return filteredRecords.length > 0 ? filteredRecords[0] : undefined;
|
||
|
|
} catch (error) {
|
||
|
|
logger.log('error', `Error getting record for ${domainNameArg}: ${error.message}`);
|
||
|
|
return undefined;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Creates a new DNS record
|
||
|
|
* @param domainNameArg - The domain name for the record
|
||
|
|
* @param typeArg - The DNS record type
|
||
|
|
* @param contentArg - The record content (IP address, CNAME target, etc.)
|
||
|
|
* @param ttlArg - Time to live in seconds (default: 1 = automatic)
|
||
|
|
* @returns Created CloudflareRecord instance
|
||
|
|
*/
|
||
|
|
public async createRecord(
|
||
|
|
domainNameArg: string,
|
||
|
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||
|
|
contentArg: string,
|
||
|
|
ttlArg: number = 1,
|
||
|
|
): Promise<CloudflareRecord> {
|
||
|
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||
|
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||
|
|
|
||
|
|
const response = await this.cfAccount.apiAccount.dns.records.create({
|
||
|
|
zone_id: zoneId,
|
||
|
|
type: typeArg as any,
|
||
|
|
name: domain.fullName,
|
||
|
|
content: contentArg,
|
||
|
|
ttl: ttlArg,
|
||
|
|
});
|
||
|
|
|
||
|
|
logger.log('info', `Created ${typeArg} record for ${domainNameArg}`);
|
||
|
|
return CloudflareRecord.createFromApiObject(response);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Updates an existing DNS record, or creates it if it doesn't exist
|
||
|
|
* @param domainNameArg - The domain name
|
||
|
|
* @param typeArg - The DNS record type
|
||
|
|
* @param contentArg - The new record content
|
||
|
|
* @param ttlArg - Time to live in seconds (default: 1 = automatic)
|
||
|
|
* @returns Updated CloudflareRecord instance
|
||
|
|
*/
|
||
|
|
public async updateRecord(
|
||
|
|
domainNameArg: string,
|
||
|
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||
|
|
contentArg: string,
|
||
|
|
ttlArg: number = 1,
|
||
|
|
): Promise<CloudflareRecord> {
|
||
|
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||
|
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||
|
|
|
||
|
|
// Find existing record
|
||
|
|
const existingRecord = await this.getRecord(domainNameArg, typeArg);
|
||
|
|
|
||
|
|
if (!existingRecord) {
|
||
|
|
logger.log(
|
||
|
|
'warn',
|
||
|
|
`Record ${domainNameArg} of type ${typeArg} not found for update, creating instead`,
|
||
|
|
);
|
||
|
|
return this.createRecord(domainNameArg, typeArg, contentArg, ttlArg);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update the record
|
||
|
|
const updatedRecord = await this.cfAccount.apiAccount.dns.records.edit(existingRecord.id, {
|
||
|
|
zone_id: zoneId,
|
||
|
|
type: typeArg as any,
|
||
|
|
name: domain.fullName,
|
||
|
|
content: contentArg,
|
||
|
|
ttl: ttlArg,
|
||
|
|
});
|
||
|
|
|
||
|
|
logger.log('info', `Updated ${typeArg} record for ${domainNameArg}`);
|
||
|
|
return CloudflareRecord.createFromApiObject(updatedRecord);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Deletes a DNS record
|
||
|
|
* @param domainNameArg - The domain name
|
||
|
|
* @param typeArg - The DNS record type
|
||
|
|
*/
|
||
|
|
public async deleteRecord(
|
||
|
|
domainNameArg: string,
|
||
|
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||
|
|
): Promise<void> {
|
||
|
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||
|
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||
|
|
const record = await this.getRecord(domainNameArg, typeArg);
|
||
|
|
|
||
|
|
if (record) {
|
||
|
|
await this.cfAccount.apiAccount.dns.records.delete(record.id, {
|
||
|
|
zone_id: zoneId,
|
||
|
|
});
|
||
|
|
logger.log('info', `Deleted ${typeArg} record for ${domainNameArg}`);
|
||
|
|
} else {
|
||
|
|
logger.log('warn', `Record ${domainNameArg} of type ${typeArg} not found for deletion`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Removes all DNS records of a specific type for a domain
|
||
|
|
* @param domainNameArg - The domain name
|
||
|
|
* @param typeArg - The DNS record type to clean
|
||
|
|
*/
|
||
|
|
public async cleanRecords(
|
||
|
|
domainNameArg: string,
|
||
|
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||
|
|
): Promise<void> {
|
||
|
|
try {
|
||
|
|
logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`);
|
||
|
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||
|
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||
|
|
|
||
|
|
// List all records in the zone for this domain
|
||
|
|
const records = await this.listRecords(domain.zoneName);
|
||
|
|
|
||
|
|
// Only delete records matching the specified name and type
|
||
|
|
const recordsToDelete = records.filter((recordArg) => {
|
||
|
|
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
||
|
|
});
|
||
|
|
|
||
|
|
logger.log(
|
||
|
|
'info',
|
||
|
|
`Found ${recordsToDelete.length} ${typeArg} records to delete for ${domainNameArg}`,
|
||
|
|
);
|
||
|
|
|
||
|
|
for (const recordToDelete of recordsToDelete) {
|
||
|
|
try {
|
||
|
|
await this.cfAccount.apiAccount.dns.records.delete(recordToDelete.id, {
|
||
|
|
zone_id: zoneId,
|
||
|
|
});
|
||
|
|
logger.log('info', `Deleted ${typeArg} record ${recordToDelete.id} for ${domainNameArg}`);
|
||
|
|
} catch (deleteError) {
|
||
|
|
logger.log('error', `Failed to delete record: ${deleteError.message}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
logger.log(
|
||
|
|
'error',
|
||
|
|
`Error cleaning ${typeArg} records for ${domainNameArg}: ${error.message}`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|