add and update multiple indexes at once
This commit is contained in:
parent
2bd12aeaa3
commit
176a3f66b0
24
README.md
24
README.md
@ -88,17 +88,17 @@ Returns all of `:domain`'s DNS records or performs a search based on provided qu
|
|||||||
```
|
```
|
||||||
|
|
||||||
### `POST /zone/records/:domain`
|
### `POST /zone/records/:domain`
|
||||||
Updates a DNS record of `:domain` at `index`.
|
Updates a single or multiple DNS records of `:domain` at `index`.
|
||||||
|
|
||||||
**Body:**
|
**Body:**
|
||||||
```typescript
|
```typescript
|
||||||
{
|
{
|
||||||
index: number;
|
|
||||||
record: {
|
record: {
|
||||||
|
index: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
}
|
} | {...}[];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -107,12 +107,16 @@ Updates a DNS record of `:domain` at `index`.
|
|||||||
{
|
{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
record: DNSRecord;
|
changed: DNSRecord[];
|
||||||
|
errors: {
|
||||||
|
message: string;
|
||||||
|
record: DNSRecord;
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `PUT /zone/records/:domain`
|
### `PUT /zone/records/:domain`
|
||||||
Creates a new DNS record for `:domain`.
|
Creates a single or multiple new DNS records for `:domain`.
|
||||||
|
|
||||||
**Body:**
|
**Body:**
|
||||||
```typescript
|
```typescript
|
||||||
@ -121,7 +125,7 @@ Creates a new DNS record for `:domain`.
|
|||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
} | {...}[];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -130,12 +134,16 @@ Creates a new DNS record for `:domain`.
|
|||||||
{
|
{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
record: DNSRecord;
|
created: ({ index: number, ...DNSRecord })[];
|
||||||
|
errors: {
|
||||||
|
message: string;
|
||||||
|
record: DNSRecord;
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `DELETE /zone/records/:domain`
|
### `DELETE /zone/records/:domain`
|
||||||
Deletes a DNS record from `:domain` at `index`.
|
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.
|
||||||
|
|
||||||
**Body:**
|
**Body:**
|
||||||
```typescript
|
```typescript
|
||||||
|
202
src/index.ts
202
src/index.ts
@ -101,56 +101,98 @@ api.get('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a record by its index in the zone file
|
* Update records by their index in the zone file
|
||||||
* index: number;
|
|
||||||
* record: {
|
* record: {
|
||||||
|
* index: number;
|
||||||
* name?: string;
|
* name?: string;
|
||||||
* type?: DNSRecordType;
|
* type?: DNSRecordType;
|
||||||
* value?: string;
|
* value?: string;
|
||||||
* }
|
* }[];
|
||||||
*/
|
*/
|
||||||
api.post('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
api.post('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const domain = req.params.domain;
|
||||||
const index = parseInt(req.body.index, 10);
|
let setters = req.body.record;
|
||||||
const setters = req.body.record;
|
|
||||||
|
|
||||||
const cached = await getOrLoad(domain);
|
if (!setters) {
|
||||||
const { zone } = cached;
|
res.json({ success: true, message: 'Nothing was changed.' });
|
||||||
if (index == null || isNaN(index) || !zone.records[index]) {
|
|
||||||
throw new Error('Invalid record index.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = Object.keys(setters);
|
|
||||||
const record = zone.records[index];
|
|
||||||
if (!setters || keys.length === 0) {
|
|
||||||
res.json({ success: true, message: 'Nothing was changed.', record });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setters.type) {
|
if (!Array.isArray(setters)) {
|
||||||
const upperType = setters.type.toUpperCase();
|
setters = [setters];
|
||||||
if (upperType === 'SOA' && record.type !== DNSRecordType.SOA) {
|
|
||||||
throw new Error('Cannot change type to Start Of Authority.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DNSRecordType[<keyof typeof DNSRecordType>upperType] && upperType !== '*') {
|
|
||||||
throw new Error('Unsupported record type.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys.forEach((key) => {
|
const cached = await getOrLoad(domain);
|
||||||
if (record[key]) {
|
const { zone } = cached;
|
||||||
record[key] = setters[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!validator.validateRecord(record)) {
|
const changed = [];
|
||||||
throw new Error('Validation error: Invalid characters');
|
const errors = [];
|
||||||
|
|
||||||
|
for (const setter of setters) {
|
||||||
|
const index = parseInt(setter.index, 10);
|
||||||
|
if (index == null || isNaN(index) || !zone.records[index]) {
|
||||||
|
errors.push({
|
||||||
|
message: 'Invalid record index.'
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys = Object.keys(setter);
|
||||||
|
const record = { ...zone.records[index] };
|
||||||
|
if (!setter || keys.length === 0) {
|
||||||
|
errors.push({
|
||||||
|
message: 'Nothing was changed.',
|
||||||
|
record,
|
||||||
|
})
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setter.type) {
|
||||||
|
const upperType = setter.type.toUpperCase();
|
||||||
|
if (upperType === 'SOA' && record.type !== DNSRecordType.SOA) {
|
||||||
|
errors.push({
|
||||||
|
message: 'Cannot change type to Start Of Authority.',
|
||||||
|
record
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DNSRecordType[<keyof typeof DNSRecordType>upperType] && upperType !== '*') {
|
||||||
|
errors.push({
|
||||||
|
message: 'Unsupported record type.',
|
||||||
|
record
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (record[key]) {
|
||||||
|
record[key] = setter[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!validator.validateRecord(record)) {
|
||||||
|
errors.push({
|
||||||
|
message: 'Validation error: Invalid characters',
|
||||||
|
record
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
zone.records[index] = record;
|
||||||
|
changed.push(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
await cache.update(domain, cached);
|
await cache.update(domain, cached);
|
||||||
|
|
||||||
res.json({ success: true, message: 'Record changed successfully.', record });
|
if (!changed.length && errors.length) {
|
||||||
|
res.status(400).json({ success: false, message: 'Updating record(s) failed.', changed, errors });
|
||||||
|
} else if (changed.length) {
|
||||||
|
res.json({ success: true, message: 'Record(s) changed successfully.', changed, errors });
|
||||||
|
} else {
|
||||||
|
res.json({ success: true, message: 'Nothing was changed.', changed, errors });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,50 +226,88 @@ api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
* name: string;
|
* name: string;
|
||||||
* type: DNSRecordType;
|
* type: DNSRecordType;
|
||||||
* value: string;
|
* value: string;
|
||||||
* }
|
* }[];
|
||||||
*/
|
*/
|
||||||
api.put('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
api.put('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const domain = req.params.domain;
|
||||||
const setter = req.body.record;
|
let setters = req.body.record;
|
||||||
|
|
||||||
if (!setter) {
|
if (!setters) {
|
||||||
throw new Error('New record is missing!');
|
throw new Error('New record is missing!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const missing = ['name', 'type', 'value'].reduce<string[]>(
|
if (!Array.isArray(setters)) {
|
||||||
(list, entry) => (setter[entry] == null ? [...list, entry] : list)
|
setters = [setters];
|
||||||
, []);
|
|
||||||
|
|
||||||
if (missing.length) {
|
|
||||||
throw new Error(`${missing.join(', ')} ${missing.length > 1 ? 'are' : 'is'} required.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name, type, value } = setter;
|
|
||||||
const upperType = type.toUpperCase();
|
|
||||||
if (upperType === 'SOA') {
|
|
||||||
throw new Error('Cannot add another Start Of Authority record. Please use POST method to modify the existing record.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DNSRecordType[<keyof typeof DNSRecordType>upperType] && upperType !== '*') {
|
|
||||||
throw new Error('Unsupported record type.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = await getOrLoad(domain);
|
const cached = await getOrLoad(domain);
|
||||||
const { zone } = cached;
|
const { zone } = cached;
|
||||||
const newRecord = { name, type: upperType, value };
|
|
||||||
|
|
||||||
if (!validator.validateRecord(newRecord)) {
|
const created = [];
|
||||||
throw new Error('Validation error: Invalid characters');
|
const errors = [];
|
||||||
|
|
||||||
|
for (const setter of setters) {
|
||||||
|
const missing = ['name', 'type', 'value'].reduce<string[]>(
|
||||||
|
(list, entry) => (setter[entry] == null ? [...list, entry] : list)
|
||||||
|
, []);
|
||||||
|
|
||||||
|
if (missing.length) {
|
||||||
|
errors.push({
|
||||||
|
message: `${missing.join(', ')} ${missing.length > 1 ? 'are' : 'is'} required.`,
|
||||||
|
record: setter
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, type, value } = setter;
|
||||||
|
const upperType = type.toUpperCase();
|
||||||
|
if (upperType === 'SOA') {
|
||||||
|
errors.push({
|
||||||
|
message: 'Cannot add another Start Of Authority record. Please use POST method to modify the existing record.',
|
||||||
|
record: setter
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DNSRecordType[<keyof typeof DNSRecordType>upperType] && upperType !== '*') {
|
||||||
|
errors.push({
|
||||||
|
message: 'Unsupported record type.',
|
||||||
|
record: setter
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newRecord = { name, type: upperType, value };
|
||||||
|
|
||||||
|
if (!validator.validateRecord(newRecord)) {
|
||||||
|
errors.push({
|
||||||
|
message: 'Validation error: Invalid characters',
|
||||||
|
record: setter
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache.search(cached, name, upperType, value, true).length) {
|
||||||
|
errors.push({
|
||||||
|
message: 'Exact same record already exists. No need to duplicate records!',
|
||||||
|
record: setter
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = zone.records.push(newRecord) - 1;
|
||||||
|
created.push({ ...newRecord, index });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cache.search(cached, name, upperType, value, true).length) {
|
|
||||||
throw new Error('Exact same record already exists. No need to duplicate records!');
|
|
||||||
}
|
|
||||||
|
|
||||||
zone.records.push(newRecord);
|
|
||||||
|
|
||||||
await cache.update(domain, cached);
|
await cache.update(domain, cached);
|
||||||
res.status(201).json({ success: true, message: 'Record added.', record: newRecord });
|
|
||||||
|
if (!created.length && errors.length) {
|
||||||
|
res.status(400).json({ success: false, message: 'Creating record(s) failed.', created, errors });
|
||||||
|
} else if (created.length) {
|
||||||
|
res.status(201).json({ success: true, message: 'Record(s) created successfully.', created, errors });
|
||||||
|
} else {
|
||||||
|
res.json({ success: true, message: 'Nothing was created.', created, errors });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user