diff --git a/package-lock.json b/package-lock.json index a3deb67..1e4580f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "icydns", "version": "1.0.0", "license": "ISC", "dependencies": { @@ -14,11 +15,11 @@ "uuid": "^8.3.2" }, "devDependencies": { - "@types/cors": "^2.8.10", - "@types/express": "^4.17.11", - "@types/node": "^15.3.0", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.13", + "@types/node": "^16.7.10", "@types/uuid": "^8.3.1", - "typescript": "^4.2.4" + "typescript": "^4.4.2" } }, "node_modules/@types/body-parser": { @@ -41,15 +42,15 @@ } }, "node_modules/@types/cors": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", - "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", - "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -76,9 +77,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz", - "integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==", + "version": "16.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", + "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", "dev": true }, "node_modules/@types/qs": { @@ -589,9 +590,9 @@ } }, "node_modules/typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -655,15 +656,15 @@ } }, "@types/cors": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", - "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, "@types/express": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", - "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", "dev": true, "requires": { "@types/body-parser": "*", @@ -690,9 +691,9 @@ "dev": true }, "@types/node": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz", - "integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==", + "version": "16.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", + "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", "dev": true }, "@types/qs": { @@ -1095,9 +1096,9 @@ } }, "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", "dev": true }, "unpipe": { diff --git a/package.json b/package.json index d688892..2e13ada 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,10 @@ "uuid": "^8.3.2" }, "devDependencies": { - "@types/cors": "^2.8.10", - "@types/express": "^4.17.11", - "@types/node": "^15.3.0", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.13", + "@types/node": "^16.7.10", "@types/uuid": "^8.3.1", - "typescript": "^4.2.4" + "typescript": "^4.4.2" } } diff --git a/src/dns/cache.ts b/src/dns/cache.ts index 29d8717..ecb6ae9 100644 --- a/src/dns/cache.ts +++ b/src/dns/cache.ts @@ -87,12 +87,12 @@ export class DNSCache { try { await this.validator.validateAndSave(name, zone); - } catch (e) { + } catch (e: any) { // Reload previous state if (e.message.contains('Validation')) { await this.load(name, zone.file); } - throw e; + throw e as Error; } } @@ -122,8 +122,8 @@ export class DNSCache { if (!skipReload) { try { await this.rndc.reload(name); - } catch (e) { - logger.warn('%s automatic zone reload failed:', name, e.stack); + } catch (e: unknown) { + logger.warn('%s automatic zone reload failed:', name, (e as Error).stack); } } } diff --git a/src/index.ts b/src/index.ts index 9de9798..3205494 100644 --- a/src/index.ts +++ b/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 'express-async-errors'; import path from 'path'; @@ -20,7 +26,7 @@ const dir = process.env.ZONEFILES || '.'; const app = express(); const api = express.Router(); -app.use(express.json()); +app.use(express.json() as RequestHandler); app.enable('trust proxy'); app.use(cors({ @@ -73,12 +79,13 @@ api.use((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')); 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' }); return; } @@ -86,12 +93,18 @@ const domainAuthorization: RequestHandler = (req, res, 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 */ api.get('/zone/records/:domain', domainAuthorization, async (req, res) => { - const domain = req.params.domain; - const cached = await getOrLoad(domain); + const { cached } = res.locals; const type = req.query.type as DNSRecordType; 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) => { - const domain = req.params.domain; + const { domain, cached } = res.locals; let setters = req.body.record; if (!setters) { @@ -132,8 +145,7 @@ api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => { setters = [setters]; } - const cached = await getOrLoad(domain); - const { zone } = cached; + const { zone } = cached as CachedZone; const changed = []; const errors = []; @@ -237,11 +249,10 @@ api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => { * index: number; */ api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => { - const domain = req.params.domain; + const { domain, cached } = res.locals; let indexes = req.body.index; - const cached = await getOrLoad(domain); - const { zone } = cached; + const { zone } = cached as CachedZone; if (!Array.isArray(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) => { - const domain = req.params.domain; + const { domain, cached } = res.locals; let setters = req.body.record; if (!setters) { @@ -307,8 +318,7 @@ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => { setters = [setters]; } - const cached = await getOrLoad(domain); - const { zone } = cached; + const { zone } = cached as CachedZone; const created = []; const errors = []; @@ -397,8 +407,7 @@ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => { * Get full zone as file */ api.get('/zone/:domain/download', domainAuthorization, async (req, res) => { - const domain = req.params.domain; - const cached = await getOrLoad(domain); + const { cached } = res.locals; res.send(createZoneFile(cached.zone).join('\n')); }); @@ -406,8 +415,7 @@ api.get('/zone/:domain/download', domainAuthorization, async (req, res) => { * Get full zone */ api.get('/zone/:domain', domainAuthorization, async (req, res) => { - const domain = req.params.domain; - const cached = await getOrLoad(domain); + const { cached } = res.locals; res.json(cached.zone); }); @@ -416,8 +424,7 @@ api.get('/zone/:domain', domainAuthorization, async (req, res) => { * ttl?: number */ api.post('/zone/:domain', domainAuthorization, async (req, res) => { - const domain = req.params.domain; - const cached = await getOrLoad(domain); + const { domain, cached } = res.locals; if (req.body.ttl) { const numTTL = parseInt(req.body.TTL, 10); @@ -447,7 +454,7 @@ api.post('/zone/:domain', domainAuthorization, async (req, res) => { * dualRequest?: boolean; */ 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 waitPartial = req.body.dualRequest === true; 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.' }); } - const cached = await getOrLoad(domain); - const { zone } = cached; + const { zone } = cached as CachedZone; const actions: string[] = []; 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')); }); -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({ success: false, message: err.message diff --git a/src/keys.ts b/src/keys.ts index 716a624..51719ee 100644 --- a/src/keys.ts +++ b/src/keys.ts @@ -10,8 +10,8 @@ export class Keys { let content = '{}'; try { content = await fs.readFile(file, { encoding: 'utf-8' }); - } catch (e) { - if (e.message.includes('ENOENT')) { + } catch (e: unknown) { + if ((e as Error).message.includes('ENOENT')) { fs.writeFile(file, '{}'); } else { throw e;