From a965d8dec1eab8bc144ad275a2457597a2905831 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Fri, 13 Mar 2026 20:19:06 -0700 Subject: [PATCH 1/6] 3.0.0-alpha.7.0 --- package.json | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a8d72c0..37c10b7 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,18 @@ { "name": "@nictool/api", - "version": "3.0.0-alpha.7", + "version": "3.0.0-alpha.7.0", "description": "NicTool API", "main": "index.js", "type": "module", - "files": [ "CHANGELOG.md", "conf.d", "html", "lib", "routes", "sql", "server.js" ], + "files": [ + "CHANGELOG.md", + "conf.d", + "html", + "lib", + "routes", + "sql", + "server.js" + ], "scripts": { "format": "npm run lint:fix && npm run prettier:fix", "lint": "npx eslint *.js **/*.js", @@ -60,4 +68,4 @@ "singleQuote": true, "trailingComma": "all" } -} \ No newline at end of file +} From 6570978e0a5dc9cf2e7d6a72a8c39152eaea1420 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Sat, 14 Mar 2026 22:37:02 -0700 Subject: [PATCH 2/6] the changes --- CHANGELOG.md | 4 ++++ lib/nameserver.js | 4 ++-- lib/nameserver.test.js | 15 +++++++++++++++ lib/util.js | 4 ++-- lib/zone.js | 30 +++++++++++++++++++++++++----- lib/zone.test.js | 15 +++++++++++++++ routes/zone.js | 25 ++++++++++++++++++++++--- 7 files changed, 85 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c519682..fec6a7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Unreleased +- lib/zone: add limit option +- lib/nameserver.js: handle null fields from DB +- routes/zone: report zone name on validation failure + ### [3.0.0-alpha.7] - 2026-03-13 - fixes diff --git a/lib/nameserver.js b/lib/nameserver.js index 84a99f3..cc9fc3c 100644 --- a/lib/nameserver.js +++ b/lib/nameserver.js @@ -103,11 +103,11 @@ function dbToObject(rows) { } for (const f of ['export']) { for (const p of ['type', 'interval', 'serials', 'status']) { - if (row[`${f}_${p}`] !== undefined) { + if (![null, undefined].includes(row[`${f}_${p}`])) { if (row[f] === undefined) row[f] = {} row[f][p] = row[`${f}_${p}`] - delete row[`${f}_${p}`] } + delete row[`${f}_${p}`] } } } diff --git a/lib/nameserver.test.js b/lib/nameserver.test.js index 173a19c..b4d8113 100644 --- a/lib/nameserver.test.js +++ b/lib/nameserver.test.js @@ -33,6 +33,21 @@ describe('nameserver', function () { assert.ok(await Nameserver.put({ id: testCase.id, name: testCase.name })) }) + it('handles null export interval gracefully', async () => { + await Nameserver.mysql.execute( + 'UPDATE nt_nameserver SET export_interval = NULL WHERE nt_nameserver_id = ?', + [testCase.id], + ) + + const ns = await Nameserver.get({ id: testCase.id }) + assert.equal(ns[0].export.interval, undefined) + + await Nameserver.mysql.execute( + 'UPDATE nt_nameserver SET export_interval = ? WHERE nt_nameserver_id = ?', + [0, testCase.id], + ) + }) + it('deletes a nameserver', async () => { assert.ok(await Nameserver.delete({ id: testCase.id })) let g = await Nameserver.get({ id: testCase.id, deleted: 1 }) diff --git a/lib/util.js b/lib/util.js index 469b2eb..10945e8 100644 --- a/lib/util.js +++ b/lib/util.js @@ -7,8 +7,8 @@ function setEnv() { /* c8 ignore next 9 */ switch (os.hostname()) { - case 'mbp.simerson.net': - case 'imac27.simerson.net': + case 'mattbook-m3.home.simerson.net': + case 'imac27.home.simerson.net': process.env.NODE_ENV = 'development' break default: diff --git a/lib/zone.js b/lib/zone.js index 44e065e..e3250b0 100644 --- a/lib/zone.js +++ b/lib/zone.js @@ -22,9 +22,13 @@ class Zone { args = JSON.parse(JSON.stringify(args)) if (args.deleted === undefined) args.deleted = false - const rows = await Mysql.execute( - ...Mysql.select( - `SELECT nt_zone_id AS id + const limit = Number.isInteger(args.limit) ? args.limit : undefined + delete args.limit + + const sqlLimit = limit === undefined ? '' : ` LIMIT ${Math.max(1, limit)}` + + const [query, params] = Mysql.select( + `SELECT nt_zone_id AS id , nt_group_id AS gid , zone , mailaddr @@ -39,9 +43,10 @@ class Zone { , last_publish , deleted FROM nt_zone`, - mapToDbColumn(args, zoneDbMap), - ), + mapToDbColumn(args, zoneDbMap), ) + + const rows = await Mysql.execute(`${query}${sqlLimit}`, params) for (const row of rows) { for (const b of boolFields) { row[b] = row[b] === 1 @@ -49,6 +54,21 @@ class Zone { for (const f of ['description', 'location']) { if ([null].includes(row[f])) row[f] = '' } + + // Coerce legacy DB NULLs to sane defaults so responses validate + const zoneDefaults = { + minimum: 3600, + ttl: 3600, + refresh: 86400, + retry: 7200, + expire: 1209600, + } + for (const [f, val] of Object.entries(zoneDefaults)) { + if ([null, undefined].includes(row[f])) row[f] = val + } + + if ([null, undefined].includes(row.serial)) row.serial = 0 + if (row['last_publish'] === undefined) delete row['last_publish'] if (/00:00:00/.test(row['last_publish'])) row['last_publish'] = null if (args.deleted === false) delete row.deleted diff --git a/lib/zone.test.js b/lib/zone.test.js index c4be676..72f3dcc 100644 --- a/lib/zone.test.js +++ b/lib/zone.test.js @@ -35,6 +35,21 @@ describe('zone', function () { assert.ok(await Zone.put({ id: testCase.id, mailaddr: testCase.mailaddr })) }) + it('handles null minimum gracefully', async () => { + await Zone.mysql.execute( + 'UPDATE nt_zone SET minimum = NULL WHERE nt_zone_id = ?', + [testCase.id], + ) + + const z = await Zone.get({ id: testCase.id }) + assert.equal(z[0].minimum, 3600) + + await Zone.mysql.execute( + 'UPDATE nt_zone SET minimum = ? WHERE nt_zone_id = ?', + [testCase.minimum, testCase.id], + ) + }) + describe('deletes a zone', async () => { it('can delete a zone', async () => { assert.ok(await Zone.delete({ id: testCase.id })) diff --git a/routes/zone.js b/routes/zone.js index d046c0b..90c5f53 100644 --- a/routes/zone.js +++ b/routes/zone.js @@ -3,6 +3,24 @@ import validate from '@nictool/validate' import Zone from '../lib/zone.js' import { meta } from '../lib/util.js' +function zoneResponseFailAction(request, h, err) { + const detail = err?.details?.find((d) => + Array.isArray(d.path) && d.path[0] === 'zone' && d.path[2] === 'zone', + ) + + if (detail) { + const index = detail.path[1] + const badZone = request.response?.source?.zone?.[index]?.zone + const badId = request.response?.source?.zone?.[index]?.id + + if (badZone !== undefined) { + err.message = `${err.message}. Invalid zone value: "${badZone}" (id: ${badId ?? 'unknown'})` + } + } + + throw err +} + function ZoneRoutes(server) { server.route([ { @@ -15,13 +33,14 @@ function ZoneRoutes(server) { }, response: { schema: validate.zone.GET_res, - failAction: 'log', + failAction: zoneResponseFailAction, }, tags: ['api'], }, handler: async (request, h) => { const getArgs = { deleted: request.query.deleted === true ? 1 : 0, + limit: 1000, } if (request.params.id) getArgs.id = parseInt(request.params.id, 10) @@ -48,7 +67,7 @@ function ZoneRoutes(server) { }, response: { schema: validate.zone.GET_res, - failAction: 'log', + failAction: zoneResponseFailAction, }, tags: ['api'], }, @@ -78,7 +97,7 @@ function ZoneRoutes(server) { }, response: { schema: validate.zone.GET_res, - failAction: 'log', + failAction: zoneResponseFailAction, }, tags: ['api'], }, From 538586e57cfe729208959ac72ea7bdc735b2fede Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Sat, 14 Mar 2026 22:37:16 -0700 Subject: [PATCH 3/6] doc(CONTRIBUTORS): updated --- .release | 2 +- CONTRIBUTORS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.release b/.release index d106d83..648aa35 160000 --- a/.release +++ b/.release @@ -1 +1 @@ -Subproject commit d106d83e69d5d8c99e4d6be4ba98ddde550d082b +Subproject commit 648aa356cba200c1a46f4bda9e29871d1e9ba557 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index a489471..3547fbb 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -2,7 +2,7 @@ This handcrafted artisanal software is brought to you by: -|
msimerson (14)| +|
msimerson (15)| | :---: | this file is generated by [.release](https://github.com/msimerson/.release). From 194f18499fe204e0ea9e46e9a656062ceb1eda78 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Sat, 14 Mar 2026 22:38:11 -0700 Subject: [PATCH 4/6] update CHANGELOG --- CHANGELOG.md | 5 +++++ package.json | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fec6a7c..1239ae2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Unreleased +### [3.0.0-alpha.8] - 2026-03-14 + - lib/zone: add limit option - lib/nameserver.js: handle null fields from DB - routes/zone: report zone name on validation failure @@ -59,3 +61,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). [3.0.0-alpha.5]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.5 [3.0.0-alpha.6]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.6 [3.0.0-alpha.7]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.7 +[3.0.0-alpha.7.1]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.7.1 +[3.0.0]: https://github.com/NicTool/api/releases/tag/3.0.0 +[3.0.0-alpha.7.0]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.7.0 diff --git a/package.json b/package.json index 37c10b7..9839ae0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nictool/api", - "version": "3.0.0-alpha.7.0", + "version": "3.0.0-alpha.7.1", "description": "NicTool API", "main": "index.js", "type": "module", @@ -68,4 +68,4 @@ "singleQuote": true, "trailingComma": "all" } -} +} \ No newline at end of file From a3083f8f37c8957b34c92da1b5d484d2f2a34a49 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Sat, 14 Mar 2026 22:38:19 -0700 Subject: [PATCH 5/6] chore: format --- lib/zone.test.js | 13 +++++-------- routes/zone.js | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/zone.test.js b/lib/zone.test.js index 72f3dcc..99079d6 100644 --- a/lib/zone.test.js +++ b/lib/zone.test.js @@ -36,18 +36,15 @@ describe('zone', function () { }) it('handles null minimum gracefully', async () => { - await Zone.mysql.execute( - 'UPDATE nt_zone SET minimum = NULL WHERE nt_zone_id = ?', - [testCase.id], - ) + await Zone.mysql.execute('UPDATE nt_zone SET minimum = NULL WHERE nt_zone_id = ?', [testCase.id]) const z = await Zone.get({ id: testCase.id }) assert.equal(z[0].minimum, 3600) - await Zone.mysql.execute( - 'UPDATE nt_zone SET minimum = ? WHERE nt_zone_id = ?', - [testCase.minimum, testCase.id], - ) + await Zone.mysql.execute('UPDATE nt_zone SET minimum = ? WHERE nt_zone_id = ?', [ + testCase.minimum, + testCase.id, + ]) }) describe('deletes a zone', async () => { diff --git a/routes/zone.js b/routes/zone.js index 90c5f53..ea233dc 100644 --- a/routes/zone.js +++ b/routes/zone.js @@ -4,8 +4,8 @@ import Zone from '../lib/zone.js' import { meta } from '../lib/util.js' function zoneResponseFailAction(request, h, err) { - const detail = err?.details?.find((d) => - Array.isArray(d.path) && d.path[0] === 'zone' && d.path[2] === 'zone', + const detail = err?.details?.find( + (d) => Array.isArray(d.path) && d.path[0] === 'zone' && d.path[2] === 'zone', ) if (detail) { From 779c5908191c5639151d937d30c5bccf6dcfb8d2 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Sat, 14 Mar 2026 22:41:00 -0700 Subject: [PATCH 6/6] update CHANGELOG --- CHANGELOG.md | 6 ++---- package.json | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1239ae2..c1f65fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). - feat(lib/nameserver): added, with tests - feat(routes/nameserver): added, with tests -### 3.0.0-alpha.3 +### [3.0.0-alpha.3] - routes/permission: added GET, POST, DELETE - permission.get: default search with deleted=0 @@ -61,6 +61,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). [3.0.0-alpha.5]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.5 [3.0.0-alpha.6]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.6 [3.0.0-alpha.7]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.7 -[3.0.0-alpha.7.1]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.7.1 -[3.0.0]: https://github.com/NicTool/api/releases/tag/3.0.0 -[3.0.0-alpha.7.0]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.7.0 +[3.0.0-alpha.8]: https://github.com/NicTool/api/releases/tag/v3.0.0-alpha.8 diff --git a/package.json b/package.json index 9839ae0..c9ef7ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nictool/api", - "version": "3.0.0-alpha.7.1", + "version": "3.0.0-alpha.8", "description": "NicTool API", "main": "index.js", "type": "module",