updates, use param handler

This commit is contained in:
Evert Prants 2021-09-04 10:59:56 +03:00
parent 0cb08ca596
commit 6724e8724a
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
5 changed files with 70 additions and 63 deletions

57
package-lock.json generated
View File

@ -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": {

View File

@ -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"
} }
} }

View File

@ -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);
} }
} }
} }

View File

@ -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

View File

@ -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;