delete bulk and export/import

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-10-26 12:31:35 +01:00
padre e0f561de09
commit 28eea2a7ae
Se han modificado 5 ficheros con 536 adiciones y 9 borrados

Ver fichero

@@ -166,6 +166,232 @@ export class OVHService {
return results;
}
async bulkDeleteRecords(zoneName, recordIds) {
const results = [];
for (const recordId of recordIds) {
try {
await this.deleteDNSRecord(zoneName, recordId);
results.push({ recordId, success: true });
} catch (error) {
results.push({ recordId, success: false, error: error.message });
}
}
return results;
}
exportToBind9(zoneName, records) {
const lines = [];
lines.push(`; Zone file for ${zoneName}`);
lines.push(`; Generated on ${new Date().toISOString()}`);
lines.push('');
lines.push(`$ORIGIN ${zoneName}.`);
lines.push('');
// Sort records by type for better readability
const sortedRecords = [...records].sort((a, b) => {
const typeOrder = { SOA: 0, NS: 1, A: 2, AAAA: 3, CNAME: 4, MX: 5, TXT: 6, SRV: 7 };
return (typeOrder[a.fieldType] || 99) - (typeOrder[b.fieldType] || 99);
});
for (const record of sortedRecords) {
const subdomain = record.subDomain || '@';
const ttl = record.ttl || 3600;
let line = '';
switch (record.fieldType) {
case 'A':
case 'AAAA':
line = `${subdomain}\t${ttl}\tIN\t${record.fieldType}\t${record.target}`;
break;
case 'CNAME':
line = `${subdomain}\t${ttl}\tIN\tCNAME\t${record.target}${record.target.endsWith('.') ? '' : '.'}`;
break;
case 'MX':
const priority = record.priority || 10;
line = `${subdomain}\t${ttl}\tIN\tMX\t${priority} ${record.target}${record.target.endsWith('.') ? '' : '.'}`;
break;
case 'TXT':
const txtValue = record.target.includes(' ') && !record.target.startsWith('"')
? `"${record.target}"`
: record.target;
line = `${subdomain}\t${ttl}\tIN\tTXT\t${txtValue}`;
break;
case 'SRV':
const priority_srv = record.priority || 0;
const weight = record.weight || 0;
const port = record.port || 0;
line = `${subdomain}\t${ttl}\tIN\tSRV\t${priority_srv} ${weight} ${port} ${record.target}${record.target.endsWith('.') ? '' : '.'}`;
break;
case 'NS':
line = `${subdomain}\t${ttl}\tIN\tNS\t${record.target}${record.target.endsWith('.') ? '' : '.'}`;
break;
case 'CAA':
const flags = record.flags || 0;
const tag = record.tag || 'issue';
line = `${subdomain}\t${ttl}\tIN\tCAA\t${flags} ${tag} "${record.target}"`;
break;
default:
line = `${subdomain}\t${ttl}\tIN\t${record.fieldType}\t${record.target}`;
}
lines.push(line);
}
lines.push('');
return lines.join('\n');
}
parseFromBind9(zoneName, zoneContent) {
const records = [];
const lines = zoneContent.split('\n');
let currentOrigin = zoneName;
for (let line of lines) {
// Remove comments
const commentIndex = line.indexOf(';');
if (commentIndex !== -1) {
line = line.substring(0, commentIndex);
}
line = line.trim();
// Skip empty lines
if (!line) continue;
// Handle $ORIGIN directive
if (line.startsWith('$ORIGIN')) {
const parts = line.split(/\s+/);
if (parts[1]) {
currentOrigin = parts[1].replace(/\.$/, '');
}
continue;
}
// Skip $TTL and other directives for now
if (line.startsWith('$')) continue;
// Parse record line
const parts = line.split(/\s+/);
if (parts.length < 4) continue;
let idx = 0;
let subdomain = parts[idx++];
// Handle @ symbol
if (subdomain === '@') {
subdomain = '';
}
// Remove trailing dot from subdomain
if (subdomain.endsWith('.')) {
subdomain = subdomain.slice(0, -1);
}
let ttl = 3600;
let recordClass = 'IN';
let recordType = '';
// Parse TTL (if it's a number)
if (!isNaN(parts[idx])) {
ttl = parseInt(parts[idx++]);
}
// Parse class (usually IN)
if (parts[idx] === 'IN' || parts[idx] === 'CH' || parts[idx] === 'HS') {
recordClass = parts[idx++];
}
// Parse record type
recordType = parts[idx++];
// Parse record data based on type
const recordData = {
fieldType: recordType,
subDomain: subdomain,
ttl: ttl
};
switch (recordType) {
case 'A':
case 'AAAA':
recordData.target = parts[idx];
break;
case 'CNAME':
case 'NS':
recordData.target = parts[idx].replace(/\.$/, '');
break;
case 'MX':
recordData.priority = parseInt(parts[idx++]);
recordData.target = parts[idx].replace(/\.$/, '');
break;
case 'TXT':
// Join remaining parts and remove quotes
const txtValue = parts.slice(idx).join(' ');
recordData.target = txtValue.replace(/^"|"$/g, '');
break;
case 'SRV':
recordData.priority = parseInt(parts[idx++]);
recordData.weight = parseInt(parts[idx++]);
recordData.port = parseInt(parts[idx++]);
recordData.target = parts[idx].replace(/\.$/, '');
break;
case 'CAA':
recordData.flags = parseInt(parts[idx++]);
recordData.tag = parts[idx++];
const caaValue = parts.slice(idx).join(' ');
recordData.target = caaValue.replace(/^"|"$/g, '');
break;
default:
recordData.target = parts.slice(idx).join(' ');
}
// Skip SOA records as they are managed by OVH
if (recordType !== 'SOA') {
records.push(recordData);
}
}
return records;
}
async importFromBind9(zoneName, zoneContent, replaceAll = false) {
const records = this.parseFromBind9(zoneName, zoneContent);
if (replaceAll) {
// Delete all existing records (except SOA and NS for zone apex)
const existingRecords = await this.getDNSRecords(zoneName);
const recordsToDelete = existingRecords.filter(r =>
r.fieldType !== 'SOA' &&
!(r.fieldType === 'NS' && (!r.subDomain || r.subDomain === ''))
);
for (const record of recordsToDelete) {
try {
await this.deleteDNSRecord(zoneName, record.id);
} catch (error) {
console.error(`Failed to delete record ${record.id}:`, error);
}
}
}
// Create all imported records
const results = [];
for (const recordData of records) {
try {
const result = await this.createDNSRecord(zoneName, recordData);
results.push({ success: true, record: result });
} catch (error) {
results.push({ success: false, error: error.message, record: recordData });
}
}
return results;
}
getConfig() {
return this.config;
}