Share the excluded-ranges filter with IP Addresses; add Admin Links page

IP Addresses (IPAM) now reads the same excluded-ranges setting the
Consistency page manages, so "not interesting" addresses - a Docker
bridge network repeating on every host, say - can be hidden there too.
Adds a "Hide excluded addresses" toggle (on by default, with a live
count), an inline ranges editor matching Consistency's, an "excluded"
badge on rows shown anyway, and a per-row "Exclude..." shortcut that
suggests a /24 (or /64 for IPv6) around that address. Editing ranges
from either page updates both, since it's one shared setting.

Also adds Operations > Admin Links: a single page summarizing every
admin bookmark added across all servers (Dockge, Webmin, Cockpit, etc,
previously only visible per-server on each server's own detail page),
sortable and searchable, with the same add/edit/delete capability -
adding one here just asks which server it belongs to.

Verified both with real HTTP-level tests: a genuine Express app, a
scratch SQLite DB, and forged sessions, covering the exclusion
matching, the shared-setting round trip, the links aggregation and
join, and role enforcement - the real dev DB was confirmed untouched
throughout. Both packages build clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
bobbanandClaude Sonnet 5 committed 2026-09-30 00:07:27 +02:00
1 parent 236b1da0dc
commit 1ff1c6550c
9 files changed
+435 -9

No files matched your search

+10 -1
View File
@@ -11,6 +11,8 @@ import { loadIntegrationConfig } from "../integrations/loadIntegration.js";
import { createTailscaleAdapter } from "../integrations/tailscale/adapter.js";
import { createProxmoxAdapter } from "../integrations/proxmox/adapter.js";
import { createPhpIpamAdapter } from "../integrations/phpipam/adapter.js";
import { makeExclusion } from "../services/consistency.js";
import { getSettings } from "../services/settingsStore.js";
export const ipamRouter = Router();
@@ -36,7 +38,14 @@ async function matchingDnsRecordsByIp(ips: string[]): Promise<Map<string, string
ipamRouter.get("/", asyncHandler(async (_req, res) => {
const rows = await db.select().from(ipamEntries).orderBy(ipamEntries.ipAddress);
const byIp = await matchingDnsRecordsByIp(rows.map((r) => r.ipAddress));
res.json({ entries: rows.map((r) => ({ ...r, matchingDnsRecords: byIp.get(r.ipAddress) ?? [] })) });
// Same excluded-ranges setting the Consistency page manages — "not interesting" addresses (Docker's bridge
// network repeating on every host, say) can be hidden here too, without duplicating that configuration.
const { consistency } = await getSettings();
const excluded = makeExclusion(consistency.excludedRanges);
res.json({
entries: rows.map((r) => ({ ...r, matchingDnsRecords: byIp.get(r.ipAddress) ?? [], excluded: excluded(r.ipAddress) })),
excludedRanges: consistency.excludedRanges,
});
}));
const createInput = z.object({
+18
View File
@@ -170,6 +170,24 @@ serversRouter.get("/summary", asyncHandler(async (_req, res) => {
res.json(buildServerSummary(rows, healthChecks, Date.now(), await activeSubjects()));
}));
// For the Operations > Admin Links page — every server's admin bookmarks in one place, instead of visiting
// each server's own detail page to find them.
serversRouter.get("/links", asyncHandler(async (_req, res) => {
const rows = await db
.select({
id: serverLinks.id,
serverId: serverLinks.serverId,
serverName: servers.name,
serverHostname: servers.hostname,
label: serverLinks.label,
url: serverLinks.url,
})
.from(serverLinks)
.innerJoin(servers, eq(serverLinks.serverId, servers.id))
.orderBy(servers.name, serverLinks.label);
res.json({ links: rows });
}));
serversRouter.get("/:id/detail", asyncHandler(async (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id)) return res.status(400).json({ error: "invalid_id" });