New src/features/redis/keyBrowser/ shared by RedisDetailPage keys tab and
SchemaSidebar redis branch (both previously flat one-shot lists):
- keyTree: ':'-separated folder tree with recursive key counts, numeric
sort, exact-hit pinned at root, local key removal (no full refetch),
type color dots, readable TTL (45s/5m30s/2d5h)
- useRedisKeyBrowser: cursor state machine + request-sequence guard +
Map merge on load-more; setter-synced args (Enter right after typing
no longer races stale render), auto-search on db/instance change
- RedisKeyBrowser: enter-to-scan search bar (SCAN has cost - no debounce,
opsxdb-style) with clear button, effective type filter, load-more
button with cursor hint, '已全部加载' end state
- useRedisDbs: real db list from /redis/:id/dbs (cluster -> DB0 banner)
- RedisDetailPage: hash row edits now use field-level HSET/HDEL endpoint
(preserves other fields + TTL); delete/add update tree locally; CLI
output renders arrays/objects/errors properly
- SchemaSidebar: redis branch swaps to shared browser (compact mode,
inline detail preview + insert-command preserved), auto-switches to
Keys tab for redis instances
- vite dev proxy target overridable via API_PROXY_TARGET (local testing)
Browser-verified: 200/page with load-more (200->400->600), exact hit
badge, keyword auto-expand, key detail panel, queries sidebar
Modeled on opsxdb's redis scan design:
- opaque base64 scan cursor {pattern,db,type,nodes,index,cursor}; server
validates search conditions match, rejects stale cursors (400)
- soft page cap 200 keys (whole scan batches returned, no key loss),
max 20 SCAN calls per page; next_cursor until all shards exhausted
- pattern rule: no glob chars -> *kw* contains match; non-glob input
also probed with TYPE for exact hit (returned as exact, slot-routed
so cluster/NAT safe)
- pipeline batch TYPE/TTL replaces per-key serial N+1; type filter now
effective server-side
- cluster: per-master direct SCAN merged across shards (MasterAddrs on
wrapper), DB0-only, MOVED errors hint at switching deploy mode;
SlowLog/ClientList aggregate across masters (were cluster-broken)
- GET /redis/:id/dbs (0-15, cluster -> [0])
- POST /redis/:id/keys/:key/field: hash row-level HSET/HDEL (no whole-
key rewrite)
- RedisExecuteCommand fixed (was always-403): accepts {db, command}
full-string, quote-aware tokenizer, whitelist on first word, expanded
read-only set
- RedisUpdateKey preserves previous TTL unless explicitly set/cleared
(was silently dropping TTL); string type skips the DEL
Verified live against local redis: 200/page no overlap across pages,
cursor 400 on condition change, exact hit, type filter, hash field
edit round-trip, CLI DBSIZE/HGET ok, SET rejected
Root cause (pre-existing): user state initialized with default role
'developer' while JWT role restoration happened via render-phase setState
~1 render later; first guard pass evaluated tickets/workflow against the
developer writable-datasource gate and bounced admins to dashboard on
direct load.
Fix: initialize user state lazily from the JWT in one pass
(restoreUserFromToken) - first render already carries the correct role.
Verified in browser: direct loads of /tickets and /workflow stay put;
URL query state survives refresh; expired session redirects to
/login?redirect=<path> and login returns to the original page.
- remove shadcn HSL dual-track vars (523->416->~300 lines index.css);
tailwind semantic colors (border/background/card/primary/destructive/
popover/muted/accent) now bridge directly to var(--dg-*) tokens
- drop dead darkMode:['class'] config and inert dark: variants in kept
ui components; theme switching is data-theme based (4 themes intact)
- first-frame inline theme script in index.html (reads namespaced key
with legacy fallback, resolves system, sets color-scheme) - no flash
- consolidate duplicated db-type brand colors into lib/constants.ts
single source (dbTypeColorMap + dbTypeBrandColorMap, with pg/mongodb
aliases); instance health stroke now uses theme tokens
- verified via browser: dark #0d1117 / light #f5f6f8 both resolve,
tokens cascade correctly
- delete 4 dead pages (~2300 lines): Home, ConfigPlatformPage,
DataDictionaryPage, NotificationsPage (kept the live NotificationPage)
- delete 52 unused shadcn ui components (keep button/card/dialog in use;
re-generate via shadcn CLI when needed)
- delete App.css, SkeletonCard, empty shared/ and features engine dirs
- remove dead theme blocks (midnight-purple / orange-vitality) from index.css
- uninstall ~40 dead deps (echarts, sonner, next-themes, zod,
react-hook-form, unused radix packages, cmdk, vaul, ...)
- fix DEVELOPMENT.md drift: mock layer section rewritten to real API layer,
permission docs now point to menu.ts single source
- new resp package (OK/Page/Fail/BadRequest/.../InternalErr): unified error
envelope {detail, code?}, sanitized 500s (full error logged server-side,
generic message to client - stops DB error leakage), pagination envelope
{items,total,page,page_size}; migrate 200+ inline c.JSON error sites
- refresh token: 7d rotation tokens (SHA-256 hashed at rest), public
POST /auth/refresh with single-use rotation, POST /auth/logout revokes
all user refresh tokens (endpoint previously missing - frontend 404)
- security: gh-ost command line password masked in task status payload;
Instance.MarshalJSON guarantees encrypted passwords never leave the API
(removes reliance on per-handler manual masking); production (DEBUG=false)
with default JWT secret now auto-generates and persists a random secret
- safe.Go recover wrapper for background goroutines (instance probe,
gh-ost async)
- delete dead service layer (zero callers)
- fix pre-existing go vet unkeyed bson.E literals