updates, use param handler
This commit is contained in:
parent
0cb08ca596
commit
6724e8724a
57
package-lock.json
generated
57
package-lock.json
generated
@ -5,6 +5,7 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
|
"name": "icydns",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -14,11 +15,11 @@
|
|||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.10",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/express": "^4.17.11",
|
"@types/express": "^4.17.13",
|
||||||
"@types/node": "^15.3.0",
|
"@types/node": "^16.7.10",
|
||||||
"@types/uuid": "^8.3.1",
|
"@types/uuid": "^8.3.1",
|
||||||
"typescript": "^4.2.4"
|
"typescript": "^4.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
@ -41,15 +42,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/cors": {
|
"node_modules/@types/cors": {
|
||||||
"version": "2.8.10",
|
"version": "2.8.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
|
||||||
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==",
|
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/express": {
|
"node_modules/@types/express": {
|
||||||
"version": "4.17.11",
|
"version": "4.17.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||||
"integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==",
|
"integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
@ -76,9 +77,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "15.3.0",
|
"version": "16.7.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz",
|
||||||
"integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==",
|
"integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
@ -589,9 +590,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.2.4",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
||||||
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
|
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@ -655,15 +656,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/cors": {
|
"@types/cors": {
|
||||||
"version": "2.8.10",
|
"version": "2.8.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
|
||||||
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==",
|
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/express": {
|
"@types/express": {
|
||||||
"version": "4.17.11",
|
"version": "4.17.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||||
"integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==",
|
"integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
@ -690,9 +691,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "15.3.0",
|
"version": "16.7.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz",
|
||||||
"integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==",
|
"integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/qs": {
|
"@types/qs": {
|
||||||
@ -1095,9 +1096,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.2.4",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
||||||
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
|
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.10",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/express": "^4.17.11",
|
"@types/express": "^4.17.13",
|
||||||
"@types/node": "^15.3.0",
|
"@types/node": "^16.7.10",
|
||||||
"@types/uuid": "^8.3.1",
|
"@types/uuid": "^8.3.1",
|
||||||
"typescript": "^4.2.4"
|
"typescript": "^4.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,12 +87,12 @@ export class DNSCache {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.validator.validateAndSave(name, zone);
|
await this.validator.validateAndSave(name, zone);
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
// Reload previous state
|
// Reload previous state
|
||||||
if (e.message.contains('Validation')) {
|
if (e.message.contains('Validation')) {
|
||||||
await this.load(name, zone.file);
|
await this.load(name, zone.file);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e as Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,8 +122,8 @@ export class DNSCache {
|
|||||||
if (!skipReload) {
|
if (!skipReload) {
|
||||||
try {
|
try {
|
||||||
await this.rndc.reload(name);
|
await this.rndc.reload(name);
|
||||||
} catch (e) {
|
} catch (e: unknown) {
|
||||||
logger.warn('%s automatic zone reload failed:', name, e.stack);
|
logger.warn('%s automatic zone reload failed:', name, (e as Error).stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
src/index.ts
56
src/index.ts
@ -1,4 +1,10 @@
|
|||||||
import express, { ErrorRequestHandler, NextFunction, Request, RequestHandler, Response } from 'express';
|
import express, {
|
||||||
|
ErrorRequestHandler,
|
||||||
|
NextFunction,
|
||||||
|
Request,
|
||||||
|
RequestHandler,
|
||||||
|
Response
|
||||||
|
} from 'express';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import 'express-async-errors';
|
import 'express-async-errors';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -20,7 +26,7 @@ const dir = process.env.ZONEFILES || '.';
|
|||||||
const app = express();
|
const app = express();
|
||||||
const api = express.Router();
|
const api = express.Router();
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json() as RequestHandler);
|
||||||
app.enable('trust proxy');
|
app.enable('trust proxy');
|
||||||
|
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
@ -73,12 +79,13 @@ api.use((req, res, next) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const domainAuthorization: RequestHandler = (req, res, next) => {
|
const domainAuthorization: RequestHandler = (req, res, next) => {
|
||||||
if (!req.params.domain || !res.locals.token) {
|
const domain = res.locals.domain || req.params.domain;
|
||||||
|
if (!domain || !res.locals.token) {
|
||||||
next(new Error('Unexpected bad request'));
|
next(new Error('Unexpected bad request'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keys.getDomain(res.locals.token) !== req.params.domain) {
|
if (keys.getDomain(res.locals.token) !== domain) {
|
||||||
res.status(401).json({ success: false, message: 'Unauthorized access to domain' });
|
res.status(401).json({ success: false, message: 'Unauthorized access to domain' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -86,12 +93,18 @@ const domainAuthorization: RequestHandler = (req, res, next) => {
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
api.param('domain', async (_req, res, next, id) => {
|
||||||
|
const cached = await getOrLoad(id);
|
||||||
|
res.locals.cached = cached;
|
||||||
|
res.locals.domain = id;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get zone records
|
* Get zone records
|
||||||
*/
|
*/
|
||||||
api.get('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
api.get('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const { cached } = res.locals;
|
||||||
const cached = await getOrLoad(domain);
|
|
||||||
|
|
||||||
const type = req.query.type as DNSRecordType;
|
const type = req.query.type as DNSRecordType;
|
||||||
const name = req.query.name as string;
|
const name = req.query.name as string;
|
||||||
@ -120,7 +133,7 @@ api.get('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
* }[];
|
* }[];
|
||||||
*/
|
*/
|
||||||
api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const { domain, cached } = res.locals;
|
||||||
let setters = req.body.record;
|
let setters = req.body.record;
|
||||||
|
|
||||||
if (!setters) {
|
if (!setters) {
|
||||||
@ -132,8 +145,7 @@ api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
setters = [setters];
|
setters = [setters];
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = await getOrLoad(domain);
|
const { zone } = cached as CachedZone;
|
||||||
const { zone } = cached;
|
|
||||||
|
|
||||||
const changed = [];
|
const changed = [];
|
||||||
const errors = [];
|
const errors = [];
|
||||||
@ -237,11 +249,10 @@ api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
* index: number;
|
* index: number;
|
||||||
*/
|
*/
|
||||||
api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const { domain, cached } = res.locals;
|
||||||
let indexes = req.body.index;
|
let indexes = req.body.index;
|
||||||
|
|
||||||
const cached = await getOrLoad(domain);
|
const { zone } = cached as CachedZone;
|
||||||
const { zone } = cached;
|
|
||||||
|
|
||||||
if (!Array.isArray(indexes)) {
|
if (!Array.isArray(indexes)) {
|
||||||
indexes = [indexes];
|
indexes = [indexes];
|
||||||
@ -296,7 +307,7 @@ api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
* }[];
|
* }[];
|
||||||
*/
|
*/
|
||||||
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, cached } = res.locals;
|
||||||
let setters = req.body.record;
|
let setters = req.body.record;
|
||||||
|
|
||||||
if (!setters) {
|
if (!setters) {
|
||||||
@ -307,8 +318,7 @@ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
setters = [setters];
|
setters = [setters];
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = await getOrLoad(domain);
|
const { zone } = cached as CachedZone;
|
||||||
const { zone } = cached;
|
|
||||||
|
|
||||||
const created = [];
|
const created = [];
|
||||||
const errors = [];
|
const errors = [];
|
||||||
@ -397,8 +407,7 @@ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
|||||||
* Get full zone as file
|
* Get full zone as file
|
||||||
*/
|
*/
|
||||||
api.get('/zone/:domain/download', domainAuthorization, async (req, res) => {
|
api.get('/zone/:domain/download', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const { cached } = res.locals;
|
||||||
const cached = await getOrLoad(domain);
|
|
||||||
res.send(createZoneFile(cached.zone).join('\n'));
|
res.send(createZoneFile(cached.zone).join('\n'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -406,8 +415,7 @@ api.get('/zone/:domain/download', domainAuthorization, async (req, res) => {
|
|||||||
* Get full zone
|
* Get full zone
|
||||||
*/
|
*/
|
||||||
api.get('/zone/:domain', domainAuthorization, async (req, res) => {
|
api.get('/zone/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const { cached } = res.locals;
|
||||||
const cached = await getOrLoad(domain);
|
|
||||||
res.json(cached.zone);
|
res.json(cached.zone);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -416,8 +424,7 @@ api.get('/zone/:domain', domainAuthorization, async (req, res) => {
|
|||||||
* ttl?: number
|
* ttl?: number
|
||||||
*/
|
*/
|
||||||
api.post('/zone/:domain', domainAuthorization, async (req, res) => {
|
api.post('/zone/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const { domain, cached } = res.locals;
|
||||||
const cached = await getOrLoad(domain);
|
|
||||||
|
|
||||||
if (req.body.ttl) {
|
if (req.body.ttl) {
|
||||||
const numTTL = parseInt(req.body.TTL, 10);
|
const numTTL = parseInt(req.body.TTL, 10);
|
||||||
@ -447,7 +454,7 @@ api.post('/zone/:domain', domainAuthorization, async (req, res) => {
|
|||||||
* dualRequest?: boolean;
|
* dualRequest?: boolean;
|
||||||
*/
|
*/
|
||||||
api.post('/set-ip/:domain', domainAuthorization, async (req, res) => {
|
api.post('/set-ip/:domain', domainAuthorization, async (req, res) => {
|
||||||
const domain = req.params.domain;
|
const { domain, cached } = res.locals;
|
||||||
const subdomain = req.body.subdomain || '@';
|
const subdomain = req.body.subdomain || '@';
|
||||||
const waitPartial = req.body.dualRequest === true;
|
const waitPartial = req.body.dualRequest === true;
|
||||||
const { v4, v6 } = fromRequest(req);
|
const { v4, v6 } = fromRequest(req);
|
||||||
@ -456,8 +463,7 @@ api.post('/set-ip/:domain', domainAuthorization, async (req, res) => {
|
|||||||
res.json({ success: true, message: 'Nothing to do.' });
|
res.json({ success: true, message: 'Nothing to do.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = await getOrLoad(domain);
|
const { zone } = cached as CachedZone;
|
||||||
const { zone } = cached;
|
|
||||||
const actions: string[] = [];
|
const actions: string[] = [];
|
||||||
|
|
||||||
if (v4) {
|
if (v4) {
|
||||||
@ -529,7 +535,7 @@ api.post('/set-ip/:domain', domainAuthorization, async (req, res) => {
|
|||||||
logger.info('zone %s set-ip from %s: %s', domain, req.ip, actions.join('\n'));
|
logger.info('zone %s set-ip from %s: %s', domain, req.ip, actions.join('\n'));
|
||||||
});
|
});
|
||||||
|
|
||||||
const errorHandler: ErrorRequestHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
|
const errorHandler: ErrorRequestHandler = (err: any, _req: Request, res: Response, _next: NextFunction) => {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err.message
|
message: err.message
|
||||||
|
@ -10,8 +10,8 @@ export class Keys {
|
|||||||
let content = '{}';
|
let content = '{}';
|
||||||
try {
|
try {
|
||||||
content = await fs.readFile(file, { encoding: 'utf-8' });
|
content = await fs.readFile(file, { encoding: 'utf-8' });
|
||||||
} catch (e) {
|
} catch (e: unknown) {
|
||||||
if (e.message.includes('ENOENT')) {
|
if ((e as Error).message.includes('ENOENT')) {
|
||||||
fs.writeFile(file, '{}');
|
fs.writeFile(file, '{}');
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
|
Reference in New Issue
Block a user