Add a global search / command palette (Ctrl/Cmd+K)
With 6 integrations, DNS, secrets, IPAM, and servers all in one app, there was no single place to type a hostname/IP/name and jump straight to it. New GET /api/search aggregates a LIKE-based search across servers, secrets, IPAM, integrations, DNS providers, DNS zones, and DNS records (joining zone/provider names onto each record result) in one round trip — homelab-scale row counts make a naive LIKE scan plenty fast, no FTS needed. Every underlying resource's own list endpoint already only requires requireAuth (viewer role included), so the aggregate endpoint uses the same single check. Frontend: a self-contained CommandPalette component (Tabler's modal CSS classes driven by React state, since the app doesn't load Bootstrap's JS) opens via a sidebar search button or Ctrl/Cmd+K from anywhere, debounces input, and supports arrow-key navigation. Results link to the right page: servers to their existing /servers/:id detail route; DNS zone/record results deep-link via new ?providerId= &zoneId= query-param handling added to Dns.tsx (auto-selects that provider/zone on load, since Dns.tsx previously held selection only in local state with no URL sync); secrets/IPAM results link with ?q= to prefill each page's existing client-side search box; integrations link to their type's dashboard page (no per-instance route exists yet, so same-type integrations share one link). Verified the query logic (joins, case-insensitive LIKE, correct zone/provider name resolution) against an isolated scratch database seeded with realistic cross-referencing rows — a server name match, a case-mismatched match against both a secret and a DNS provider sharing "cloudflare", an IP address matching both an IPAM entry and the DNS A record pointing at it (confirming the record's joined zone and provider names came through correctly), an integration name match, and a no-match query returning every category empty. Did not re-verify the requireAuth/asyncHandler wiring itself, since it's the same one-line pattern already proven across every other router in this app. Confirmed the real dev database's mtime was untouched throughout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
23eb7f0d70
commit
009bb3e027
8 files changed
+408
-4
No files matched your search
@@ -532,6 +532,24 @@ export interface SynologySystemInfo {
|
||||
memory: { totalBytes: number | null; usedBytes: number | null };
|
||||
}
|
||||
|
||||
export interface SearchResponse {
|
||||
servers: { id: number; name: string; hostname: string | null }[];
|
||||
secrets: { id: number; name: string; type: SecretType }[];
|
||||
ipam: { id: number; ipAddress: string; label: string | null }[];
|
||||
integrations: { id: number; type: IntegrationType; name: string }[];
|
||||
dnsProviders: { id: number; providerType: DnsProviderType; name: string }[];
|
||||
dnsZones: { providerId: number; zoneId: string; zoneName: string; providerName: string }[];
|
||||
dnsRecords: {
|
||||
providerId: number;
|
||||
zoneId: string;
|
||||
zoneName: string;
|
||||
type: string;
|
||||
name: string;
|
||||
content: string;
|
||||
providerName: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export class UnauthorizedError extends Error {}
|
||||
export class ForbiddenError extends Error {}
|
||||
|
||||
@@ -557,6 +575,9 @@ async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
|
||||
export const api = {
|
||||
me: () => request<{ user: CurrentUser }>("/api/me"),
|
||||
search: {
|
||||
query: (q: string) => request<SearchResponse>(`/api/search?q=${encodeURIComponent(q)}`),
|
||||
},
|
||||
users: {
|
||||
list: () => request<{ users: UserRecord[] }>("/api/users"),
|
||||
updateRole: (id: number, role: UserRole) =>
|
||||
|
||||
Reference in new issue
Block a user