From 5c0018f24aca3f1d63652656ad9d6dc974ad8307 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Sun, 9 Aug 2026 16:29:08 -0700 Subject: [PATCH] release(v2.0.3): fix Directory tab managed-filter bug and site-status 500 (#186) - GET /api/directory-admin/resources let every kind:'host' resource through regardless of promotion status, so "Auto-promote to Directory" unchecked on a discovery plugin never kept unpromoted devices out of the Directory tab. - GET /api/directory-admin/site-status queried the nonexistent Resource.subType column instead of metadata.subType, throwing SequelizeDatabaseError. - Added a "Show ignored" toggle to Discovered Inventory (off by default). - Untracked nodejs/config/inventory.sqlite -- the app's default runtime DB, not a fixture, committed by mistake across 13 prior releases. Co-authored-by: Claude Sonnet 5 --- .gitignore | 6 ++++++ CHANGELOG.md | 10 ++++++++++ nodejs/config/inventory.sqlite | Bin 118784 -> 0 bytes nodejs/package-lock.json | 4 ++-- nodejs/package.json | 2 +- nodejs/routes/api_directory_admin.js | 16 ++++++++++++---- nodejs/views/directory.ejs | 7 +++++++ 7 files changed, 38 insertions(+), 7 deletions(-) delete mode 100644 nodejs/config/inventory.sqlite diff --git a/.gitignore b/.gitignore index 4dfa580..bbbdd91 100755 --- a/.gitignore +++ b/.gitignore @@ -91,6 +91,12 @@ secrets.js # they must never be committed. The empty *.example templates ARE tracked. config/*-secrets.js +# Default sqlite ORM storage (nodejs/models/index.js falls back to this path +# when no external DB is configured via conf.orm) -- live runtime data, not a +# fixture. Was committed by mistake across many prior releases. NB: this is +# nodejs/config/, distinct from the root ./config/ secrets dir above. +nodejs/config/*.sqlite + # Jekyll build artifact (GitHub Pages builds remotely; ignore locally) docs/_site diff --git a/CHANGELOG.md b/CHANGELOG.md index aca8768..8ce9c94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v2.0.3 - 2026-08-09 + +### Fixed +- **Directory tab showed unpromoted discoveries.** `GET /api/directory-admin/resources` unconditionally admitted every `kind: 'host'` resource, and every discovery plugin (UniFi, Proxmox, nmap) creates its finds as `kind: 'host'` — so unchecking "Auto-promote to Directory" on a plugin never actually kept undiscovered/unpromoted devices out of the Directory tab, only out of the LDAP-group auto-provisioning. Now only `site` resources are unconditionally shown; anything else that discovery ever touched requires `metadata.managed === true` (set by promotion, an agent, or merging into an already-managed resource). +- **`GET /api/directory-admin/site-status` 500'd.** Queried `Resource.list({ where: { subType: 'wireguard' } })`, but `subType` only ever lives in `metadata.subType` (every driver/discovery plugin reads it that way) — never a top-level DB column, so SQLite raised `no such column: Resource.subType`. Filters in JS over `metadata.subType` now. +- **Discovered Inventory had no way to review ignored devices.** Added a "Show ignored" toggle (off by default) to the tab, so `metadata.ignored === true` rows stay hidden from routine triage but remain reachable. + +### Chore +- **Untracked `nodejs/config/inventory.sqlite`.** It's the app's default runtime DB (`nodejs/models/index.js` falls back to this path when no external DB is configured), not a fixture — it had been committed by mistake across 13 prior releases, churning on every local run. Removed from tracking and gitignored. + # v2.0.2 - 2026-08-09 ### Fixed diff --git a/nodejs/config/inventory.sqlite b/nodejs/config/inventory.sqlite deleted file mode 100644 index 53b93fe8729614d4092986fa33018277b4bd00f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118784 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 RAOHd&00JNY0uP74{{c4A?PUM} diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 64efa8e..ecbd3cf 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "t42-theta-directory", - "version": "2.0.2", + "version": "2.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "t42-theta-directory", - "version": "2.0.2", + "version": "2.0.3", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-free": "^7.3.0", diff --git a/nodejs/package.json b/nodejs/package.json index c4a2811..71ea39e 100755 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "t42-theta-directory", - "version": "2.0.2", + "version": "2.0.3", "description": "A very simple LDAP management and SSO system", "author": [ { diff --git a/nodejs/routes/api_directory_admin.js b/nodejs/routes/api_directory_admin.js index 7ccd092..ea680c5 100644 --- a/nodejs/routes/api_directory_admin.js +++ b/nodejs/routes/api_directory_admin.js @@ -199,10 +199,17 @@ router.get('/resources', async (req, res, next) => { try { let resources = await Resource.list(); resources = resources.filter(r => { - if (r.kind === 'host' || r.kind === 'site') return true; - const isAuto = r.metadata?.discovery_sources?.length > 0 && !r.metadata.discovery_sources.includes('manual'); + // Sites are structural containers, not discovery output -- always shown. + if (r.kind === 'site') return true; + // A resource discovery ever touched only belongs in the Directory once + // it's explicitly managed (created by an agent, promoted by a user, or + // merged into an already-managed resource). Until then it's pending + // review in the Discovered Inventory tab. Anything discovery never + // touched (created directly through this admin UI) has no + // discovery_sources and is always shown. + const isDiscovered = r.metadata?.discovery_sources?.length > 0; const isManaged = r.metadata?.managed === true; - return !isAuto || isManaged; + return !isDiscovered || isManaged; }); // Even admins never receive secret metadata (e.g. client_secret_hash) over // the wire; projectResources strips it unconditionally. @@ -885,7 +892,8 @@ let localSiteConfig = { router.get('/site-status', async (req, res, next) => { try { const sites = await Resource.list({ where: { kind: 'site' } }); - const gateResources = await Resource.list({ where: { subType: 'wireguard' } }); + const allResources = await Resource.list(); + const gateResources = allResources.filter(r => r.metadata && r.metadata.subType === 'wireguard'); res.json({ status: 'ok', diff --git a/nodejs/views/directory.ejs b/nodejs/views/directory.ejs index 578a503..168baa8 100644 --- a/nodejs/views/directory.ejs +++ b/nodejs/views/directory.ejs @@ -136,6 +136,10 @@ Network Discovery Dashboard
+
+ + +
@@ -2258,11 +2262,14 @@ function renderDiscoveryTable() { const search = $('#discovery-search-filter').val().toLowerCase(); + const showIgnored = $('#discovery-show-ignored').is(':checked'); const filtered = allDiscoveryResources.filter(r => { if (search && !r.name.toLowerCase().includes(search) && !r.slug.toLowerCase().includes(search)) return false; // Directory contains managed items; Discovered Inventory only shows unmanaged/pending items awaiting promotion const isExplicitManaged = r.metadata && (r.metadata.managed === true || r.metadata.managed === 'true'); if (isExplicitManaged || r.kind === 'site' || r.kind === 'service') return false; + const isIgnored = r.metadata && (r.metadata.ignored === true || r.metadata.ignored === 'true'); + if (isIgnored && !showIgnored) return false; return true; });