From 5a4e94c5487c4b8bea80e28add781aa33bcd46bb Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Thu, 27 Mar 2025 16:26:05 -0700 Subject: [PATCH 1/2] dep(eslint): upgrade to v9 - dep(\*): bump versions to latest - feat(conf.d/http): added tls - feat(session): added JWT for session auth - feat(zone): removed location - feat(routes/zone_record): added, fixes #17 - change(routes/users): result is always array - change(routes/ns): GET id is optional, result is always array - change(routes/zone): GET id is optional, result is always array --- .eslintrc.yaml | 15 ----- .github/workflows/ci.yml | 22 +++---- .release | 2 +- CHANGELOG.md | 12 ++++ README.md | 2 +- conf.d/http.yml | 35 ++++++++++- eslint.config.mjs | 39 ++++++++++++ lib/config.js | 1 + lib/config.test.js | 9 ++- lib/session.js | 3 + lib/test/zone.json | 1 - lib/zone.js | 1 - lib/zone_record.js | 12 ++-- package.json | 30 +++++----- routes/group.test.js | 42 ++++--------- routes/index.js | 46 +++++++++++---- routes/nameserver.js | 16 +++-- routes/nameserver.test.js | 53 +++++------------ routes/permission.js | 6 ++ routes/permission.test.js | 39 ++++-------- routes/session.js | 50 ++++++++++++---- routes/session.test.js | 18 ++---- routes/test/zone.json | 3 +- routes/user.js | 24 +++++--- routes/user.test.js | 49 ++++----------- routes/zone.js | 18 ++++-- routes/zone.test.js | 48 +++++---------- routes/zone_record.js | 121 ++++++++++++++++++++++++++++++++++++++ sql/init-mysql.sh | 11 ++-- test.sh | 11 ++-- 30 files changed, 455 insertions(+), 284 deletions(-) delete mode 100644 .eslintrc.yaml create mode 100644 eslint.config.mjs create mode 100644 routes/zone_record.js diff --git a/.eslintrc.yaml b/.eslintrc.yaml deleted file mode 100644 index 4576447..0000000 --- a/.eslintrc.yaml +++ /dev/null @@ -1,15 +0,0 @@ -env: - node: true - es6: true - es2021: true - es2024: true -extends: eslint:recommended -parser: '@babel/eslint-parser' -parserOptions: - babelOptions: - configFile: false - plugins: [ "@babel/plugin-syntax-import-attributes" ] - requireConfigFile: false - ecmaVersion: latest - sourceType: module -rules: {} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 181e49c..3ab87f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: env: CI: true NODE_ENV: test + NODE_VER: 22 jobs: lint: @@ -21,7 +22,7 @@ jobs: run: sudo /etc/init.d/mysql start - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: ${{ env.NODE_VER }} - uses: actions/checkout@v4 - run: npm install - name: Initialize MySQL @@ -43,8 +44,11 @@ jobs: - id: get uses: msimerson/node-lts-versions@v1 outputs: - lts: ${{ steps.get.outputs.lts }} active: ${{ steps.get.outputs.active }} + maintenance: ${{ steps.get.outputs.maintenance }} + lts: ${{ steps.get.outputs.lts }} + current: ${{ steps.get.outputs.current }} + min: ${{ steps.get.outputs.min }} test: needs: [ get-lts ] @@ -52,8 +56,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - # node-version: ${{ fromJson(needs.get-lts.outputs.active) }} - node-version: [ 20 ] + node-version: ${{ fromJson(needs.get-lts.outputs.active) }} fail-fast: false steps: - run: sudo /etc/init.d/mysql start @@ -70,15 +73,13 @@ jobs: runs-on: macos-latest strategy: matrix: - # node-version: ${{ fromJson(needs.get-lts.outputs.active) }} - node-version: [ 20 ] + node-version: ${{ fromJson(needs.get-lts.outputs.active) }} fail-fast: false steps: - name: Install & Start MySQL run: | - brew install mysql - brew tap homebrew/services - brew services start mysql + brew install mysql@8.4 + brew services start mysql@8.4 - uses: actions/checkout@v4 - uses: actions/setup-node@v4 name: Node ${{ matrix.node-version }} on ${{ matrix.os }} @@ -94,8 +95,7 @@ jobs: runs-on: windows-latest strategy: matrix: - node-version: [ 20 ] - # node-version: ${{ fromJson(needs.get-lts.outputs.active) }} + node-version: ${{ fromJson(needs.get-lts.outputs.active) }} experimental: [true] fail-fast: false steps: diff --git a/.release b/.release index 704e50c..7307651 160000 --- a/.release +++ b/.release @@ -1 +1 @@ -Subproject commit 704e50ce14387d29293bd208d0a6ff290b49d66e +Subproject commit 73076513e83c2057a32515831b638771c15b1d83 diff --git a/CHANGELOG.md b/CHANGELOG.md index c0e8b08..c1b7847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ### Unreleased +### [3.0.0-alpha.6] - 2025-04-08 + +- dep(eslint): upgraded to v9 +- dep(\*): bump versions to latest +- feat(conf.d/http): added tls +- feat(session): added JWT for session auth +- feat(zone): removed location +- feat(routes/zone_record): added, fixes #17 +- change(routes/users): result is always array +- change(routes/ns): GET id is optional, result is always array +- change(routes/zone): GET id is optional, result is always array ### [3.0.0-alpha.5] - 2024-03-06 @@ -31,3 +42,4 @@ [3.0.0-alpha.3]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.3 [3.0.0-alpha.4]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.4 [3.0.0-alpha.5]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.5 +[3.0.0-alpha.6]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.6 diff --git a/README.md b/README.md index 4e43dd7..89c71ac 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Running one of these commands: `npm run start (production)` -or +or `npm run develop (development)` diff --git a/conf.d/http.yml b/conf.d/http.yml index acd4d46..4d2a673 100644 --- a/conf.d/http.yml +++ b/conf.d/http.yml @@ -1,6 +1,11 @@ default: host: localhost port: 3000 + tls: + key: null + cert: null + jwt: + key: 'af1b926a5e21f535c4f5b6c42941c4cf' cookie: # https://hapi.dev/module/cookie/api/?v=12.0.1 name: sid-nictool @@ -11,7 +16,7 @@ default: clearInvalid: true isSameSite: Strict isSecure: true - isHttpOnly: true + isHttpOnly: false keepAlive: false # redirectTo: group: NicTool @@ -28,6 +33,32 @@ test: password: ^NicTool.Is,The#Best_Dns-Manager$ development: + host: box-under-my-desk.example.com + tls: + key: | + -----BEGIN PRIVATE KEY----- + MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDwBx1Qt9309i89 + O9Y8bhHO9BqyWWzd0hXI1o3d8Zn4aT2lhwmeeu2oSQsczvny0cJSs6HYe6asI6XZ + + Ane1BnOJ6/E+7Clo463N++OS + -----END PRIVATE KEY----- + + cert: | + -----BEGIN CERTIFICATE----- + MIID9DCCAtygAwIBAgIUF+ziLgjIA3qCf95DmVskHqSNvLUwDQYJKoZIhvcNAQEL + BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xGjAYBgNVBAoM + + juZxYqQoPYBpk+eG/sudGGFKKGow1RbGbbNUrqATYxJCqPrN0mZuNkAgATbQtBjS + vyvASCDueS0= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIID2TCCAsGgAwIBAgIUF+ziLgjIA3qCf95DmVskHqSNvLEwDQYJKoZIhvcNAQEL + BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xGjAYBgNVBAoM + + iMt4AE3zfKgj/OLyAeseUlqukbnBQYlTiMUuPLTTp6d7uBi8/VuXBTrZ9nafPvSZ + TqccpFMgxCeImsJCgO5hBJYUTELDNEmJS5Vgy3Y= + -----END CERTIFICATE----- + cookie: - isSecure: false + # isSecure: false password: ^NicTool.Is,The#Best_Dns-Manager$ diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..3ed7310 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,39 @@ +import globals from "globals"; +import babelParser from "@babel/eslint-parser"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import js from "@eslint/js"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +export default [...compat.extends("eslint:recommended"), { + languageOptions: { + globals: { + ...globals.node, + }, + + parser: babelParser, + ecmaVersion: "latest", + sourceType: "module", + + parserOptions: { + babelOptions: { + configFile: false, + plugins: ["@babel/plugin-syntax-import-attributes"], + }, + + requireConfigFile: false, + }, + }, + + rules: { + "no-unused-vars": "warn" + }, +}]; diff --git a/lib/config.js b/lib/config.js index d4f4581..08e6962 100644 --- a/lib/config.js +++ b/lib/config.js @@ -40,6 +40,7 @@ class Config { const str = fsSync.readFileSync(`./conf.d/${name}.yml`, 'utf8') const cfg = YAML.parse(str) + if (this.debug) console.debug(cfg) this.cfg[cacheKey] = applyDefaults(cfg[env ?? this.env], cfg.default) return this.cfg[cacheKey] diff --git a/lib/config.test.js b/lib/config.test.js index 3b82ee0..d223605 100644 --- a/lib/config.test.js +++ b/lib/config.test.js @@ -68,7 +68,7 @@ const httpCfg = { port: 3000, cookie: { clearInvalid: true, - isHttpOnly: true, + isHttpOnly: false, isSameSite: 'Strict', isSecure: false, name: 'sid-nictool', @@ -76,6 +76,13 @@ const httpCfg = { path: '/', ttl: 3600000, }, + jwt: { + key: 'af1b926a5e21f535c4f5b6c42941c4cf', + }, + tls: { + cert: null, + key: null, + }, keepAlive: false, group: 'NicTool', } diff --git a/lib/session.js b/lib/session.js index a5d189e..31fd11e 100644 --- a/lib/session.js +++ b/lib/session.js @@ -53,9 +53,12 @@ class Session { if (args.last_access) { const p = await this.get({ id: args.id }) + if (!p) return false + // if less than 60 seconds old, do nothing const now = parseInt(Date.now() / 1000, 10) const oneMinuteAgo = now - 60 + // update only when +1 minute old (save DB writes) if (p.last_access > oneMinuteAgo) return true args.last_access = now diff --git a/lib/test/zone.json b/lib/test/zone.json index 3f18ca0..7e20f13 100644 --- a/lib/test/zone.json +++ b/lib/test/zone.json @@ -10,6 +10,5 @@ "expire": 3, "minimum": 4, "ttl": 3600, - "location": "", "last_publish": null } diff --git a/lib/zone.js b/lib/zone.js index e19a6e1..c712da3 100644 --- a/lib/zone.js +++ b/lib/zone.js @@ -37,7 +37,6 @@ class Zone { , expire , minimum , ttl - , location , last_modified , last_publish , deleted diff --git a/lib/zone_record.js b/lib/zone_record.js index 0cf598f..c5745bb 100644 --- a/lib/zone_record.js +++ b/lib/zone_record.js @@ -165,9 +165,9 @@ function unApplyMap(obj, map) { const [algo, flags, iters, salt, bitmaps, next] = obj.address .slice(1, -1) .split("','") - obj['hash algorithm'] = /^\d+$/.test(algo) ? parseInt(algo) : algo ?? '' - obj.flags = /^\d+$/.test(flags) ? parseInt(flags) : flags ?? '' - obj.iterations = /^\d+$/.test(iters) ? parseInt(iters) : iters ?? '' + obj['hash algorithm'] = /^\d+$/.test(algo) ? parseInt(algo) : (algo ?? '') + obj.flags = /^\d+$/.test(flags) ? parseInt(flags) : (flags ?? '') + obj.iterations = /^\d+$/.test(iters) ? parseInt(iters) : (iters ?? '') obj.salt = salt obj['type bit maps'] = bitmaps obj['next hashed owner name'] = next @@ -176,9 +176,9 @@ function unApplyMap(obj, map) { } if (obj.type === 'NSEC3PARAM') { const [algo, flags, iters, salt] = obj.address.slice(1, -1).split("','") - obj['hash algorithm'] = /^\d+$/.test(algo) ? parseInt(algo) : algo ?? '' - obj.flags = /^\d+$/.test(flags) ? parseInt(flags) : flags ?? '' - obj.iterations = /^\d+$/.test(iters) ? parseInt(iters) : iters ?? '' + obj['hash algorithm'] = /^\d+$/.test(algo) ? parseInt(algo) : (algo ?? '') + obj.flags = /^\d+$/.test(flags) ? parseInt(flags) : (flags ?? '') + obj.iterations = /^\d+$/.test(iters) ? parseInt(iters) : (iters ?? '') obj.salt = salt delete obj.address delete map.address diff --git a/package.json b/package.json index ecefb71..d85acaf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nictool/api", - "version": "3.0.0-alpha.5", + "version": "3.0.0-alpha.6", "description": "NicTool API", "main": "index.js", "type": "module", @@ -11,9 +11,10 @@ "prettier": "npx prettier *.js conf.d lib routes html --check", "prettier:fix": "npx prettier *.js conf.d lib routes html --write", "start": "NODE_ENV=production node ./server", - "develop": "NODE_ENV=development node ./server", + "develop": "NODE_ENV=development node --watch server.js ./server", "test": "./test.sh", "versions": "npx dependency-version-checker check", + "versions:fix": "npx dependency-version-checker update", "watch": "./test.sh watch" }, "repository": { @@ -33,21 +34,22 @@ }, "homepage": "https://github.com/NicTool/api#readme", "devDependencies": { - "@babel/eslint-parser": "^7.23.10", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "eslint": "^8.57.0" + "@babel/eslint-parser": "^7.27.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "eslint": "^9.24.0" }, "dependencies": { "@hapi/cookie": "^12.0.1", - "@hapi/hapi": "^21.3.3", - "@hapi/hoek": "^11.0.4", + "@hapi/hapi": "^21.4.0", + "@hapi/hoek": "^11.0.7", "@hapi/inert": "^7.1.0", + "@hapi/jwt": "^3.2.0", "@hapi/vision": "^7.0.3", - "@nictool/dns-resource-record": "^1.2.1", - "@nictool/validate": "^0.8.0", - "hapi-swagger": "^17.2.1", - "mysql2": "^3.9.2", - "qs": "^6.11.2", - "yaml": "^2.4.0" + "@nictool/dns-resource-record": "^1.2.2", + "@nictool/validate": "^0.8.2", + "hapi-swagger": "^17.3.2", + "mysql2": "^3.14.0", + "qs": "^6.14.0", + "yaml": "^2.7.1" } -} +} \ No newline at end of file diff --git a/routes/group.test.js b/routes/group.test.js index 12d8a58..d3d662a 100644 --- a/routes/group.test.js +++ b/routes/group.test.js @@ -23,7 +23,7 @@ after(async () => { }) describe('group routes', () => { - let sessionCookie + let auth = { headers: { } } it('POST /session establishes a session', async () => { const res = await server.inject({ @@ -34,19 +34,17 @@ describe('group routes', () => { password: userCase.password, }, }) - assert.ok(res.headers['set-cookie'][0]) - sessionCookie = res.headers['set-cookie'][0].split(';')[0] + assert.ok(res.result.group.id) + // auth.headers = { Cookie: res.headers['set-cookie'][0].split(';')[0] } + auth.headers = { Authorization: `Bearer ${res.result.session.token}` } }) it(`GET /group/${groupCase.id}`, async () => { const res = await server.inject({ method: 'GET', url: `/group/${groupCase.id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.ok([200, 204].includes(res.statusCode)) }) @@ -55,17 +53,13 @@ describe('group routes', () => { testCase.id = case2Id // make it unique testCase.name = `example2.com` delete testCase.deleted - // console.log(testCase) const res = await server.inject({ method: 'POST', url: '/group', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, payload: testCase, }) - // console.log(res.result) assert.equal(res.statusCode, 201) }) @@ -73,11 +67,8 @@ describe('group routes', () => { const res = await server.inject({ method: 'GET', url: `/group/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.ok([200, 204].includes(res.statusCode)) }) @@ -85,11 +76,8 @@ describe('group routes', () => { const res = await server.inject({ method: 'DELETE', url: `/group/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) }) @@ -97,11 +85,8 @@ describe('group routes', () => { const res = await server.inject({ method: 'GET', url: `/group/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 204) }) @@ -109,11 +94,8 @@ describe('group routes', () => { const res = await server.inject({ method: 'GET', url: `/group/${case2Id}?deleted=true`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.ok([200, 204].includes(res.statusCode)) }) @@ -121,9 +103,7 @@ describe('group routes', () => { const res = await server.inject({ method: 'DELETE', url: '/session', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) assert.equal(res.statusCode, 200) }) diff --git a/routes/index.js b/routes/index.js index 79429b3..b33c66a 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,8 +3,9 @@ import path from 'node:path' import url from 'node:url' +import Jwt from '@hapi/jwt' import Hapi from '@hapi/hapi' -import Cookie from '@hapi/cookie' +// import Cookie from '@hapi/cookie' import Inert from '@hapi/inert' import Vision from '@hapi/vision' import HapiSwagger from 'hapi-swagger' @@ -22,6 +23,7 @@ import { Session, SessionRoutes } from './session.js' import { PermissionRoutes } from './permission.js' import { NameserverRoutes } from './nameserver.js' import { ZoneRoutes } from './zone.js' +import { ZoneRecordRoutes } from './zone_record.js' let server @@ -31,10 +33,12 @@ async function setup() { server = Hapi.server({ port: httpCfg.port, host: httpCfg.host, + tls: httpCfg.tls, query: { parser: (query) => qs.parse(query), }, routes: { + cors: true, files: { relativeTo: path.join( path.dirname(url.fileURLToPath(import.meta.url)), @@ -44,7 +48,7 @@ async function setup() { }, }) - await server.register(Cookie) + await server.register(Jwt) await server.register(Inert) await server.register([ Inert, @@ -60,19 +64,28 @@ async function setup() { }, ]) - server.auth.strategy('session', 'cookie', { - cookie: httpCfg.cookie, - - validate: async (request, session) => { - const s = await Session.get({ id: session.id }) - if (!s) return { isValid: false } // invalid cookie - - // const account = await User.get({ id: s.nt_user_id }) - return { isValid: true } // , credentials: account } + server.auth.strategy('nt_jwt_strategy', 'jwt', { + keys: httpCfg.jwt.key, + verify: { + aud: 'urn:audience:test', + iss: 'urn:issuer:test', + sub: false, + nbf: true, + exp: true, + maxAgeSec: 14400, // 4 hours + timeSkewSec: 15, + }, + httpAuthScheme: 'Bearer', + headerName: 'authorization', + validate: (artifacts, request, h) => { + return { + isValid: true, + credentials: artifacts.decoded.payload.nt, + } }, }) - server.auth.default('session') + server.auth.default('nt_jwt_strategy') server.route({ method: 'GET', @@ -88,6 +101,7 @@ async function setup() { PermissionRoutes(server) NameserverRoutes(server) ZoneRoutes(server) + ZoneRecordRoutes(server) server.route({ method: '*', @@ -97,6 +111,14 @@ async function setup() { }, }) + server.events.on('request', (request, event, tags) => { + if (tags.error) { + console.error( + `Request ${event.request} error: ${event.error ? event.error.message : 'unknown'}`, + ) + } + }) + server.events.on('stop', () => { if (User.mysql) User.mysql.disconnect() if (Session.mysql) Session.mysql.disconnect() diff --git a/routes/nameserver.js b/routes/nameserver.js index bc792e6..1ad9d35 100644 --- a/routes/nameserver.js +++ b/routes/nameserver.js @@ -7,27 +7,29 @@ function NameserverRoutes(server) { server.route([ { method: 'GET', - path: '/nameserver/{id}', + path: '/nameserver/{id?}', options: { validate: { query: validate.nameserver.GET_req, + failAction: 'log', }, response: { schema: validate.nameserver.GET_res, + failAction: 'log', }, tags: ['api'], }, handler: async (request, h) => { const getArgs = { deleted: request.query.deleted === true ? 1 : 0, - id: parseInt(request.params.id, 10), } + if (request.params.id) getArgs.id = parseInt(request.params.id, 10) const nameservers = await Nameserver.get(getArgs) return h .response({ - nameserver: nameservers[0], + nameserver: nameservers, meta: { api: meta.api, msg: `here's your nameserver`, @@ -42,9 +44,11 @@ function NameserverRoutes(server) { options: { validate: { payload: validate.nameserver.POST, + failAction: 'log', }, response: { schema: validate.nameserver.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -55,7 +59,7 @@ function NameserverRoutes(server) { return h .response({ - nameserver: nameservers[0], + nameserver: nameservers, meta: { api: meta.api, msg: `the nameserver was created`, @@ -70,9 +74,11 @@ function NameserverRoutes(server) { options: { validate: { query: validate.nameserver.DELETE, + failAction: 'log', }, response: { schema: validate.nameserver.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -100,7 +106,7 @@ function NameserverRoutes(server) { return h .response({ - nameserver: nameservers[0], + nameserver: nameservers, meta: { api: meta.api, msg: `I deleted that nameserver`, diff --git a/routes/nameserver.test.js b/routes/nameserver.test.js index c02499a..c93038c 100644 --- a/routes/nameserver.test.js +++ b/routes/nameserver.test.js @@ -27,7 +27,7 @@ after(async () => { }) describe('nameserver routes', () => { - let sessionCookie + let auth = { headers: { } } it('POST /session establishes a session', async () => { const res = await server.inject({ @@ -38,21 +38,19 @@ describe('nameserver routes', () => { password: userCase.password, }, }) - assert.ok(res.headers['set-cookie'][0]) - sessionCookie = res.headers['set-cookie'][0].split(';')[0] + assert.ok(res.result.session.token) + auth.headers = { Authorization: `Bearer ${res.result.session.token}` } }) it(`GET /nameserver/${nsCase.id}`, async () => { const res = await server.inject({ method: 'GET', url: `/nameserver/${nsCase.id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) - assert.equal(res.result.nameserver.name, nsCase.name) + assert.equal(res.result.nameserver[0].name, nsCase.name) }) it(`POST /nameserver (${case2Id})`, async () => { @@ -64,38 +62,29 @@ describe('nameserver routes', () => { const res = await server.inject({ method: 'POST', url: '/nameserver', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, payload: testCase, }) - // console.log(res.result) assert.equal(res.statusCode, 201) - assert.ok(res.result.nameserver.gid) + assert.ok(res.result.nameserver[0].gid) }) it(`GET /nameserver/${case2Id}`, async () => { const res = await server.inject({ method: 'GET', url: `/nameserver/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) - assert.ok(res.result.nameserver.gid) + assert.ok(res.result.nameserver[0].gid) }) it(`DELETE /nameserver/${case2Id}`, async () => { const res = await server.inject({ method: 'DELETE', url: `/nameserver/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) }) @@ -103,11 +92,8 @@ describe('nameserver routes', () => { const res = await server.inject({ method: 'DELETE', url: `/nameserver/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 404) }) @@ -115,24 +101,17 @@ describe('nameserver routes', () => { const res = await server.inject({ method: 'GET', url: `/nameserver/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) - // assert.equal(res.statusCode, 200) - assert.equal(res.result.nameserver, undefined) + assert.deepEqual(res.result.nameserver, []) }) it(`GET /nameserver/${case2Id} (deleted)`, async () => { const res = await server.inject({ method: 'GET', url: `/nameserver/${case2Id}?deleted=true`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) assert.ok(res.result.nameserver) }) @@ -141,9 +120,7 @@ describe('nameserver routes', () => { const res = await server.inject({ method: 'DELETE', url: '/session', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) assert.equal(res.statusCode, 200) }) diff --git a/routes/permission.js b/routes/permission.js index 99fdbe9..0f6191d 100644 --- a/routes/permission.js +++ b/routes/permission.js @@ -11,9 +11,11 @@ function PermissionRoutes(server) { options: { validate: { query: validate.permission.GET_req, + failAction: 'log', }, response: { schema: validate.permission.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -42,9 +44,11 @@ function PermissionRoutes(server) { options: { validate: { payload: validate.permission.POST, + failAction: 'log', }, response: { schema: validate.permission.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -70,9 +74,11 @@ function PermissionRoutes(server) { options: { validate: { query: validate.permission.DELETE, + failAction: 'log', }, response: { schema: validate.permission.GET_res, + failAction: 'log', }, tags: ['api'], }, diff --git a/routes/permission.test.js b/routes/permission.test.js index 43b3ca9..3c4c7bb 100644 --- a/routes/permission.test.js +++ b/routes/permission.test.js @@ -26,7 +26,7 @@ after(async () => { }) describe('permission routes', () => { - let sessionCookie + let auth = { headers: { } } it('POST /session establishes a session', async () => { const res = await server.inject({ @@ -37,19 +37,16 @@ describe('permission routes', () => { password: userCase.password, }, }) - assert.ok(res.headers['set-cookie'][0]) - sessionCookie = res.headers['set-cookie'][0].split(';')[0] + assert.ok(res.result.user.id) + auth.headers = { Authorization: `Bearer ${res.result.session.token}` } }) it(`GET /permission/${userCase.id}`, async () => { const res = await server.inject({ method: 'GET', url: `/permission/${userCase.id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) assert.equal(res.result.permission.zone.create, true) assert.equal(res.result.permission.nameserver.create, false) @@ -67,9 +64,7 @@ describe('permission routes', () => { const res = await server.inject({ method: 'POST', url: '/permission', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, payload: testCase, }) // console.log(res.result) @@ -82,9 +77,7 @@ describe('permission routes', () => { const res = await server.inject({ method: 'GET', url: `/permission/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) @@ -96,9 +89,7 @@ describe('permission routes', () => { const res = await server.inject({ method: 'DELETE', url: `/permission/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) @@ -108,9 +99,7 @@ describe('permission routes', () => { const res = await server.inject({ method: 'DELETE', url: `/permission/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 404) @@ -120,9 +109,7 @@ describe('permission routes', () => { const res = await server.inject({ method: 'GET', url: `/permission/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) // assert.equal(res.statusCode, 200) @@ -133,9 +120,7 @@ describe('permission routes', () => { const res = await server.inject({ method: 'GET', url: `/permission/${case2Id}?deleted=true`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) @@ -146,9 +131,7 @@ describe('permission routes', () => { const res = await server.inject({ method: 'DELETE', url: '/session', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) assert.equal(res.statusCode, 200) }) diff --git a/routes/session.js b/routes/session.js index ee2cea4..761031b 100644 --- a/routes/session.js +++ b/routes/session.js @@ -1,10 +1,15 @@ import validate from '@nictool/validate' +import Config from '../lib/config.js' +import Jwt from '@hapi/jwt' + import User from '../lib/user.js' import Session from '../lib/session.js' import { meta } from '../lib/util.js' +const httpCfg = await Config.get('http') + function SessionRoutes(server) { server.route([ { @@ -13,11 +18,12 @@ function SessionRoutes(server) { options: { response: { schema: validate.session.GET_res, + failAction: 'log', }, tags: ['api'], }, handler: async (request, h) => { - const { user, group, session } = request.state['sid-nictool'] + const { user, group, session } = h.request.auth.credentials Session.put({ id: session.id, last_access: true }) @@ -41,9 +47,11 @@ function SessionRoutes(server) { auth: { mode: 'try' }, validate: { payload: validate.session.POST, + failAction: 'log', }, response: { - schema: validate.session.GET, + schema: validate.session.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -59,17 +67,30 @@ function SessionRoutes(server) { last_access: parseInt(Date.now() / 1000, 10), }) - request.cookieAuth.set({ - user: account.user, - group: account.group, - session: { id: sessId }, - }) + const token = Jwt.token.generate( + { + aud: 'urn:audience:test', + iss: 'urn:issuer:test', + nt: { + user: account.user, + group: account.group, + session: { id: sessId }, + }, + }, + { + key: httpCfg.jwt.key, + algorithm: 'HS512', + }, + { + ttlSec: 28800, // 8 hours + }, + ) return h .response({ user: account.user, group: account.group, - session: { id: sessId }, + session: { id: sessId, token: token }, meta: { api: meta.api, msg: `you are logged in`, @@ -84,20 +105,27 @@ function SessionRoutes(server) { options: { validate: { query: validate.session.DELETE, + failAction: 'log', }, response: { schema: validate.session.GET, + failAction: 'log', }, tags: ['api'], }, - handler: (request, h) => { - request.cookieAuth.clear() + handler: async (request, h) => { + const { user, group, session } = h.request.auth.credentials + + const bool = await Session.delete({ + id: session.id, + session: '3.0.0', + }) return h .response({ meta: { api: meta.api, - msg: 'You are logged out', + msg: `You are logged out: ${bool}`, }, }) .code(200) diff --git a/routes/session.test.js b/routes/session.test.js index 5167290..9e3bd1f 100644 --- a/routes/session.test.js +++ b/routes/session.test.js @@ -23,10 +23,6 @@ after(async () => { await server.stop() }) -const parseCookie = (c) => { - return c.split(';')[0] -} - describe('session routes', () => { const routes = [{ GET: '/' }, { DELETE: '/session' }] @@ -46,7 +42,7 @@ describe('session routes', () => { }) describe('with session, can retrieve private URIs', () => { - let sessionCookie + let auth = { headers: { } } before(async () => { const res = await server.inject({ @@ -58,17 +54,15 @@ describe('session routes', () => { }, }) assert.equal(res.statusCode, 200) - assert.ok(res.headers['set-cookie'][0]) - sessionCookie = parseCookie(res.headers['set-cookie'][0]) + assert.ok(res.result.user.id) + auth.headers = { Authorization: `Bearer ${res.result.session.token}` } }) after(async () => { const res = await server.inject({ method: 'DELETE', url: '/session', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) @@ -83,9 +77,7 @@ describe('session routes', () => { const res = await server.inject({ method: key, url: val, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) assert.equal(res.request.auth.isAuthenticated, true) assert.equal(res.statusCode, 200) diff --git a/routes/test/zone.json b/routes/test/zone.json index 951b5d2..08357df 100644 --- a/routes/test/zone.json +++ b/routes/test/zone.json @@ -10,6 +10,5 @@ "expire": 4, "minimum": 5, "ttl": 3601, - "location": "", "last_publish": null -} \ No newline at end of file +} diff --git a/routes/user.js b/routes/user.js index f199d00..1f0fdd0 100644 --- a/routes/user.js +++ b/routes/user.js @@ -11,21 +11,25 @@ function UserRoutes(server) { options: { validate: { query: validate.user.GET_req, + failAction: 'log', }, response: { schema: validate.user.GET_res, + failAction: 'log', }, - // tags: ['api'], + tags: ['api'], }, handler: async (request, h) => { // get myself - const { nt_user_id } = request.state['sid-nictool'] - const users = await User.get({ id: nt_user_id }) + const { user, group, session } = h.request.auth.credentials + + const users = await User.get({ id: user.id }) delete users[0].gid + return h .response({ - user: users[0], + user: users, meta: { api: meta.api, msg: `this is you`, @@ -40,9 +44,11 @@ function UserRoutes(server) { options: { validate: { query: validate.user.GET_req, + failAction: 'log', }, response: { schema: validate.user.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -68,7 +74,7 @@ function UserRoutes(server) { return h .response({ - user: users[0], + user: users, group: { id: gid }, meta: { api: meta.api, @@ -84,9 +90,11 @@ function UserRoutes(server) { options: { validate: { payload: validate.user.POST, + failAction: 'log', }, response: { schema: validate.user.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -102,7 +110,7 @@ function UserRoutes(server) { return h .response({ - user: users[0], + user: users, group, meta: { api: meta.api, @@ -118,9 +126,11 @@ function UserRoutes(server) { options: { validate: { query: validate.user.DELETE, + failAction: 'log', }, response: { schema: validate.user.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -144,7 +154,7 @@ function UserRoutes(server) { return h .response({ - user: users[0], + user: users, meta: { api: meta.api, msg: `I deleted that user`, diff --git a/routes/user.test.js b/routes/user.test.js index 3e08484..2a0e6d1 100644 --- a/routes/user.test.js +++ b/routes/user.test.js @@ -8,7 +8,7 @@ import Group from '../lib/group.js' import groupCase from './test/group.json' with { type: 'json' } import userCase from './test/user.json' with { type: 'json' } -let server, sessionCookie +let server, auth = { headers: { } } before(async () => { server = await init() @@ -23,10 +23,6 @@ after(async () => { await server.stop() }) -const parseCookie = (c) => { - return c.split(';')[0] -} - describe('user routes', () => { it('POST /session establishes a session', async () => { const res = await server.inject({ @@ -37,17 +33,15 @@ describe('user routes', () => { password: userCase.password, }, }) - assert.ok(res.headers['set-cookie'][0]) - sessionCookie = parseCookie(res.headers['set-cookie'][0]) + assert.ok(res.result.user.id) + auth.headers = { Authorization: `Bearer ${res.result.session.token}` } }) it('GET /user', async () => { const res = await server.inject({ method: 'GET', url: '/user', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) @@ -57,11 +51,8 @@ describe('user routes', () => { const res = await server.inject({ method: 'GET', url: `/user/${userCase.id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) }) @@ -74,12 +65,9 @@ describe('user routes', () => { const res = await server.inject({ method: 'POST', url: '/user', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, payload: testCase, }) - // console.log(res.result) assert.equal(res.statusCode, 201) }) @@ -87,11 +75,8 @@ describe('user routes', () => { const res = await server.inject({ method: 'GET', url: `/user/${userId2}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) }) @@ -99,11 +84,8 @@ describe('user routes', () => { const res = await server.inject({ method: 'DELETE', url: `/user/${userId2}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) }) @@ -111,11 +93,8 @@ describe('user routes', () => { const res = await server.inject({ method: 'GET', url: `/user/${userId2}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.ok([200, 204].includes(res.statusCode)) }) @@ -123,11 +102,8 @@ describe('user routes', () => { const res = await server.inject({ method: 'GET', url: `/user/${userId2}?deleted=true`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.ok([200, 204].includes(res.statusCode)) }) @@ -135,11 +111,8 @@ describe('user routes', () => { const res = await server.inject({ method: 'DELETE', url: '/session', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) - // console.log(res.result) assert.equal(res.statusCode, 200) }) }) diff --git a/routes/zone.js b/routes/zone.js index 340e944..d046c0b 100644 --- a/routes/zone.js +++ b/routes/zone.js @@ -7,30 +7,32 @@ function ZoneRoutes(server) { server.route([ { method: 'GET', - path: '/zone/{id}', + path: '/zone/{id?}', options: { validate: { query: validate.zone.GET_req, + failAction: 'log', }, response: { schema: validate.zone.GET_res, + failAction: 'log', }, tags: ['api'], }, handler: async (request, h) => { const getArgs = { deleted: request.query.deleted === true ? 1 : 0, - id: parseInt(request.params.id, 10), } + if (request.params.id) getArgs.id = parseInt(request.params.id, 10) const zones = await Zone.get(getArgs) return h .response({ - zone: zones[0], + zone: zones, meta: { api: meta.api, - msg: `here's your zone`, + msg: `here's your zone(s)`, }, }) .code(200) @@ -42,9 +44,11 @@ function ZoneRoutes(server) { options: { validate: { payload: validate.zone.POST, + failAction: 'log', }, response: { schema: validate.zone.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -55,7 +59,7 @@ function ZoneRoutes(server) { return h .response({ - zone: zones[0], + zone: zones, meta: { api: meta.api, msg: `the zone was created`, @@ -70,9 +74,11 @@ function ZoneRoutes(server) { options: { validate: { query: validate.zone.DELETE, + failAction: 'log', }, response: { schema: validate.zone.GET_res, + failAction: 'log', }, tags: ['api'], }, @@ -100,7 +106,7 @@ function ZoneRoutes(server) { return h .response({ - zone: zones[0], + zone: zones, meta: { api: meta.api, msg: `I deleted that zone`, diff --git a/routes/zone.test.js b/routes/zone.test.js index ab0a80b..6f090cd 100644 --- a/routes/zone.test.js +++ b/routes/zone.test.js @@ -27,7 +27,7 @@ after(async () => { }) describe('zone routes', () => { - let sessionCookie + let auth = { headers: { } } it('POST /session establishes a session', async () => { const res = await server.inject({ @@ -38,21 +38,19 @@ describe('zone routes', () => { password: userCase.password, }, }) - assert.ok(res.headers['set-cookie'][0]) - sessionCookie = res.headers['set-cookie'][0].split(';')[0] + assert.ok(res.result.user.id) + auth.headers = { Authorization: `Bearer ${res.result.session.token}` } }) it(`GET /zone/${nsCase.id}`, async () => { const res = await server.inject({ method: 'GET', url: `/zone/${nsCase.id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) - assert.equal(res.result.zone.name, nsCase.name) + assert.equal(res.result.zone[0].name, nsCase.name) }) it(`POST /zone (${case2Id})`, async () => { @@ -64,36 +62,30 @@ describe('zone routes', () => { const res = await server.inject({ method: 'POST', url: '/zone', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, payload: testCase, }) // console.log(res.result) assert.equal(res.statusCode, 201) - assert.ok(res.result.zone.gid) + assert.ok(res.result.zone[0].gid) }) it(`GET /zone/${case2Id}`, async () => { const res = await server.inject({ method: 'GET', url: `/zone/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) - assert.ok(res.result.zone.gid) + assert.ok(res.result.zone[0].gid) }) it(`DELETE /zone/${case2Id}`, async () => { const res = await server.inject({ method: 'DELETE', url: `/zone/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) @@ -103,9 +95,7 @@ describe('zone routes', () => { const res = await server.inject({ method: 'DELETE', url: `/zone/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 404) @@ -115,35 +105,29 @@ describe('zone routes', () => { const res = await server.inject({ method: 'GET', url: `/zone/${case2Id}`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) // assert.equal(res.statusCode, 200) - assert.equal(res.result.zone, undefined) + assert.deepEqual(res.result.zone, []) }) it(`GET /zone/${case2Id} (deleted)`, async () => { const res = await server.inject({ method: 'GET', url: `/zone/${case2Id}?deleted=true`, - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) // console.log(res.result) assert.equal(res.statusCode, 200) - assert.equal(res?.result?.zone, undefined) + assert.ok(res.result.zone) }) it('DELETE /session', async () => { const res = await server.inject({ method: 'DELETE', url: '/session', - headers: { - Cookie: sessionCookie, - }, + headers: auth.headers, }) assert.equal(res.statusCode, 200) }) diff --git a/routes/zone_record.js b/routes/zone_record.js new file mode 100644 index 0000000..aafd664 --- /dev/null +++ b/routes/zone_record.js @@ -0,0 +1,121 @@ +import validate from '@nictool/validate' + +import ZoneRecord from '../lib/zone_record.js' +import { meta } from '../lib/util.js' + +function ZoneRecordRoutes(server) { + server.route([ + { + method: 'GET', + path: '/zone_record/{id?}', + options: { + validate: { + query: validate.zone_record.GET_req, + failAction: 'log', + }, + response: { + schema: validate.zone_record.GET_res, + failAction: 'log', + }, + tags: ['api'], + }, + handler: async (request, h) => { + const getArgs = { + deleted: request.query.deleted === true ? 1 : 0, + } + if (request.params.id) getArgs.id = parseInt(request.params.id, 10) + if (request.query.zid) getArgs.zid = parseInt(request.query.zid, 10) + + const zrs = await ZoneRecord.get(getArgs) + + return h + .response({ + zone_record: zrs, + meta: { + api: meta.api, + msg: `here's your zone record(s)`, + }, + }) + .code(200) + }, + }, + { + method: 'POST', + path: '/zone_record', + options: { + validate: { + payload: validate.zone_record.POST, + }, + response: { + schema: validate.zone_record.GET_res, + }, + tags: ['api'], + }, + handler: async (request, h) => { + const id = await ZoneRecord.create(request.payload) + + const zrs = await ZoneRecord.get({ id }) + + return h + .response({ + zone_record: zrs[0], + meta: { + api: meta.api, + msg: `the zone record was created`, + }, + }) + .code(201) + }, + }, + { + method: 'DELETE', + path: '/zone_record/{id}', + options: { + validate: { + query: validate.zone_record.DELETE, + }, + response: { + schema: validate.zone_record.GET_res, + }, + tags: ['api'], + }, + handler: async (request, h) => { + const zrs = await ZoneRecord.get({ + deleted: request.query.deleted === true ? 1 : 0, + id: parseInt(request.params.id, 10), + }) + + if (zrs.length === 0) { + return h + .response({ + meta: { + api: meta.api, + msg: `I couldn't find that zone record`, + }, + }) + .code(404) + } + + const r = await ZoneRecord.delete({ + id: zrs[0].id, + deleted: 1, + }) + console.log(`deleted`, r) + + return h + .response({ + zone: zrs[0], + meta: { + api: meta.api, + msg: `I deleted that zone record`, + }, + }) + .code(200) + }, + }, + ]) +} + +export default ZoneRecordRoutes + +export { ZoneRecord, ZoneRecordRoutes } diff --git a/sql/init-mysql.sh b/sql/init-mysql.sh index e57572b..2018c94 100755 --- a/sql/init-mysql.sh +++ b/sql/init-mysql.sh @@ -1,5 +1,7 @@ #!/bin/sh +MYSQL_BIN="" + if [ "$MYSQL_PWD" = "" ]; then export MYSQL_PWD=root @@ -9,7 +11,8 @@ then Linux*) ;; Darwin*) - mysqladmin --user=root --password='' --protocol=tcp password 'root' + MYSQL_BIN=/opt/homebrew/opt/mysql@8.4/bin/ + ${MYSQL_BIN}mysqladmin --user=root --password='' --protocol=tcp password 'root' ;; CYGWIN*|MINGW*|MINGW32*|MSYS*) mysqladmin --user=root --password='' --protocol=tcp password 'root' @@ -21,14 +24,14 @@ fi # AUTH="--defaults-extra-file=./sql/my-gha.cnf" if [ "$1" = "drop" ]; then - mysql --user=root -e 'DROP DATABASE IF EXISTS nictool;' || exit 1 + ${MYSQL_BIN}mysql --user=root -e 'DROP DATABASE IF EXISTS nictool;' || exit 1 fi -mysql --user=root -e 'CREATE DATABASE nictool;' || exit 1 +${MYSQL_BIN}mysql --user=root -e 'CREATE DATABASE nictool;' || exit 1 for f in ./sql/*.sql; do echo "cat $f | mysql nictool" - cat $f | mysql --user=root nictool || exit 1 + cat $f | ${MYSQL_BIN}mysql --user=root nictool || exit 1 done exit 0 \ No newline at end of file diff --git a/test.sh b/test.sh index 3bad8fe..a53ee00 100755 --- a/test.sh +++ b/test.sh @@ -13,15 +13,18 @@ cleanup() { trap cleanup EXIT 1 2 3 6 -if [ $# -ge 1 ] && [ "$1" = "watch" ]; then - $NODE --test --watch +if [ $# -ge 1 ]; then + if [ "$1" = "watch" ]; then + $NODE --test --watch + else + $NODE --test --test-reporter=spec "$1" + fi else # if [ -n "$GITHUB_WORKFLOW" ]; then # npm i --no-save node-test-github-reporter # $NODE --test --test-reporter=node-test-github-reporter - # else - $NODE --test --test-reporter=spec lib/*.test.js routes/*.test.js # fi + $NODE --test --test-reporter=spec lib/*.test.js routes/*.test.js fi # npx mocha --exit --no-warnings=ExperimentalWarning lib/*.test.js routes/*.test.js From 5ca71c168812904860341499c3ec2945552de1b2 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 9 Apr 2025 10:59:54 -0700 Subject: [PATCH 2/2] ci: mac updates --- sql/init-mysql.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/init-mysql.sh b/sql/init-mysql.sh index 2018c94..f3638f1 100755 --- a/sql/init-mysql.sh +++ b/sql/init-mysql.sh @@ -30,7 +30,7 @@ ${MYSQL_BIN}mysql --user=root -e 'CREATE DATABASE nictool;' || exit 1 for f in ./sql/*.sql; do - echo "cat $f | mysql nictool" + echo "cat $f | ${MYSQL_BIN}mysql nictool" cat $f | ${MYSQL_BIN}mysql --user=root nictool || exit 1 done