From cc2f4fea844bc5017ea311b1522a00028ddfacaf Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Sun, 16 May 2021 17:40:48 +0300 Subject: [PATCH] delete multiple records at once --- README.md | 13 +++++++--- src/dns/cache.ts | 6 ++++- src/index.ts | 56 +++++++++++++++++++++++++++++++++------- src/models/interfaces.ts | 3 ++- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5af569c..29dcf52 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Returns all of `:domain`'s DNS records or performs a search based on provided qu ``` ### `POST /zone/records/:domain` -Updates a single or multiple DNS records of `:domain` at `index`. +Updates or marks for deletion a single or multiple DNS records of `:domain` at `index`. **Body:** ```typescript @@ -98,6 +98,7 @@ Updates a single or multiple DNS records of `:domain` at `index`. name?: string; type?: string; value?: string; + forDeletion?: boolean; } | {...}[]; } ``` @@ -143,12 +144,12 @@ Creates a single or multiple new DNS records for `:domain`. ``` ### `DELETE /zone/records/:domain` -Deletes a DNS record from `:domain` at `index`. **Warning:** Deleting an index that is not at the end of the record causes following records' indexes to shift back by one. +Deletes a single or multiple DNS records from `:domain` at `index`. **Warning:** Deleting an index that is not at the end of the record causes following records' indexes to shift back by one. Refresh your indexes after every addition and deletion! **Body:** ```typescript { - index: number; + index: number | number[]; } ``` @@ -157,7 +158,11 @@ Deletes a DNS record from `:domain` at `index`. **Warning:** Deleting an index t { success: boolean; message: string; - record: DNSRecord; + deleted: DNSRecord[]; + errors: { + message: string; + record: DNSRecord; + }[]; } ``` diff --git a/src/dns/cache.ts b/src/dns/cache.ts index f5af0f3..4d9a65f 100644 --- a/src/dns/cache.ts +++ b/src/dns/cache.ts @@ -107,9 +107,13 @@ export class DNSCache { throw new Error('No such cached zone file!'); } - zone.changed = new Date(); + // Delete marked-for-deletion records + zone.zone.records = zone.zone.records.filter((record) => record.forDeletion !== true); + + // Set new serial const soa = zone.zone.records.find((record) => record.type === DNSRecordType.SOA) as SOARecord; soa.serial = Math.floor(Date.now() / 1000); + zone.changed = new Date(); this.set(name, zone); await this.save(name); diff --git a/src/index.ts b/src/index.ts index ca24f4e..bb754fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -107,6 +107,7 @@ api.get('/zone/records/:domain', domainAuthorization, async (req, res) => { * name?: string; * type?: DNSRecordType; * value?: string; + * forDeletion?: boolean; * }[]; */ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => { @@ -166,8 +167,18 @@ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => { } } + if (record.type === DNSRecordType.SOA && setter.forDeletion) { + errors.push({ + message: 'Cannot delete the Start Of Authority record.', + record + }); + continue; + } + keys.forEach((key) => { - if (record[key]) { + if (key === 'forDeletion') { + record.forDeletion = true; + } else if (record[key]) { record[key] = setter[key]; } }); @@ -201,23 +212,50 @@ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => { */ api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => { const domain = req.params.domain; - const index = parseInt(req.body.index, 10); + let indexes = req.body.index; const cached = await getOrLoad(domain); const { zone } = cached; - if (!index || isNaN(index) || !zone.records[index]) { - throw new Error('Invalid record index.'); + + if (!Array.isArray(indexes)) { + indexes = [indexes]; } - const record = zone.records[index]; - if (record.type === DNSRecordType.SOA) { - throw new Error('Cannot delete the Start Of Authority record.'); + const deleted = []; + const errors = []; + + for (const number of indexes) { + const index = parseInt(number, 10); + if (index == null || isNaN(index) || !zone.records[index]) { + errors.push({ + message: 'Invalid record index.', + record: { index } + }); + continue; + } + + const record = zone.records[index]; + if (record.type === DNSRecordType.SOA) { + errors.push({ + message: 'Cannot delete the Start Of Authority record.', + record + }); + continue; + } + + zone.records[index].forDeletion = true; + deleted.push(record); } - zone.records.splice(index, 1); await cache.update(domain, cached); - res.json({ success: true, message: 'Record deleted successfully.', record }); + if (!deleted.length && errors.length) { + res.status(400).json({ success: false, message: 'Deleting record(s) failed.', deleted, errors }); + } else if (deleted.length) { + res.json({ success: true, message: 'Record(s) deleted successfully.', deleted, errors }); + } else { + res.json({ success: true, message: 'Nothing was deleted.', deleted, errors }); + } }); /** diff --git a/src/models/interfaces.ts b/src/models/interfaces.ts index 7f013d9..298837c 100644 --- a/src/models/interfaces.ts +++ b/src/models/interfaces.ts @@ -1,10 +1,11 @@ import { DNSRecordType } from "../dns/records"; export interface DNSRecord { - [key: string]: string | number; + [key: string]: string | number | boolean | undefined; name: string; type: DNSRecordType; value: string; + forDeletion?: boolean; } export interface SOARecord extends DNSRecord {