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,
|
||||
"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": {
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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 '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
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user