cors, change http methods

This commit is contained in:
Evert Prants 2021-05-21 16:41:06 +03:00
parent 621d6b812c
commit 275ae6460d
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
5 changed files with 112 additions and 32 deletions

View File

@ -1,20 +1,23 @@
# IcyDNS HTTP API # IcyDNS HTTP API
HTTP API for managing BIND zone files. HTTP API for managing BIND zone files.
## Running ## Running
This application is intended to be run behind a proxy. Requires node v14+ for `fs/promises`. Also requires `bind-tools` for checking zone files. This application is intended to be run behind a proxy. Requires node v14+ for `fs/promises`. Also requires `bind-tools` for checking zone files.
* `npm install` - `npm install`
* `npm run build` - `npm run build`
* `npm start` - `npm start`
### Environment variables ### Environment variables
* `PORT` - server port
* `ZONEFILES` - path to zone files - `PORT` - server port
* `CACHE_TTL` - internal zone cache time-to-live - `ZONEFILES` - path to zone files
* `RNDC_SERVER` - RNDC host - `CACHE_TTL` - internal zone cache time-to-live
* `RNDC_PORT` - RNDC port - `RNDC_SERVER` - RNDC host
* `RNDC_KEYFILE` - location of RNDC's key file - `RNDC_PORT` - RNDC port
- `RNDC_KEYFILE` - location of RNDC's key file
Zones are automatically reloaded using `rndc` after updates. If you do not have rndc configured, you will need to reload the zones manually, but the files still get updated. Zones are automatically reloaded using `rndc` after updates. If you do not have rndc configured, you will need to reload the zones manually, but the files still get updated.
@ -22,12 +25,14 @@ Zones are automatically reloaded using `rndc` after updates. If you do not have
**All requests are prefixed with `/api/v1`.** Authorization is by bearer token, i.e. `-H 'Authorization: Bearer <token>'`. `?` denotes optional parameter. **All requests are prefixed with `/api/v1`.** Authorization is by bearer token, i.e. `-H 'Authorization: Bearer <token>'`. `?` denotes optional parameter.
### `GET /zone/:domain` ### `GET /zone/{domain}`
Returns all of `:domain`'s DNS records.
Returns all of `{domain}`'s DNS records.
**Query:** None **Query:** None
**Response:** **Response:**
```typescript ```typescript
{ {
ttl: number; ttl: number;
@ -41,17 +46,20 @@ Returns all of `:domain`'s DNS records.
} }
``` ```
### `GET /zone/:domain/download` ### `GET /zone/{domain}/download`
Provides `:domain`'s records as a file.
Provides `{domain}`'s records as a file.
**Query:** None **Query:** None
**Response:** BIND zone file **Response:** BIND zone file
### `POST /zone/:domain` ### `POST /zone/{domain}`
Reloads `:domain`'s zone file. Optionally changes the zone file's TTL value.
Reloads `{domain}`'s zone file. Optionally changes the zone file's TTL value.
**Body:** **Body:**
```typescript ```typescript
{ {
ttl?: number; ttl?: number;
@ -59,6 +67,7 @@ Reloads `:domain`'s zone file. Optionally changes the zone file's TTL value.
``` ```
**Response:** **Response:**
```typescript ```typescript
{ {
success: boolean; success: boolean;
@ -67,15 +76,18 @@ Reloads `:domain`'s zone file. Optionally changes the zone file's TTL value.
} }
``` ```
### `GET /zone/records/:domain` ### `GET /zone/records/{domain}`
Returns all of `:domain`'s DNS records or performs a search based on provided query parameters.
Returns all of `{domain}`'s DNS records or performs a search based on provided query parameters.
**Query:** **Query:**
* `name?`
* `type?` - `name?`
* `value?` - `type?`
- `value?`
**Response:** **Response:**
```typescript ```typescript
[ [
[index]: { [index]: {
@ -87,10 +99,12 @@ Returns all of `:domain`'s DNS records or performs a search based on provided qu
] ]
``` ```
### `POST /zone/records/:domain` ### `PATCH /zone/records/{domain}`
Updates or marks for deletion a single or multiple DNS records of `:domain` at `index`. **Warning:** `setIndex` will cause your other records to shift around, so it is currently only recommended to use for a single record at a time.
Updates or marks for deletion a single or multiple DNS records of `{domain}` at `index`. **Warning:** `setIndex` will cause your other records to shift around, so it is currently only recommended to use for a single record at a time.
**Body:** **Body:**
```typescript ```typescript
{ {
record: { record: {
@ -105,6 +119,7 @@ Updates or marks for deletion a single or multiple DNS records of `:domain` at `
``` ```
**Response:** **Response:**
```typescript ```typescript
{ {
success: boolean; success: boolean;
@ -117,10 +132,12 @@ Updates or marks for deletion a single or multiple DNS records of `:domain` at `
} }
``` ```
### `PUT /zone/records/:domain` ### `POST /zone/records/{domain}`
Creates a single or multiple new DNS records for `:domain`.
Creates a single or multiple new DNS records for `{domain}`.
**Body:** **Body:**
```typescript ```typescript
{ {
record: { record: {
@ -133,6 +150,7 @@ Creates a single or multiple new DNS records for `:domain`.
``` ```
**Response:** **Response:**
```typescript ```typescript
{ {
success: boolean; success: boolean;
@ -145,10 +163,12 @@ Creates a single or multiple new DNS records for `:domain`.
} }
``` ```
### `DELETE /zone/records/:domain` ### `DELETE /zone/records/{domain}`
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!
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:** **Body:**
```typescript ```typescript
{ {
index: number | number[]; index: number | number[];
@ -156,6 +176,7 @@ Deletes a single or multiple DNS records from `:domain` at `index`. **Warning:**
``` ```
**Response:** **Response:**
```typescript ```typescript
{ {
success: boolean; success: boolean;
@ -168,10 +189,12 @@ Deletes a single or multiple DNS records from `:domain` at `index`. **Warning:**
} }
``` ```
### `POST /set-ip/:domain` ### `POST /set-ip/{domain}`
Quickly updates the `:domain`'s IP address (first occurences of `A` and `AAAA` records of `@` or `subdomain`). One of the IP addresses is taken from the request, so it's a good idea to use curl with `-4` to automatically set the IPv4 address and provide the IPv6 address with a body parameter.
Quickly updates the `{domain}`'s IP address (first occurences of `A` and `AAAA` records of `@` or `subdomain`). One of the IP addresses is taken from the request, so it's a good idea to use curl with `-4` to automatically set the IPv4 address and provide the IPv6 address with a body parameter.
**Body:** **Body:**
```typescript ```typescript
{ {
ipv4?: string; ipv4?: string;
@ -182,6 +205,7 @@ Quickly updates the `:domain`'s IP address (first occurences of `A` and `AAAA` r
``` ```
**Response:** **Response:**
```typescript ```typescript
{ {
success: boolean; success: boolean;

50
package-lock.json generated
View File

@ -1,5 +1,5 @@
{ {
"name": "icy-dyndns", "name": "icydns",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
@ -8,10 +8,12 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1", "express": "^4.17.1",
"express-async-errors": "^3.1.1" "express-async-errors": "^3.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.10",
"@types/express": "^4.17.11", "@types/express": "^4.17.11",
"@types/node": "^15.3.0", "@types/node": "^15.3.0",
"typescript": "^4.2.4" "typescript": "^4.2.4"
@ -36,6 +38,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/cors": {
"version": "2.8.10",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz",
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==",
"dev": true
},
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "4.17.11", "version": "4.17.11",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
@ -170,6 +178,18 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
}, },
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -401,6 +421,14 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/on-finished": { "node_modules/on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@ -610,6 +638,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/cors": {
"version": "2.8.10",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz",
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==",
"dev": true
},
"@types/express": { "@types/express": {
"version": "4.17.11", "version": "4.17.11",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
@ -726,6 +760,15 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
}, },
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": { "debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -904,6 +947,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",

View File

@ -13,10 +13,12 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1", "express": "^4.17.1",
"express-async-errors": "^3.1.1" "express-async-errors": "^3.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.10",
"@types/express": "^4.17.11", "@types/express": "^4.17.11",
"@types/node": "^15.3.0", "@types/node": "^15.3.0",
"typescript": "^4.2.4" "typescript": "^4.2.4"

View File

@ -1,4 +1,5 @@
import express, { ErrorRequestHandler, NextFunction, Request, RequestHandler, Response } from 'express'; import express, { ErrorRequestHandler, NextFunction, Request, RequestHandler, Response } from 'express';
import cors from 'cors';
import 'express-async-errors'; import 'express-async-errors';
import path from 'path'; import path from 'path';
import { DNSCache } from './dns/cache'; import { DNSCache } from './dns/cache';
@ -20,6 +21,11 @@ const api = express.Router();
app.use(express.json()); app.use(express.json());
app.enable('trust proxy'); app.enable('trust proxy');
app.use(cors({
origin: '*',
credentials: true
}));
const keys = new Keys(); const keys = new Keys();
const rndc = ReloadExecutor.fromEnvironment(); const rndc = ReloadExecutor.fromEnvironment();
const validator = new ValidatorExecutor(); const validator = new ValidatorExecutor();
@ -111,7 +117,7 @@ api.get('/zone/records/:domain', domainAuthorization, async (req, res) => {
* forDeletion?: boolean; * forDeletion?: boolean;
* }[]; * }[];
*/ */
api.post('/zone/records/:domain', domainAuthorization, async (req, res) => { api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain; const domain = req.params.domain;
let setters = req.body.record; let setters = req.body.record;
@ -283,7 +289,7 @@ api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => {
* index?: number; * index?: number;
* }[]; * }[];
*/ */
api.put('/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;
let setters = req.body.record; let setters = req.body.record;

View File

@ -15,7 +15,7 @@
// "sourceMap": true, /* Generates corresponding '.map' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */ // "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist", /* Redirect output structure to the directory. */ "outDir": "./dist", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */ // "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */ // "removeComments": true, /* Do not emit comments to output. */