cors, change http methods
This commit is contained in:
parent
621d6b812c
commit
275ae6460d
80
README.md
80
README.md
@ -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
50
package-lock.json
generated
@ -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",
|
||||||
|
@ -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"
|
||||||
|
10
src/index.ts
10
src/index.ts
@ -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;
|
||||||
|
|
||||||
|
@ -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. */
|
||||||
|
Reference in New Issue
Block a user