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 { 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 { 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 { 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 { 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 { 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 { 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}`, ); } } }